1use std::fmt;
4
5use alloy_primitives::{Address, B256, U256};
6use serde::{Deserialize, Serialize};
7
8use cow_types::OrderKind;
9
10pub const COMPOSABLE_COW_ADDRESS: Address = Address::new([
16 0xfd, 0xaf, 0xc9, 0xd1, 0x90, 0x2f, 0x4e, 0x0b, 0x84, 0xf6, 0x5f, 0x49, 0xf2, 0x44, 0xb3, 0x2b,
17 0x31, 0x01, 0x3b, 0x74,
18]);
19
20pub const TWAP_HANDLER_ADDRESS: Address = Address::new([
24 0x6c, 0xf1, 0xe9, 0xca, 0x41, 0xf7, 0x61, 0x1d, 0xef, 0x40, 0x81, 0x22, 0x79, 0x3c, 0x35, 0x8a,
25 0x3d, 0x11, 0xe5, 0xa5,
26]);
27
28pub const CURRENT_BLOCK_TIMESTAMP_FACTORY_ADDRESS: Address = Address::new([
37 0x52, 0xed, 0x56, 0xda, 0x04, 0x30, 0x9a, 0xca, 0x4c, 0x3f, 0xec, 0xc5, 0x95, 0x29, 0x8d, 0x80,
38 0xc2, 0xf1, 0x6b, 0xac,
39]);
40
41pub const MAX_FREQUENCY: u32 = 365 * 24 * 60 * 60; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
50pub struct ConditionalOrderParams {
51 pub handler: Address,
53 pub salt: B256,
55 pub static_input: Vec<u8>,
57}
58
59impl ConditionalOrderParams {
60 #[must_use]
72 pub const fn new(handler: Address, salt: B256, static_input: Vec<u8>) -> Self {
73 Self { handler, salt, static_input }
74 }
75
76 #[must_use]
86 pub const fn with_handler(mut self, handler: Address) -> Self {
87 self.handler = handler;
88 self
89 }
90
91 #[must_use]
101 pub const fn with_salt(mut self, salt: B256) -> Self {
102 self.salt = salt;
103 self
104 }
105
106 #[must_use]
116 pub fn with_static_input(mut self, static_input: Vec<u8>) -> Self {
117 self.static_input = static_input;
118 self
119 }
120
121 #[must_use]
127 pub const fn is_empty_static_input(&self) -> bool {
128 self.static_input.is_empty()
129 }
130
131 #[must_use]
137 pub const fn static_input_len(&self) -> usize {
138 self.static_input.len()
139 }
140
141 #[must_use]
151 pub const fn salt_ref(&self) -> &B256 {
152 &self.salt
153 }
154}
155
156impl fmt::Display for ConditionalOrderParams {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 write!(f, "params(handler={:#x})", self.handler)
159 }
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
166pub enum TwapStartTime {
167 AtMiningTime,
169 At(u32),
171}
172
173impl TwapStartTime {
174 #[must_use]
181 pub const fn as_str(self) -> &'static str {
182 match self {
183 Self::AtMiningTime => "at-mining-time",
184 Self::At(_) => "at-unix",
185 }
186 }
187
188 #[must_use]
194 pub const fn is_at_mining_time(self) -> bool {
195 matches!(self, Self::AtMiningTime)
196 }
197
198 #[must_use]
204 pub const fn is_fixed(self) -> bool {
205 matches!(self, Self::At(_))
206 }
207
208 #[must_use]
215 pub const fn timestamp(self) -> Option<u32> {
216 match self {
217 Self::At(ts) => Some(ts),
218 Self::AtMiningTime => None,
219 }
220 }
221}
222
223impl fmt::Display for TwapStartTime {
224 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225 match self {
226 Self::AtMiningTime => f.write_str("at-mining-time"),
227 Self::At(ts) => write!(f, "at-unix-{ts}"),
228 }
229 }
230}
231
232impl From<u32> for TwapStartTime {
233 fn from(ts: u32) -> Self {
238 if ts == 0 { Self::AtMiningTime } else { Self::At(ts) }
239 }
240}
241
242impl From<TwapStartTime> for u32 {
243 fn from(t: TwapStartTime) -> Self {
248 match t {
249 TwapStartTime::AtMiningTime => 0,
250 TwapStartTime::At(ts) => ts,
251 }
252 }
253}
254
255impl From<Option<u32>> for TwapStartTime {
256 fn from(ts: Option<u32>) -> Self {
261 match ts {
262 Some(t) => Self::At(t),
263 None => Self::AtMiningTime,
264 }
265 }
266}
267
268#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
275pub enum DurationOfPart {
276 #[default]
278 Auto,
279 LimitDuration {
281 duration: u32,
283 },
284}
285
286impl DurationOfPart {
287 #[must_use]
289 pub const fn duration(self) -> Option<u32> {
290 match self {
291 Self::LimitDuration { duration } => Some(duration),
292 Self::Auto => None,
293 }
294 }
295
296 #[must_use]
298 pub const fn is_auto(self) -> bool {
299 matches!(self, Self::Auto)
300 }
301
302 #[must_use]
312 pub const fn limit(duration: u32) -> Self {
313 Self::LimitDuration { duration }
314 }
315
316 #[must_use]
325 pub const fn is_limit_duration(self) -> bool {
326 matches!(self, Self::LimitDuration { .. })
327 }
328}
329
330impl fmt::Display for DurationOfPart {
331 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
332 match self {
333 Self::Auto => f.write_str("auto"),
334 Self::LimitDuration { duration } => write!(f, "limit-duration({duration}s)"),
335 }
336 }
337}
338
339impl From<Option<u32>> for DurationOfPart {
340 fn from(d: Option<u32>) -> Self {
345 match d {
346 Some(duration) => Self::LimitDuration { duration },
347 None => Self::Auto,
348 }
349 }
350}
351
352#[derive(Debug, Clone, Serialize, Deserialize)]
357#[serde(rename_all = "camelCase")]
358pub struct TwapData {
359 pub sell_token: Address,
361 pub buy_token: Address,
363 pub receiver: Address,
365 pub sell_amount: U256,
367 pub buy_amount: U256,
369 pub start_time: TwapStartTime,
371 pub part_duration: u32,
373 pub num_parts: u32,
375 pub app_data: B256,
377 pub partially_fillable: bool,
379 pub kind: OrderKind,
381 #[serde(default)]
385 pub duration_of_part: DurationOfPart,
386}
387
388impl fmt::Display for TwapData {
389 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390 write!(
391 f,
392 "TWAP {} × {}s [{}] sell {} {:#x} → buy ≥ {} {:#x}",
393 self.num_parts,
394 self.part_duration,
395 self.start_time,
396 self.sell_amount,
397 self.sell_token,
398 self.buy_amount,
399 self.buy_token,
400 )
401 }
402}
403
404impl TwapData {
405 #[must_use]
413 pub const fn total_duration_secs(&self) -> u64 {
414 self.num_parts as u64 * self.part_duration as u64
415 }
416
417 #[must_use]
426 pub const fn end_time(&self) -> Option<u64> {
427 match self.start_time {
428 TwapStartTime::At(ts) => Some(ts as u64 + self.total_duration_secs()),
429 TwapStartTime::AtMiningTime => None,
430 }
431 }
432
433 #[must_use]
444 pub const fn is_sell(&self) -> bool {
445 self.kind.is_sell()
446 }
447
448 #[must_use]
461 pub const fn is_buy(&self) -> bool {
462 self.kind.is_buy()
463 }
464
465 #[must_use]
481 pub const fn is_expired(&self, timestamp: u64) -> bool {
482 match self.end_time() {
483 Some(end) => timestamp >= end,
484 None => false,
485 }
486 }
487
488 #[must_use]
508 pub const fn sell(
509 sell_token: Address,
510 buy_token: Address,
511 sell_amount: U256,
512 num_parts: u32,
513 part_duration: u32,
514 ) -> Self {
515 Self {
516 sell_token,
517 buy_token,
518 receiver: Address::ZERO,
519 sell_amount,
520 buy_amount: U256::ZERO,
521 start_time: TwapStartTime::AtMiningTime,
522 part_duration,
523 num_parts,
524 app_data: B256::ZERO,
525 partially_fillable: false,
526 kind: OrderKind::Sell,
527 duration_of_part: DurationOfPart::Auto,
528 }
529 }
530
531 #[must_use]
551 pub const fn buy(
552 sell_token: Address,
553 buy_token: Address,
554 buy_amount: U256,
555 num_parts: u32,
556 part_duration: u32,
557 ) -> Self {
558 Self {
559 sell_token,
560 buy_token,
561 receiver: Address::ZERO,
562 sell_amount: U256::MAX,
563 buy_amount,
564 start_time: TwapStartTime::AtMiningTime,
565 part_duration,
566 num_parts,
567 app_data: B256::ZERO,
568 partially_fillable: false,
569 kind: OrderKind::Buy,
570 duration_of_part: DurationOfPart::Auto,
571 }
572 }
573
574 #[must_use]
582 pub const fn with_receiver(mut self, receiver: Address) -> Self {
583 self.receiver = receiver;
584 self
585 }
586
587 #[must_use]
595 pub const fn with_buy_amount(mut self, buy_amount: U256) -> Self {
596 self.buy_amount = buy_amount;
597 self
598 }
599
600 #[must_use]
608 pub const fn with_sell_amount(mut self, sell_amount: U256) -> Self {
609 self.sell_amount = sell_amount;
610 self
611 }
612
613 #[must_use]
619 pub const fn with_start_time(mut self, start_time: TwapStartTime) -> Self {
620 self.start_time = start_time;
621 self
622 }
623
624 #[must_use]
630 pub const fn with_app_data(mut self, app_data: B256) -> Self {
631 self.app_data = app_data;
632 self
633 }
634
635 #[must_use]
641 pub const fn with_partially_fillable(mut self, partially_fillable: bool) -> Self {
642 self.partially_fillable = partially_fillable;
643 self
644 }
645
646 #[must_use]
652 pub const fn with_duration_of_part(mut self, duration_of_part: DurationOfPart) -> Self {
653 self.duration_of_part = duration_of_part;
654 self
655 }
656
657 #[must_use]
672 pub fn has_app_data(&self) -> bool {
673 !self.app_data.is_zero()
674 }
675}
676
677#[derive(Debug, Clone)]
683pub struct TwapStruct {
684 pub sell_token: Address,
686 pub buy_token: Address,
688 pub receiver: Address,
690 pub part_sell_amount: U256,
692 pub min_part_limit: U256,
694 pub t0: u32,
696 pub n: u32,
698 pub t: u32,
700 pub span: u32,
702 pub app_data: B256,
704}
705
706impl TwapStruct {
707 #[must_use]
715 pub fn has_app_data(&self) -> bool {
716 !self.app_data.is_zero()
717 }
718
719 #[must_use]
728 pub fn has_custom_receiver(&self) -> bool {
729 !self.receiver.is_zero()
730 }
731
732 #[must_use]
741 pub const fn start_is_fixed(&self) -> bool {
742 self.t0 != 0
743 }
744}
745
746impl TryFrom<&TwapData> for TwapStruct {
747 type Error = cow_errors::CowError;
748
749 fn try_from(d: &TwapData) -> Result<Self, Self::Error> {
753 crate::data_to_struct(d)
754 }
755}
756
757impl From<&TwapStruct> for TwapData {
758 fn from(s: &TwapStruct) -> Self {
762 crate::struct_to_data(s)
763 }
764}
765
766impl fmt::Display for TwapStruct {
767 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
768 write!(
769 f,
770 "twap-struct {} × {}s sell {} {:#x} → ≥{} {:#x}",
771 self.n,
772 self.t,
773 self.part_sell_amount,
774 self.sell_token,
775 self.min_part_limit,
776 self.buy_token,
777 )
778 }
779}
780
781#[derive(Debug, Clone)]
795pub struct GpV2OrderStruct {
796 pub sell_token: Address,
798 pub buy_token: Address,
800 pub receiver: Address,
802 pub sell_amount: U256,
804 pub buy_amount: U256,
806 pub valid_to: u32,
808 pub app_data: B256,
810 pub fee_amount: U256,
812 pub kind: B256,
814 pub partially_fillable: bool,
816 pub sell_token_balance: B256,
818 pub buy_token_balance: B256,
820}
821impl fmt::Display for GpV2OrderStruct {
822 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
823 write!(
824 f,
825 "gpv2-order({:#x} sell={} → {:#x} buy={})",
826 self.sell_token, self.sell_amount, self.buy_token, self.buy_amount
827 )
828 }
829}
830
831impl GpV2OrderStruct {
832 #[must_use]
841 pub fn has_custom_receiver(&self) -> bool {
842 !self.receiver.is_zero()
843 }
844
845 #[must_use]
851 pub const fn is_partially_fillable(&self) -> bool {
852 self.partially_fillable
853 }
854}
855
856impl TryFrom<&GpV2OrderStruct> for cow_signing::types::UnsignedOrder {
857 type Error = cow_errors::CowError;
858
859 fn try_from(s: &GpV2OrderStruct) -> Result<Self, Self::Error> {
865 crate::from_struct_to_order(s)
866 }
867}
868
869#[derive(Debug, Clone)]
876pub enum PollResult {
877 Success {
884 order: Option<cow_signing::types::UnsignedOrder>,
886 signature: Option<String>,
888 },
889 TryNextBlock,
891 TryOnBlock {
893 block_number: u64,
895 },
896 TryAtEpoch {
898 epoch: u64,
900 },
901 UnexpectedError {
903 message: String,
905 },
906 DontTryAgain {
908 reason: String,
910 },
911}
912
913impl PollResult {
914 #[must_use]
920 pub const fn is_success(&self) -> bool {
921 matches!(self, Self::Success { .. })
922 }
923
924 #[must_use]
931 pub const fn is_retryable(&self) -> bool {
932 matches!(self, Self::TryNextBlock | Self::TryOnBlock { .. } | Self::TryAtEpoch { .. })
933 }
934
935 #[must_use]
941 pub const fn is_terminal(&self) -> bool {
942 matches!(self, Self::DontTryAgain { .. })
943 }
944
945 #[must_use]
951 pub const fn is_try_next_block(&self) -> bool {
952 matches!(self, Self::TryNextBlock)
953 }
954
955 #[must_use]
961 pub const fn is_try_on_block(&self) -> bool {
962 matches!(self, Self::TryOnBlock { .. })
963 }
964
965 #[must_use]
971 pub const fn is_try_at_epoch(&self) -> bool {
972 matches!(self, Self::TryAtEpoch { .. })
973 }
974
975 #[must_use]
981 pub const fn is_unexpected_error(&self) -> bool {
982 matches!(self, Self::UnexpectedError { .. })
983 }
984
985 #[must_use]
991 pub const fn is_dont_try_again(&self) -> bool {
992 matches!(self, Self::DontTryAgain { .. })
993 }
994
995 #[must_use]
1007 pub const fn get_block_number(&self) -> Option<u64> {
1008 if let Self::TryOnBlock { block_number } = self { Some(*block_number) } else { None }
1009 }
1010
1011 #[must_use]
1023 pub const fn get_epoch(&self) -> Option<u64> {
1024 if let Self::TryAtEpoch { epoch } = self { Some(*epoch) } else { None }
1025 }
1026
1027 #[must_use]
1033 pub const fn order_ref(&self) -> Option<&cow_signing::types::UnsignedOrder> {
1034 if let Self::Success { order, .. } = self { order.as_ref() } else { None }
1035 }
1036
1037 #[must_use]
1042 pub const fn as_error_message(&self) -> Option<&str> {
1043 match self {
1044 Self::UnexpectedError { message } => Some(message.as_str()),
1045 Self::DontTryAgain { reason } => Some(reason.as_str()),
1046 Self::Success { .. } |
1047 Self::TryNextBlock |
1048 Self::TryOnBlock { .. } |
1049 Self::TryAtEpoch { .. } => None,
1050 }
1051 }
1052}
1053
1054impl fmt::Display for PollResult {
1055 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1056 match self {
1057 Self::Success { .. } => f.write_str("success"),
1058 Self::TryNextBlock => f.write_str("try-next-block"),
1059 Self::TryOnBlock { block_number } => write!(f, "try-on-block({block_number})"),
1060 Self::TryAtEpoch { epoch } => write!(f, "try-at-epoch({epoch})"),
1061 Self::UnexpectedError { message } => write!(f, "unexpected-error({message})"),
1062 Self::DontTryAgain { reason } => write!(f, "dont-try-again({reason})"),
1063 }
1064 }
1065}
1066
1067#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
1071#[repr(u8)]
1072pub enum ProofLocation {
1073 #[default]
1075 Private = 0,
1076 Emitted = 1,
1078 Swarm = 2,
1080 Waku = 3,
1082 Reserved = 4,
1084 Ipfs = 5,
1086}
1087
1088impl ProofLocation {
1089 #[must_use]
1091 pub const fn as_str(self) -> &'static str {
1092 match self {
1093 Self::Private => "private",
1094 Self::Emitted => "emitted",
1095 Self::Swarm => "swarm",
1096 Self::Waku => "waku",
1097 Self::Reserved => "reserved",
1098 Self::Ipfs => "ipfs",
1099 }
1100 }
1101
1102 #[must_use]
1104 pub const fn is_private(self) -> bool {
1105 matches!(self, Self::Private)
1106 }
1107
1108 #[must_use]
1110 pub const fn is_emitted(self) -> bool {
1111 matches!(self, Self::Emitted)
1112 }
1113
1114 #[must_use]
1116 pub const fn is_swarm(self) -> bool {
1117 matches!(self, Self::Swarm)
1118 }
1119
1120 #[must_use]
1122 pub const fn is_waku(self) -> bool {
1123 matches!(self, Self::Waku)
1124 }
1125
1126 #[must_use]
1128 pub const fn is_reserved(self) -> bool {
1129 matches!(self, Self::Reserved)
1130 }
1131
1132 #[must_use]
1134 pub const fn is_ipfs(self) -> bool {
1135 matches!(self, Self::Ipfs)
1136 }
1137}
1138
1139impl fmt::Display for ProofLocation {
1140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1141 f.write_str(self.as_str())
1142 }
1143}
1144
1145impl TryFrom<u8> for ProofLocation {
1146 type Error = cow_errors::CowError;
1147
1148 fn try_from(n: u8) -> Result<Self, Self::Error> {
1150 match n {
1151 0 => Ok(Self::Private),
1152 1 => Ok(Self::Emitted),
1153 2 => Ok(Self::Swarm),
1154 3 => Ok(Self::Waku),
1155 4 => Ok(Self::Reserved),
1156 5 => Ok(Self::Ipfs),
1157 other => Err(cow_errors::CowError::Parse {
1158 field: "ProofLocation",
1159 reason: format!("unknown discriminant: {other}"),
1160 }),
1161 }
1162 }
1163}
1164
1165impl TryFrom<&str> for ProofLocation {
1166 type Error = cow_errors::CowError;
1167
1168 fn try_from(s: &str) -> Result<Self, Self::Error> {
1170 match s {
1171 "private" => Ok(Self::Private),
1172 "emitted" => Ok(Self::Emitted),
1173 "swarm" => Ok(Self::Swarm),
1174 "waku" => Ok(Self::Waku),
1175 "reserved" => Ok(Self::Reserved),
1176 "ipfs" => Ok(Self::Ipfs),
1177 other => Err(cow_errors::CowError::Parse {
1178 field: "ProofLocation",
1179 reason: format!("unknown value: {other}"),
1180 }),
1181 }
1182 }
1183}
1184
1185impl From<ProofLocation> for u8 {
1186 fn from(loc: ProofLocation) -> Self {
1190 loc as Self
1191 }
1192}
1193
1194impl ProofStruct {
1195 #[must_use]
1206 pub const fn new(location: ProofLocation, data: Vec<u8>) -> Self {
1207 Self { location, data }
1208 }
1209
1210 #[must_use]
1216 pub const fn private() -> Self {
1217 Self { location: ProofLocation::Private, data: Vec::new() }
1218 }
1219
1220 #[must_use]
1226 pub const fn emitted() -> Self {
1227 Self { location: ProofLocation::Emitted, data: Vec::new() }
1228 }
1229
1230 #[must_use]
1236 pub const fn with_location(mut self, location: ProofLocation) -> Self {
1237 self.location = location;
1238 self
1239 }
1240
1241 #[must_use]
1247 pub fn with_data(mut self, data: Vec<u8>) -> Self {
1248 self.data = data;
1249 self
1250 }
1251
1252 #[must_use]
1258 pub const fn is_private(&self) -> bool {
1259 self.location.is_private()
1260 }
1261
1262 #[must_use]
1268 pub const fn is_emitted(&self) -> bool {
1269 self.location.is_emitted()
1270 }
1271
1272 #[must_use]
1278 pub const fn is_swarm(&self) -> bool {
1279 self.location.is_swarm()
1280 }
1281
1282 #[must_use]
1288 pub const fn is_waku(&self) -> bool {
1289 self.location.is_waku()
1290 }
1291
1292 #[must_use]
1298 pub const fn is_ipfs(&self) -> bool {
1299 self.location.is_ipfs()
1300 }
1301
1302 #[must_use]
1308 pub const fn is_reserved(&self) -> bool {
1309 self.location.is_reserved()
1310 }
1311
1312 #[must_use]
1317 pub const fn has_data(&self) -> bool {
1318 !self.data.is_empty()
1319 }
1320
1321 #[must_use]
1323 pub const fn is_empty(&self) -> bool {
1324 self.data.is_empty()
1325 }
1326
1327 #[must_use]
1329 pub const fn data_len(&self) -> usize {
1330 self.data.len()
1331 }
1332}
1333
1334impl fmt::Display for ProofStruct {
1335 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1336 write!(f, "proof({})", self.location)
1337 }
1338}
1339
1340#[cfg(test)]
1343mod tests {
1344 use super::*;
1345
1346 #[test]
1349 fn composable_cow_address_matches() {
1350 assert_eq!(
1351 format!("{COMPOSABLE_COW_ADDRESS:#x}"),
1352 "0xfdafc9d1902f4e0b84f65f49f244b32b31013b74"
1353 );
1354 }
1355
1356 #[test]
1357 fn twap_handler_address_matches() {
1358 assert_eq!(
1359 format!("{TWAP_HANDLER_ADDRESS:#x}"),
1360 "0x6cf1e9ca41f7611def408122793c358a3d11e5a5"
1361 );
1362 }
1363
1364 #[test]
1365 fn current_block_timestamp_factory_address_matches() {
1366 assert_eq!(
1367 format!("{CURRENT_BLOCK_TIMESTAMP_FACTORY_ADDRESS:#x}"),
1368 "0x52ed56da04309aca4c3fecc595298d80c2f16bac"
1369 );
1370 }
1371
1372 #[test]
1373 fn max_frequency_is_one_year() {
1374 assert_eq!(MAX_FREQUENCY, 365 * 24 * 60 * 60);
1375 assert_eq!(MAX_FREQUENCY, 31_536_000);
1376 }
1377
1378 #[test]
1381 fn conditional_order_params_new() {
1382 let p = ConditionalOrderParams::new(Address::ZERO, B256::ZERO, vec![1, 2, 3]);
1383 assert_eq!(p.handler, Address::ZERO);
1384 assert_eq!(p.salt, B256::ZERO);
1385 assert_eq!(p.static_input, vec![1, 2, 3]);
1386 }
1387
1388 #[test]
1389 fn conditional_order_params_with_handler() {
1390 let p = ConditionalOrderParams::new(Address::ZERO, B256::ZERO, vec![])
1391 .with_handler(TWAP_HANDLER_ADDRESS);
1392 assert_eq!(p.handler, TWAP_HANDLER_ADDRESS);
1393 }
1394
1395 #[test]
1396 fn conditional_order_params_with_salt() {
1397 let salt = B256::repeat_byte(0xAB);
1398 let p = ConditionalOrderParams::new(Address::ZERO, B256::ZERO, vec![]).with_salt(salt);
1399 assert_eq!(p.salt, salt);
1400 }
1401
1402 #[test]
1403 fn conditional_order_params_with_static_input() {
1404 let p = ConditionalOrderParams::new(Address::ZERO, B256::ZERO, vec![])
1405 .with_static_input(vec![0xDE, 0xAD]);
1406 assert_eq!(p.static_input, vec![0xDE, 0xAD]);
1407 }
1408
1409 #[test]
1410 fn conditional_order_params_empty_static_input() {
1411 let empty = ConditionalOrderParams::new(Address::ZERO, B256::ZERO, vec![]);
1412 assert!(empty.is_empty_static_input());
1413 assert_eq!(empty.static_input_len(), 0);
1414
1415 let non_empty = empty.with_static_input(vec![1]);
1416 assert!(!non_empty.is_empty_static_input());
1417 assert_eq!(non_empty.static_input_len(), 1);
1418 }
1419
1420 #[test]
1421 fn conditional_order_params_salt_ref() {
1422 let salt = B256::repeat_byte(0x42);
1423 let p = ConditionalOrderParams::new(Address::ZERO, salt, vec![]);
1424 assert_eq!(p.salt_ref(), &salt);
1425 }
1426
1427 #[test]
1428 fn conditional_order_params_display() {
1429 let p = ConditionalOrderParams::new(TWAP_HANDLER_ADDRESS, B256::ZERO, vec![]);
1430 let s = p.to_string();
1431 assert!(s.starts_with("params(handler=0x"));
1432 }
1433
1434 #[test]
1437 fn twap_start_time_as_str() {
1438 assert_eq!(TwapStartTime::AtMiningTime.as_str(), "at-mining-time");
1439 assert_eq!(TwapStartTime::At(100).as_str(), "at-unix");
1440 }
1441
1442 #[test]
1443 fn twap_start_time_is_at_mining_time() {
1444 assert!(TwapStartTime::AtMiningTime.is_at_mining_time());
1445 assert!(!TwapStartTime::At(42).is_at_mining_time());
1446 }
1447
1448 #[test]
1449 fn twap_start_time_is_fixed() {
1450 assert!(TwapStartTime::At(42).is_fixed());
1451 assert!(!TwapStartTime::AtMiningTime.is_fixed());
1452 }
1453
1454 #[test]
1455 fn twap_start_time_timestamp() {
1456 assert_eq!(TwapStartTime::AtMiningTime.timestamp(), None);
1457 assert_eq!(TwapStartTime::At(1_000).timestamp(), Some(1_000));
1458 }
1459
1460 #[test]
1461 fn twap_start_time_display() {
1462 assert_eq!(TwapStartTime::AtMiningTime.to_string(), "at-mining-time");
1463 assert_eq!(TwapStartTime::At(1_700_000_000).to_string(), "at-unix-1700000000");
1464 }
1465
1466 #[test]
1467 fn twap_start_time_from_u32() {
1468 assert_eq!(TwapStartTime::from(0u32), TwapStartTime::AtMiningTime);
1469 assert_eq!(TwapStartTime::from(42u32), TwapStartTime::At(42));
1470 }
1471
1472 #[test]
1473 fn twap_start_time_into_u32() {
1474 let zero: u32 = TwapStartTime::AtMiningTime.into();
1475 assert_eq!(zero, 0);
1476 let ts: u32 = TwapStartTime::At(999).into();
1477 assert_eq!(ts, 999);
1478 }
1479
1480 #[test]
1481 fn twap_start_time_from_option_u32() {
1482 assert_eq!(TwapStartTime::from(None), TwapStartTime::AtMiningTime);
1483 assert_eq!(TwapStartTime::from(Some(123)), TwapStartTime::At(123));
1484 }
1485
1486 #[test]
1489 fn duration_of_part_auto_defaults() {
1490 let d = DurationOfPart::default();
1491 assert!(d.is_auto());
1492 assert!(!d.is_limit_duration());
1493 assert_eq!(d.duration(), None);
1494 }
1495
1496 #[test]
1497 fn duration_of_part_limit() {
1498 let d = DurationOfPart::limit(1_800);
1499 assert!(!d.is_auto());
1500 assert!(d.is_limit_duration());
1501 assert_eq!(d.duration(), Some(1_800));
1502 }
1503
1504 #[test]
1505 fn duration_of_part_display() {
1506 assert_eq!(DurationOfPart::Auto.to_string(), "auto");
1507 assert_eq!(DurationOfPart::limit(600).to_string(), "limit-duration(600s)");
1508 }
1509
1510 #[test]
1511 fn duration_of_part_from_option() {
1512 assert_eq!(DurationOfPart::from(None), DurationOfPart::Auto);
1513 assert_eq!(
1514 DurationOfPart::from(Some(300)),
1515 DurationOfPart::LimitDuration { duration: 300 }
1516 );
1517 }
1518
1519 #[test]
1522 fn twap_data_sell_constructor() {
1523 let t = TwapData::sell(Address::ZERO, Address::ZERO, U256::from(1000u64), 4, 3600);
1524 assert!(t.is_sell());
1525 assert!(!t.is_buy());
1526 assert_eq!(t.sell_amount, U256::from(1000u64));
1527 assert_eq!(t.buy_amount, U256::ZERO);
1528 assert_eq!(t.receiver, Address::ZERO);
1529 assert_eq!(t.num_parts, 4);
1530 assert_eq!(t.part_duration, 3600);
1531 assert!(t.start_time.is_at_mining_time());
1532 assert!(!t.partially_fillable);
1533 assert!(t.duration_of_part.is_auto());
1534 assert!(!t.has_app_data());
1535 }
1536
1537 #[test]
1538 fn twap_data_buy_constructor() {
1539 let t = TwapData::buy(Address::ZERO, Address::ZERO, U256::from(500u64), 2, 1800);
1540 assert!(t.is_buy());
1541 assert!(!t.is_sell());
1542 assert_eq!(t.sell_amount, U256::MAX);
1543 assert_eq!(t.buy_amount, U256::from(500u64));
1544 assert_eq!(t.num_parts, 2);
1545 assert_eq!(t.part_duration, 1800);
1546 }
1547
1548 #[test]
1551 fn twap_data_total_duration_secs() {
1552 let t = TwapData::sell(Address::ZERO, Address::ZERO, U256::ZERO, 4, 3600);
1553 assert_eq!(t.total_duration_secs(), 14_400);
1554 }
1555
1556 #[test]
1557 fn twap_data_end_time_fixed() {
1558 let t = TwapData::sell(Address::ZERO, Address::ZERO, U256::ZERO, 4, 3600)
1559 .with_start_time(TwapStartTime::At(1_000_000));
1560 assert_eq!(t.end_time(), Some(1_000_000 + 14_400));
1561 }
1562
1563 #[test]
1564 fn twap_data_end_time_at_mining() {
1565 let t = TwapData::sell(Address::ZERO, Address::ZERO, U256::ZERO, 4, 3600);
1566 assert_eq!(t.end_time(), None);
1567 }
1568
1569 #[test]
1570 fn twap_data_is_expired() {
1571 let t = TwapData::sell(Address::ZERO, Address::ZERO, U256::ZERO, 4, 3600)
1572 .with_start_time(TwapStartTime::At(1_000_000));
1573 assert!(!t.is_expired(1_014_399));
1575 assert!(t.is_expired(1_014_400));
1576 assert!(t.is_expired(2_000_000));
1577 }
1578
1579 #[test]
1580 fn twap_data_is_expired_at_mining_time_always_false() {
1581 let t = TwapData::sell(Address::ZERO, Address::ZERO, U256::ZERO, 4, 3600);
1582 assert!(!t.is_expired(u64::MAX));
1583 }
1584
1585 #[test]
1588 fn twap_data_with_receiver() {
1589 let recv = Address::repeat_byte(0x01);
1590 let t = TwapData::sell(Address::ZERO, Address::ZERO, U256::ZERO, 1, 1).with_receiver(recv);
1591 assert_eq!(t.receiver, recv);
1592 }
1593
1594 #[test]
1595 fn twap_data_with_buy_amount() {
1596 let t = TwapData::sell(Address::ZERO, Address::ZERO, U256::ZERO, 1, 1)
1597 .with_buy_amount(U256::from(42u64));
1598 assert_eq!(t.buy_amount, U256::from(42u64));
1599 }
1600
1601 #[test]
1602 fn twap_data_with_sell_amount() {
1603 let t = TwapData::buy(Address::ZERO, Address::ZERO, U256::ZERO, 1, 1)
1604 .with_sell_amount(U256::from(99u64));
1605 assert_eq!(t.sell_amount, U256::from(99u64));
1606 }
1607
1608 #[test]
1609 fn twap_data_with_start_time() {
1610 let t = TwapData::sell(Address::ZERO, Address::ZERO, U256::ZERO, 1, 1)
1611 .with_start_time(TwapStartTime::At(12345));
1612 assert_eq!(t.start_time, TwapStartTime::At(12345));
1613 }
1614
1615 #[test]
1616 fn twap_data_with_app_data() {
1617 let hash = B256::repeat_byte(0xAA);
1618 let t = TwapData::sell(Address::ZERO, Address::ZERO, U256::ZERO, 1, 1).with_app_data(hash);
1619 assert!(t.has_app_data());
1620 assert_eq!(t.app_data, hash);
1621 }
1622
1623 #[test]
1624 fn twap_data_has_app_data_zero_is_false() {
1625 let t = TwapData::sell(Address::ZERO, Address::ZERO, U256::ZERO, 1, 1);
1626 assert!(!t.has_app_data());
1627 }
1628
1629 #[test]
1630 fn twap_data_with_partially_fillable() {
1631 let t = TwapData::sell(Address::ZERO, Address::ZERO, U256::ZERO, 1, 1)
1632 .with_partially_fillable(true);
1633 assert!(t.partially_fillable);
1634 }
1635
1636 #[test]
1637 fn twap_data_with_duration_of_part() {
1638 let t = TwapData::sell(Address::ZERO, Address::ZERO, U256::ZERO, 1, 1)
1639 .with_duration_of_part(DurationOfPart::limit(900));
1640 assert!(t.duration_of_part.is_limit_duration());
1641 assert_eq!(t.duration_of_part.duration(), Some(900));
1642 }
1643
1644 #[test]
1647 fn twap_data_display_at_mining_time() {
1648 let t = TwapData::sell(Address::ZERO, Address::ZERO, U256::from(24_000u64), 24, 3_600)
1649 .with_buy_amount(U256::from(1_000u64));
1650 let s = t.to_string();
1651 assert!(s.contains("24 × 3600s"), "got: {s}");
1652 assert!(s.contains("at-mining-time"), "got: {s}");
1653 assert!(s.contains("24000"), "got: {s}");
1654 assert!(s.contains("1000"), "got: {s}");
1655 }
1656
1657 #[test]
1658 fn twap_data_display_fixed_start() {
1659 let t = TwapData::sell(Address::ZERO, Address::ZERO, U256::from(1_000u64), 6, 7_200)
1660 .with_start_time(TwapStartTime::At(1_700_000_000));
1661 let s = t.to_string();
1662 assert!(s.contains("at-unix-1700000000"), "got: {s}");
1663 }
1664
1665 fn make_twap_struct() -> TwapStruct {
1668 TwapStruct {
1669 sell_token: Address::ZERO,
1670 buy_token: Address::ZERO,
1671 receiver: Address::ZERO,
1672 part_sell_amount: U256::from(250u64),
1673 min_part_limit: U256::from(100u64),
1674 t0: 0,
1675 n: 4,
1676 t: 3600,
1677 span: 0,
1678 app_data: B256::ZERO,
1679 }
1680 }
1681
1682 #[test]
1683 fn twap_struct_has_app_data() {
1684 let mut s = make_twap_struct();
1685 assert!(!s.has_app_data());
1686 s.app_data = B256::repeat_byte(0x01);
1687 assert!(s.has_app_data());
1688 }
1689
1690 #[test]
1691 fn twap_struct_has_custom_receiver() {
1692 let mut s = make_twap_struct();
1693 assert!(!s.has_custom_receiver());
1694 s.receiver = Address::repeat_byte(0x01);
1695 assert!(s.has_custom_receiver());
1696 }
1697
1698 #[test]
1699 fn twap_struct_start_is_fixed() {
1700 let mut s = make_twap_struct();
1701 assert!(!s.start_is_fixed());
1702 s.t0 = 1_000_000;
1703 assert!(s.start_is_fixed());
1704 }
1705
1706 #[test]
1707 fn twap_struct_display() {
1708 let s = make_twap_struct();
1709 let d = s.to_string();
1710 assert!(d.contains("twap-struct"), "got: {d}");
1711 assert!(d.contains("4 × 3600s"), "got: {d}");
1712 }
1713
1714 #[test]
1717 fn twap_struct_try_from_twap_data() {
1718 let data = TwapData::sell(Address::ZERO, Address::ZERO, U256::from(1000u64), 4, 3600)
1719 .with_buy_amount(U256::from(400u64))
1720 .with_start_time(TwapStartTime::At(5000))
1721 .with_duration_of_part(DurationOfPart::limit(1800));
1722 let s = TwapStruct::try_from(&data).unwrap();
1723 assert_eq!(s.part_sell_amount, U256::from(250u64));
1724 assert_eq!(s.min_part_limit, U256::from(100u64));
1725 assert_eq!(s.t0, 5000);
1726 assert_eq!(s.n, 4);
1727 assert_eq!(s.t, 3600);
1728 assert_eq!(s.span, 1800);
1729 }
1730
1731 #[test]
1732 fn twap_struct_try_from_twap_data_zero_parts_errors() {
1733 let mut data = TwapData::sell(Address::ZERO, Address::ZERO, U256::from(1000u64), 4, 3600);
1734 data.num_parts = 0;
1735 assert!(TwapStruct::try_from(&data).is_err());
1736 }
1737
1738 #[test]
1739 fn twap_data_from_twap_struct() {
1740 let s = TwapStruct {
1741 sell_token: Address::ZERO,
1742 buy_token: Address::ZERO,
1743 receiver: Address::ZERO,
1744 part_sell_amount: U256::from(250u64),
1745 min_part_limit: U256::from(100u64),
1746 t0: 5000,
1747 n: 4,
1748 t: 3600,
1749 span: 1800,
1750 app_data: B256::ZERO,
1751 };
1752 let data = TwapData::from(&s);
1753 assert_eq!(data.sell_amount, U256::from(1000u64));
1754 assert_eq!(data.buy_amount, U256::from(400u64));
1755 assert_eq!(data.start_time, TwapStartTime::At(5000));
1756 assert_eq!(data.num_parts, 4);
1757 assert_eq!(data.part_duration, 3600);
1758 assert!(data.duration_of_part.is_limit_duration());
1759 assert_eq!(data.duration_of_part.duration(), Some(1800));
1760 }
1761
1762 #[test]
1763 fn twap_data_from_twap_struct_at_mining_time() {
1764 let mut s = make_twap_struct();
1765 s.t0 = 0;
1766 s.span = 0;
1767 let data = TwapData::from(&s);
1768 assert!(data.start_time.is_at_mining_time());
1769 assert!(data.duration_of_part.is_auto());
1770 }
1771
1772 fn make_gpv2_order() -> GpV2OrderStruct {
1775 GpV2OrderStruct {
1776 sell_token: Address::ZERO,
1777 buy_token: Address::ZERO,
1778 receiver: Address::ZERO,
1779 sell_amount: U256::from(1000u64),
1780 buy_amount: U256::from(500u64),
1781 valid_to: 1_700_000_000,
1782 app_data: B256::ZERO,
1783 fee_amount: U256::ZERO,
1784 kind: B256::ZERO,
1785 partially_fillable: false,
1786 sell_token_balance: B256::ZERO,
1787 buy_token_balance: B256::ZERO,
1788 }
1789 }
1790
1791 #[test]
1792 fn gpv2_order_has_custom_receiver() {
1793 let mut o = make_gpv2_order();
1794 assert!(!o.has_custom_receiver());
1795 o.receiver = Address::repeat_byte(0x01);
1796 assert!(o.has_custom_receiver());
1797 }
1798
1799 #[test]
1800 fn gpv2_order_is_partially_fillable() {
1801 let mut o = make_gpv2_order();
1802 assert!(!o.is_partially_fillable());
1803 o.partially_fillable = true;
1804 assert!(o.is_partially_fillable());
1805 }
1806
1807 #[test]
1808 fn gpv2_order_display() {
1809 let o = make_gpv2_order();
1810 let s = o.to_string();
1811 assert!(s.contains("gpv2-order"), "got: {s}");
1812 assert!(s.contains("1000"), "got: {s}");
1813 assert!(s.contains("500"), "got: {s}");
1814 }
1815
1816 #[test]
1819 fn poll_result_success() {
1820 let r = PollResult::Success { order: None, signature: None };
1821 assert!(r.is_success());
1822 assert!(!r.is_retryable());
1823 assert!(!r.is_terminal());
1824 assert!(r.order_ref().is_none());
1825 assert!(r.as_error_message().is_none());
1826 }
1827
1828 #[test]
1829 fn poll_result_try_next_block() {
1830 let r = PollResult::TryNextBlock;
1831 assert!(r.is_try_next_block());
1832 assert!(r.is_retryable());
1833 assert!(!r.is_success());
1834 assert_eq!(r.get_block_number(), None);
1835 assert_eq!(r.get_epoch(), None);
1836 }
1837
1838 #[test]
1839 fn poll_result_try_on_block() {
1840 let r = PollResult::TryOnBlock { block_number: 42 };
1841 assert!(r.is_try_on_block());
1842 assert!(r.is_retryable());
1843 assert_eq!(r.get_block_number(), Some(42));
1844 }
1845
1846 #[test]
1847 fn poll_result_try_at_epoch() {
1848 let r = PollResult::TryAtEpoch { epoch: 1_700_000_000 };
1849 assert!(r.is_try_at_epoch());
1850 assert!(r.is_retryable());
1851 assert_eq!(r.get_epoch(), Some(1_700_000_000));
1852 }
1853
1854 #[test]
1855 fn poll_result_unexpected_error() {
1856 let r = PollResult::UnexpectedError { message: "boom".into() };
1857 assert!(r.is_unexpected_error());
1858 assert!(!r.is_retryable());
1859 assert_eq!(r.as_error_message(), Some("boom"));
1860 }
1861
1862 #[test]
1863 fn poll_result_dont_try_again() {
1864 let r = PollResult::DontTryAgain { reason: "expired".into() };
1865 assert!(r.is_dont_try_again());
1866 assert!(r.is_terminal());
1867 assert!(!r.is_retryable());
1868 assert_eq!(r.as_error_message(), Some("expired"));
1869 }
1870
1871 #[test]
1872 fn poll_result_display() {
1873 assert_eq!(PollResult::Success { order: None, signature: None }.to_string(), "success");
1874 assert_eq!(PollResult::TryNextBlock.to_string(), "try-next-block");
1875 assert_eq!(PollResult::TryOnBlock { block_number: 10 }.to_string(), "try-on-block(10)");
1876 assert_eq!(PollResult::TryAtEpoch { epoch: 99 }.to_string(), "try-at-epoch(99)");
1877 assert_eq!(
1878 PollResult::UnexpectedError { message: "x".into() }.to_string(),
1879 "unexpected-error(x)"
1880 );
1881 assert_eq!(
1882 PollResult::DontTryAgain { reason: "y".into() }.to_string(),
1883 "dont-try-again(y)"
1884 );
1885 }
1886
1887 #[test]
1890 fn proof_location_as_str() {
1891 assert_eq!(ProofLocation::Private.as_str(), "private");
1892 assert_eq!(ProofLocation::Emitted.as_str(), "emitted");
1893 assert_eq!(ProofLocation::Swarm.as_str(), "swarm");
1894 assert_eq!(ProofLocation::Waku.as_str(), "waku");
1895 assert_eq!(ProofLocation::Reserved.as_str(), "reserved");
1896 assert_eq!(ProofLocation::Ipfs.as_str(), "ipfs");
1897 }
1898
1899 #[test]
1900 fn proof_location_predicates() {
1901 assert!(ProofLocation::Private.is_private());
1902 assert!(ProofLocation::Emitted.is_emitted());
1903 assert!(ProofLocation::Swarm.is_swarm());
1904 assert!(ProofLocation::Waku.is_waku());
1905 assert!(ProofLocation::Reserved.is_reserved());
1906 assert!(ProofLocation::Ipfs.is_ipfs());
1907 assert!(!ProofLocation::Private.is_emitted());
1909 assert!(!ProofLocation::Ipfs.is_private());
1910 }
1911
1912 #[test]
1913 fn proof_location_default_is_private() {
1914 assert_eq!(ProofLocation::default(), ProofLocation::Private);
1915 }
1916
1917 #[test]
1918 fn proof_location_display() {
1919 assert_eq!(ProofLocation::Ipfs.to_string(), "ipfs");
1920 assert_eq!(ProofLocation::Waku.to_string(), "waku");
1921 }
1922
1923 #[test]
1924 fn proof_location_try_from_u8() {
1925 assert_eq!(ProofLocation::try_from(0u8).unwrap(), ProofLocation::Private);
1926 assert_eq!(ProofLocation::try_from(1u8).unwrap(), ProofLocation::Emitted);
1927 assert_eq!(ProofLocation::try_from(5u8).unwrap(), ProofLocation::Ipfs);
1928 assert!(ProofLocation::try_from(6u8).is_err());
1929 assert!(ProofLocation::try_from(255u8).is_err());
1930 }
1931
1932 #[test]
1933 fn proof_location_try_from_str() {
1934 assert_eq!(ProofLocation::try_from("private").unwrap(), ProofLocation::Private);
1935 assert_eq!(ProofLocation::try_from("ipfs").unwrap(), ProofLocation::Ipfs);
1936 assert!(ProofLocation::try_from("unknown").is_err());
1937 }
1938
1939 #[test]
1940 fn proof_location_into_u8() {
1941 let v: u8 = ProofLocation::Private.into();
1942 assert_eq!(v, 0);
1943 let v: u8 = ProofLocation::Ipfs.into();
1944 assert_eq!(v, 5);
1945 }
1946
1947 #[test]
1950 fn proof_struct_new() {
1951 let p = ProofStruct::new(ProofLocation::Swarm, vec![1, 2, 3]);
1952 assert!(p.is_swarm());
1953 assert!(p.has_data());
1954 assert_eq!(p.data_len(), 3);
1955 }
1956
1957 #[test]
1958 fn proof_struct_private() {
1959 let p = ProofStruct::private();
1960 assert!(p.is_private());
1961 assert!(p.is_empty());
1962 assert!(!p.has_data());
1963 assert_eq!(p.data_len(), 0);
1964 }
1965
1966 #[test]
1967 fn proof_struct_emitted() {
1968 let p = ProofStruct::emitted();
1969 assert!(p.is_emitted());
1970 assert!(p.is_empty());
1971 }
1972
1973 #[test]
1974 fn proof_struct_with_location() {
1975 let p = ProofStruct::private().with_location(ProofLocation::Ipfs);
1976 assert!(p.is_ipfs());
1977 }
1978
1979 #[test]
1980 fn proof_struct_with_data() {
1981 let p = ProofStruct::private().with_data(vec![0xCA, 0xFE]);
1982 assert!(p.has_data());
1983 assert_eq!(p.data_len(), 2);
1984 }
1985
1986 #[test]
1987 fn proof_struct_delegated_predicates() {
1988 assert!(ProofStruct::new(ProofLocation::Waku, vec![]).is_waku());
1989 assert!(ProofStruct::new(ProofLocation::Reserved, vec![]).is_reserved());
1990 }
1991
1992 #[test]
1993 fn proof_struct_display() {
1994 let p = ProofStruct::private();
1995 assert_eq!(p.to_string(), "proof(private)");
1996 let p = ProofStruct::new(ProofLocation::Ipfs, vec![1]);
1997 assert_eq!(p.to_string(), "proof(ipfs)");
1998 }
1999
2000 #[test]
2003 fn proof_location_try_from_str_all() {
2004 for (s, expected) in [
2005 ("emitted", ProofLocation::Emitted),
2006 ("swarm", ProofLocation::Swarm),
2007 ("waku", ProofLocation::Waku),
2008 ("reserved", ProofLocation::Reserved),
2009 ] {
2010 assert_eq!(ProofLocation::try_from(s).unwrap(), expected);
2011 }
2012 }
2013
2014 #[test]
2015 fn proof_location_try_from_u8_all() {
2016 assert_eq!(ProofLocation::try_from(2u8).unwrap(), ProofLocation::Swarm);
2017 assert_eq!(ProofLocation::try_from(3u8).unwrap(), ProofLocation::Waku);
2018 assert_eq!(ProofLocation::try_from(4u8).unwrap(), ProofLocation::Reserved);
2019 }
2020
2021 #[test]
2024 fn twap_data_serde_roundtrip() {
2025 let data = TwapData::sell(
2026 Address::repeat_byte(0x11),
2027 Address::repeat_byte(0x22),
2028 U256::from(1000u64),
2029 4,
2030 3600,
2031 )
2032 .with_buy_amount(U256::from(800u64))
2033 .with_start_time(TwapStartTime::At(1_000_000))
2034 .with_duration_of_part(DurationOfPart::limit(900));
2035
2036 let json = serde_json::to_string(&data).unwrap();
2037 let back: TwapData = serde_json::from_str(&json).unwrap();
2038 assert_eq!(back.sell_amount, data.sell_amount);
2039 assert_eq!(back.buy_amount, data.buy_amount);
2040 assert_eq!(back.num_parts, data.num_parts);
2041 assert_eq!(back.part_duration, data.part_duration);
2042 }
2043
2044 #[test]
2047 fn conditional_order_params_serde_roundtrip() {
2048 let params = ConditionalOrderParams::new(
2049 TWAP_HANDLER_ADDRESS,
2050 B256::repeat_byte(0xAB),
2051 vec![0xDE, 0xAD, 0xBE, 0xEF],
2052 );
2053 let json = serde_json::to_string(¶ms).unwrap();
2054 let back: ConditionalOrderParams = serde_json::from_str(&json).unwrap();
2055 assert_eq!(back.handler, params.handler);
2056 assert_eq!(back.salt, params.salt);
2057 assert_eq!(back.static_input, params.static_input);
2058 }
2059
2060 #[test]
2063 fn block_info_new() {
2064 let b = BlockInfo { block_number: 100, block_timestamp: 1_700_000_000 };
2065 assert_eq!(b.block_number, 100);
2066 assert_eq!(b.block_timestamp, 1_700_000_000);
2067 }
2068
2069 #[test]
2072 fn gpv2_order_try_from_bad_kind_fails() {
2073 let o = make_gpv2_order();
2074 let result = cow_signing::types::UnsignedOrder::try_from(&o);
2076 assert!(result.is_err());
2077 }
2078}
2079
2080#[derive(Debug, Clone)]
2088pub struct ProofStruct {
2089 pub location: ProofLocation,
2091 pub data: Vec<u8>,
2093}
2094
2095#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2101pub struct BlockInfo {
2102 pub block_number: u64,
2104 pub block_timestamp: u64,
2106}
2107
2108impl BlockInfo {
2109 #[must_use]
2120 pub const fn new(block_number: u64, block_timestamp: u64) -> Self {
2121 Self { block_number, block_timestamp }
2122 }
2123}
2124
2125impl fmt::Display for BlockInfo {
2126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2127 write!(f, "block(#{}, ts={})", self.block_number, self.block_timestamp)
2128 }
2129}
2130
2131#[derive(Debug, Clone, PartialEq, Eq)]
2137pub enum IsValidResult {
2138 Valid,
2140 Invalid {
2142 reason: String,
2144 },
2145}
2146
2147pub type IsValid = ();
2149
2150pub type IsNotValid = String;
2152
2153impl IsValidResult {
2154 #[must_use]
2160 pub const fn is_valid(&self) -> bool {
2161 matches!(self, Self::Valid)
2162 }
2163
2164 #[must_use]
2171 pub fn reason(&self) -> Option<&str> {
2172 match self {
2173 Self::Valid => None,
2174 Self::Invalid { reason } => Some(reason),
2175 }
2176 }
2177
2178 #[must_use]
2184 pub const fn valid() -> Self {
2185 Self::Valid
2186 }
2187
2188 #[must_use]
2198 pub fn invalid(reason: impl Into<String>) -> Self {
2199 Self::Invalid { reason: reason.into() }
2200 }
2201}
2202
2203impl fmt::Display for IsValidResult {
2204 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2205 match self {
2206 Self::Valid => f.write_str("valid"),
2207 Self::Invalid { reason } => write!(f, "invalid: {reason}"),
2208 }
2209 }
2210}
2211
2212pub const DEFAULT_TEST_HANDLER: &str = "0x910d00a310f7Dc5B29FE73458F47f519be547D3d";
2218
2219pub const DEFAULT_TEST_SALT: &str =
2221 "0x9379a0bf532ff9a66ffde940f94b1a025d6f18803054c1aef52dc94b15255bbe";
2222
2223#[derive(Debug, Clone)]
2227pub struct TestConditionalOrderParams {
2228 pub handler: Address,
2230 pub salt: B256,
2232 pub static_input: Vec<u8>,
2234 pub is_single_order: bool,
2236}
2237
2238impl Default for TestConditionalOrderParams {
2239 fn default() -> Self {
2240 Self {
2241 handler: DEFAULT_TEST_HANDLER.parse().map_or(Address::ZERO, |a| a),
2242 salt: DEFAULT_TEST_SALT.parse().map_or(B256::ZERO, |s| s),
2243 static_input: Vec::new(),
2244 is_single_order: true,
2245 }
2246 }
2247}
2248
2249#[must_use]
2263pub fn create_test_conditional_order(
2264 overrides: Option<TestConditionalOrderParams>,
2265) -> ConditionalOrderParams {
2266 let test = overrides.unwrap_or_default();
2267 ConditionalOrderParams {
2268 handler: test.handler,
2269 salt: test.salt,
2270 static_input: test.static_input,
2271 }
2272}
2273
2274#[cfg(test)]
2275#[allow(clippy::tests_outside_test_module, reason = "inner module pattern")]
2276mod post_definition_tests {
2277 use super::*;
2278
2279 #[test]
2282 fn block_info_constructor_sets_fields() {
2283 let b = BlockInfo::new(42, 1_700_000_000);
2284 assert_eq!(b.block_number, 42);
2285 assert_eq!(b.block_timestamp, 1_700_000_000);
2286 }
2287
2288 #[test]
2289 fn block_info_display_contains_number_and_timestamp() {
2290 let b = BlockInfo::new(123, 456);
2291 let rendered = format!("{b}");
2292 assert!(rendered.contains("#123"));
2293 assert!(rendered.contains("456"));
2294 }
2295
2296 #[test]
2299 fn is_valid_result_constructors_and_predicates() {
2300 let ok = IsValidResult::valid();
2301 assert!(ok.is_valid());
2302 assert_eq!(ok.reason(), None);
2303
2304 let bad = IsValidResult::invalid("price too low");
2305 assert!(!bad.is_valid());
2306 assert_eq!(bad.reason(), Some("price too low"));
2307 }
2308
2309 #[test]
2310 fn is_valid_result_display_renders_both_variants() {
2311 assert_eq!(format!("{}", IsValidResult::valid()), "valid");
2312 let rendered = format!("{}", IsValidResult::invalid("bad strike"));
2313 assert!(rendered.starts_with("invalid"));
2314 assert!(rendered.contains("bad strike"));
2315 }
2316
2317 #[test]
2320 fn test_conditional_order_params_default_resolves_constants() {
2321 let defaults = TestConditionalOrderParams::default();
2322 assert_ne!(defaults.handler, Address::ZERO);
2326 assert_ne!(defaults.salt, B256::ZERO);
2327 assert!(defaults.static_input.is_empty());
2328 assert!(defaults.is_single_order);
2329 }
2330
2331 #[test]
2332 fn create_test_conditional_order_uses_defaults_when_none() {
2333 let params = create_test_conditional_order(None);
2334 let defaults = TestConditionalOrderParams::default();
2335 assert_eq!(params.handler, defaults.handler);
2336 assert_eq!(params.salt, defaults.salt);
2337 assert_eq!(params.static_input, defaults.static_input);
2338 }
2339
2340 #[test]
2341 fn create_test_conditional_order_accepts_overrides() {
2342 let overrides = TestConditionalOrderParams {
2343 handler: Address::repeat_byte(0xAB),
2344 salt: B256::repeat_byte(0xCD),
2345 static_input: vec![1, 2, 3, 4],
2346 is_single_order: false,
2347 };
2348 let params = create_test_conditional_order(Some(overrides.clone()));
2349 assert_eq!(params.handler, overrides.handler);
2350 assert_eq!(params.salt, overrides.salt);
2351 assert_eq!(params.static_input, overrides.static_input);
2352 }
2353}