From dd787a39f4f40adb3974ab5ae42b2147a7f4d91f Mon Sep 17 00:00:00 2001 From: Kaze <230549489+kaze-cow@users.noreply.github.com> Date: Wed, 26 Aug 2026 17:04:29 +0900 Subject: [PATCH 01/11] Pack `OrderIntent` flags into a single byte MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `kind` and `partially_fillable` each occupied a whole byte of the encoded intent, and each needed its own range check on decode. Fold both into one flags byte instead: `partially_fillable` takes the most significant defined bit, `kind` the next one down, and every remaining bit is reserved and must be zero. `OrderIntent` keeps the idiomatic representation — an `OrderKind` enum and a `bool` — so callers are unaffected. Only the wire format and the validation change: a single mask check now rejects any byte carrying an undefined bit, which keeps the encoding injective and so keeps order UIDs unique. `EncodedOrderIntent::SIZE` goes 150 -> 149 and `EncodedOrderAccount::SIZE` 201 -> 200. This is a BREAKING CHANGE to the `OrderIntent` encoding: it repurposes existing bytes, so it moves every order PDA and changes every order UID. Co-Authored-By: Claude Opus 5 (1M context) --- DESIGN.md | 4 + bench-report.json | 22 +- interface/src/data/intent.rs | 338 ++++++++++++---------- interface/src/data/order.rs | 33 +-- interface/src/instruction/create_order.rs | 6 +- 5 files changed, 219 insertions(+), 184 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index d999f7b6..4468c674 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -132,6 +132,10 @@ struct OrderIntent { } ``` +The intent's `kind` and `partially_fillable` share a single flags byte in the +encoded form, one bit each, with the remaining bits reserved and required to be +zero. + Differences with Ethereum: - In Solana, the spender token account (and the owner) is part of the intent, while in Ethereum it is implied in the signature. diff --git a/bench-report.json b/bench-report.json index 2b9053c5..36e8bc43 100644 --- a/bench-report.json +++ b/bench-report.json @@ -27,22 +27,22 @@ "create_buffers/happy_path_creates_initialized_buffer_token_account": 10340, "create_buffers/happy_path_creates_multiple_buffers_in_one_instruction": 21731, "create_buffers/max_buffers_in_one_instruction": 176947, - "create_order/happy_path_creates_order_pda_with_expected_body": 7921, + "create_order/happy_path_creates_order_pda_with_expected_body": 3416, "initialize/happy_path_initializes_state_pda_with_expected_data": 4526, "reclaim_buffer/funded_buffer_is_skipped": 6299, "reclaim_buffer/happy_path_reclaims_empty_buffer_to_the_authority_itself": 7447, "reclaim_buffer/max_buffers_in_one_instruction": 136501, "reclaim_buffer/reclaims_multiple_buffers_skipping_funded": 18043, - "reclaim_order/happy_path_returns_lamports_and_closes_pda": 2133, + "reclaim_order/happy_path_returns_lamports_and_closes_pda": 2125, "settle/finalizes_with_no_pushes": 7043, - "settle/pulls_from_multiple_orders": 19750, - "settle/pulls_funds_to_destination": 13417, - "settle/pulls_to_multiple_destinations": 14564, - "settle/pushes_a_single_order": 12267, - "settle/pushes_several_orders_from_different_buffers": 17451, - "settle/pushes_several_orders_from_one_buffer": 17452, - "settle/settles_a_single_order": 12285, - "settle/settles_multiple_orders": 22679, + "settle/pulls_from_multiple_orders": 19738, + "settle/pulls_funds_to_destination": 13411, + "settle/pulls_to_multiple_destinations": 14558, + "settle/pushes_a_single_order": 12261, + "settle/pushes_several_orders_from_different_buffers": 17439, + "settle/pushes_several_orders_from_one_buffer": 17440, + "settle/settles_a_single_order": 12279, + "settle/settles_multiple_orders": 22661, "transfer_authority/manager_can_transfer_manager": 3170, "transfer_authority/manager_can_transfer_reclaim_authority": 3172, "transfer_authority/reclaim_authority_can_transfer_itself": 3175 @@ -51,7 +51,7 @@ "create_buffers/happy_path_creates_initialized_buffer_token_account": 303, "create_buffers/happy_path_creates_multiple_buffers_in_one_instruction": 435, "create_buffers/max_buffers_in_one_instruction": 331, - "create_order/happy_path_creates_order_pda_with_expected_body": 389, + "create_order/happy_path_creates_order_pda_with_expected_body": 388, "initialize/happy_path_initializes_state_pda_with_expected_data": 301, "reclaim_buffer/funded_buffer_is_skipped": 400, "reclaim_buffer/happy_path_reclaims_empty_buffer_to_the_authority_itself": 400, diff --git a/interface/src/data/intent.rs b/interface/src/data/intent.rs index 491f5310..84ec0c71 100644 --- a/interface/src/data/intent.rs +++ b/interface/src/data/intent.rs @@ -2,18 +2,16 @@ //! //! Two types live here: //! -//! - [`OrderIntent`] is the idiomatic Rust representation. Every value is valid -//! by construction: `kind` is an [`OrderKind`] enum, `partially_fillable` is -//! a `bool`. Callers pattern-match on it directly. +//! - [`OrderIntent`] is the idiomatic Rust representation. //! - [`EncodedOrderIntent`] is its canonical byte representation: the only //! thing sent on the wire and also the data encoding used to generate the -//! order UID. +//! order UID. There, `kind` and `partially_fillable` share a single flags +//! byte. //! //! Conversion is asymmetric: [`EncodedOrderIntent`]`::from(OrderIntent)` is -//! infallible; decoding raw bytes via [`OrderIntent`]`::try_from` returns -//! `Result` and rejects out-of-range `kind` or `partially_fillable` bytes up -//! front. There is no path that produces an `OrderIntent` whose `kind` byte or -//! `partially_fillable` byte was not validated. +//! infallible, but decoding raw bytes via [`OrderIntent`]`::try_from` returns +//! `Result` and rejects a flags byte carrying a bit the encoding doesn't +//! define. use core::mem::size_of; @@ -23,7 +21,8 @@ use solana_hash::Hash; use solana_program_error::ProgramError; use solana_pubkey::Pubkey; -/// Direction of the trade. +/// Direction of the trade. The discriminants are the values the `kind` bit of +/// the encoded flags byte takes. #[derive(Clone, Copy, Debug, Eq, PartialEq, Default)] #[repr(u8)] pub enum OrderKind { @@ -83,7 +82,7 @@ pub struct OrderIntent { pub app_data: [u8; 32], } -/// Canonical 150-byte representation of an [`OrderIntent`]. The wire format and +/// Canonical 149-byte representation of an [`OrderIntent`]. The wire format and /// the order UID preimage. /// /// Layout: one character per byte, cell widths proportional to field size, @@ -91,16 +90,20 @@ pub struct OrderIntent { /// annotated below. Amounts and `valid_to` are little-endian encoded. /// /// ```text -/// partially_fillable ─────┐ -/// kind ────┐│ -/// ┌───────────────────────────────┬───────────────────────────────┬───────────────────────────────┬───────┬───────┬───┬┬┬───────────────────────────────┐ -/// │ │ │ │sell_ │buy_ │val│││ │ -/// │ owner │ buy_token_account │ sell_token_account │ │ │id_│││ app_data │ -/// │ │ │ │amount │amount │to │││ │ -/// └───────────────────────────────┴───────────────────────────────┴───────────────────────────────┴───────┴───────┴───┴┴┴───────────────────────────────┘ -/// 0 32 64 96 104 112 116 118 150 -/// 117 +/// flags ───┐ +/// ┌───────────────────────────────┬───────────────────────────────┬───────────────────────────────┬───────┬───────┬───┬┬───────────────────────────────┐ +/// │ │ │ │sell_ │buy_ │val││ │ +/// │ owner │ buy_token_account │ sell_token_account │ │ │id_││ app_data │ +/// │ │ │ │amount │amount │to ││ │ +/// └───────────────────────────────┴───────────────────────────────┴───────────────────────────────┴───────┴───────┴───┴┴───────────────────────────────┘ +/// 0 32 64 96 104 112 116 149 +/// 117 /// ``` +/// +/// The flags byte packs `kind` and the intent's booleans, one bit each, from +/// the most significant defined bit down: `partially_fillable`, then `kind` +/// ([`OrderKind::Sell`] = 0, [`OrderKind::Buy`] = 1). Every other bit is +/// reserved and must be zero. #[derive(Clone, Debug, Deref, Eq, PartialEq)] pub struct EncodedOrderIntent([u8; Self::SIZE]); @@ -112,11 +115,17 @@ impl EncodedOrderIntent { const WIDTH_SELL_AMOUNT: usize = size_of::(); const WIDTH_BUY_AMOUNT: usize = size_of::(); const WIDTH_VALID_TO: usize = size_of::(); - const WIDTH_KIND: usize = size_of::(); - const WIDTH_PARTIALLY_FILLABLE: usize = size_of::(); + const WIDTH_FLAGS: usize = size_of::(); const WIDTH_APP_DATA: usize = size_of::<[u8; 32]>(); - pub const SIZE: usize = 150; + // The bits of the flags byte, from the most significant defined bit down: + // one per boolean field of `OrderIntent`, plus `kind`. + const FLAG_PARTIALLY_FILLABLE: u8 = 1 << 1; + const FLAG_KIND: u8 = 1 << 0; + + const FLAGS_MASK: u8 = Self::FLAG_PARTIALLY_FILLABLE | Self::FLAG_KIND; + + pub const SIZE: usize = 149; /// Canonical hash of the bytes. pub fn hash(&self) -> Hash { @@ -124,9 +133,8 @@ impl EncodedOrderIntent { } /// Decode raw bytes to an [`OrderIntent`] and compute the UID in one shot. - /// Returns [`ProgramError::InvalidInstructionData`] for an out-of-range - /// `kind` or `partially_fillable` byte; every other byte combination - /// decodes. + /// Returns [`ProgramError::InvalidInstructionData`] for a flags byte that + /// doesn't encode correctly; every other byte combination decodes. pub fn decode_and_hash(bytes: &[u8; Self::SIZE]) -> Result<(OrderIntent, Hash), ProgramError> { let intent = OrderIntent::try_from(bytes)?; // The UID is the SHA-256 of the input bytes. Hashing the input @@ -143,6 +151,15 @@ pub fn hash_bytes(bytes: &[u8; EncodedOrderIntent::SIZE]) -> Hash { solana_sha256_hasher::hashv(&[bytes.as_slice()]) } +/// The intent's `kind` and booleans packed into their canonical flags byte. +/// Reserved bits are left clear. +fn flags_byte(intent: &OrderIntent) -> u8 { + // `wrapping_neg` transforms a true `bool` (effectively 1) into 0xffffffff + let mask = |value: u8| value.wrapping_neg(); + EncodedOrderIntent::FLAG_PARTIALLY_FILLABLE & mask(intent.partially_fillable as u8) + | EncodedOrderIntent::FLAG_KIND & mask(intent.kind as u8) +} + impl From<&EncodedOrderIntent> for [u8; EncodedOrderIntent::SIZE] { fn from(encoded: &EncodedOrderIntent) -> Self { encoded.0 @@ -154,17 +171,7 @@ impl From<&OrderIntent> for EncodedOrderIntent { // `mut_array_refs` checks that `SIZE` is consistent with the sum of // the widths. let mut out = [0u8; Self::SIZE]; - let ( - owner, - buy_token, - sell_token, - sell_amount, - buy_amount, - valid_to, - kind, - partially_fillable, - app_data, - ) = mut_array_refs![ + let (owner, buy_token, sell_token, sell_amount, buy_amount, valid_to, flags, app_data) = mut_array_refs![ &mut out, EncodedOrderIntent::WIDTH_OWNER, EncodedOrderIntent::WIDTH_BUY_TOKEN, @@ -172,8 +179,7 @@ impl From<&OrderIntent> for EncodedOrderIntent { EncodedOrderIntent::WIDTH_SELL_AMOUNT, EncodedOrderIntent::WIDTH_BUY_AMOUNT, EncodedOrderIntent::WIDTH_VALID_TO, - EncodedOrderIntent::WIDTH_KIND, - EncodedOrderIntent::WIDTH_PARTIALLY_FILLABLE, + EncodedOrderIntent::WIDTH_FLAGS, EncodedOrderIntent::WIDTH_APP_DATA ]; *owner = intent.owner.to_bytes(); @@ -182,8 +188,7 @@ impl From<&OrderIntent> for EncodedOrderIntent { *sell_amount = intent.sell_amount.to_le_bytes(); *buy_amount = intent.buy_amount.to_le_bytes(); *valid_to = intent.valid_to.to_le_bytes(); - *kind = [intent.kind as u8]; - *partially_fillable = [intent.partially_fillable as u8]; + *flags = [flags_byte(intent)]; *app_data = intent.app_data; Self(out) } @@ -200,17 +205,7 @@ impl TryFrom<&[u8; EncodedOrderIntent::SIZE]> for OrderIntent { // as valid or it might be possible to replay the same order more // than once. - let ( - owner, - buy_token, - sell_token, - sell_amount, - buy_amount, - valid_to, - kind, - partially_fillable, - app_data, - ) = array_refs![ + let (owner, buy_token, sell_token, sell_amount, buy_amount, valid_to, flags, app_data) = array_refs![ bytes, EncodedOrderIntent::WIDTH_OWNER, EncodedOrderIntent::WIDTH_BUY_TOKEN, @@ -218,11 +213,18 @@ impl TryFrom<&[u8; EncodedOrderIntent::SIZE]> for OrderIntent { EncodedOrderIntent::WIDTH_SELL_AMOUNT, EncodedOrderIntent::WIDTH_BUY_AMOUNT, EncodedOrderIntent::WIDTH_VALID_TO, - EncodedOrderIntent::WIDTH_KIND, - EncodedOrderIntent::WIDTH_PARTIALLY_FILLABLE, + EncodedOrderIntent::WIDTH_FLAGS, EncodedOrderIntent::WIDTH_APP_DATA ]; + let [flags] = *flags; + // A reserved bit carries no meaning to this version of the program, so + // accepting it would give the same intent several encodings, and with + // them several UIDs. + if flags & !EncodedOrderIntent::FLAGS_MASK != 0 { + return Err(ProgramError::InvalidInstructionData); + } + Ok(OrderIntent { owner: Pubkey::new_from_array(*owner), buy_token_account: Pubkey::new_from_array(*buy_token), @@ -230,16 +232,12 @@ impl TryFrom<&[u8; EncodedOrderIntent::SIZE]> for OrderIntent { sell_amount: u64::from_le_bytes(*sell_amount), buy_amount: u64::from_le_bytes(*buy_amount), valid_to: u32::from_le_bytes(*valid_to), - kind: match kind { - [0] => OrderKind::Sell, - [1] => OrderKind::Buy, - _ => return Err(ProgramError::InvalidInstructionData), - }, - partially_fillable: match partially_fillable { - [0] => false, - [1] => true, - _ => return Err(ProgramError::InvalidInstructionData), + kind: if flags & EncodedOrderIntent::FLAG_KIND == 0 { + OrderKind::Sell + } else { + OrderKind::Buy }, + partially_fillable: flags & EncodedOrderIntent::FLAG_PARTIALLY_FILLABLE != 0, app_data: *app_data, }) } @@ -266,14 +264,13 @@ impl OrderIntent { pub mod fixtures { use proptest::{prelude::*, strategy::Union}; - use super::{EncodedOrderIntent, OrderIntent, OrderKind, Pubkey}; + use super::{flags_byte, EncodedOrderIntent, OrderIntent, OrderKind, Pubkey}; /// Every valid [`OrderKind`]. pub const ALL_ORDER_KINDS: [OrderKind; 2] = [OrderKind::Sell, OrderKind::Buy]; // Hardcoded but verified in a sanity-check test. - pub const KIND_OFFSET: usize = 116; - pub const PARTIALLY_FILLABLE_OFFSET: usize = KIND_OFFSET + EncodedOrderIntent::WIDTH_KIND; + pub const FLAGS_OFFSET: usize = 116; pub fn sample_intent(kind: OrderKind, partially_fillable: bool) -> OrderIntent { OrderIntent { @@ -294,6 +291,35 @@ pub mod fixtures { Union::new(ALL_ORDER_KINDS.map(Just)) } + /// The canonical flags byte for the given `kind` and booleans, as the + /// encoder writes it. Lets a test pin the flags slot of otherwise arbitrary + /// bytes. + pub fn intent_flags_byte(partially_fillable: bool, kind: OrderKind) -> u8 { + flags_byte(&OrderIntent { + partially_fillable, + kind, + ..Default::default() + }) + } + + /// Any flags byte the decoder accepts. + pub fn arb_flags_byte() -> impl Strategy { + (any::(), arb_order_kind()) + .prop_map(|(partially_fillable, kind)| intent_flags_byte(partially_fillable, kind)) + } + + /// Any flags byte the decoder rejects: one carrying at least one bit + /// outside those the encoding defines. + pub fn arb_invalid_flags_byte() -> impl Strategy { + ( + any::().prop_filter("at least one reserved bit must be set", |reserved| { + reserved & !EncodedOrderIntent::FLAGS_MASK != 0 + }), + arb_flags_byte(), + ) + .prop_map(|(reserved, defined)| (reserved & !EncodedOrderIntent::FLAGS_MASK) | defined) + } + /// Any valid [`OrderIntent`]. pub fn arb_order_intent() -> impl Strategy { ( @@ -329,15 +355,17 @@ pub mod fixtures { mod tests { use hex_literal::hex; - use super::fixtures::{sample_intent, KIND_OFFSET, PARTIALLY_FILLABLE_OFFSET}; + use super::fixtures::{sample_intent, FLAGS_OFFSET}; use super::*; - // Full Cartesian product of `OrderKind × bool` for tests that need to - // exercise every shape an `OrderIntent` can take on these axes. - fn all_kind_and_fillable() -> impl Iterator { - fixtures::ALL_ORDER_KINDS - .into_iter() - .flat_map(|kind| core::iter::repeat(kind).zip([false, true])) + // Every shape an `OrderIntent` can take on its validated axes: the `kind` + // enum and the `partially_fillable` flag bit. + fn all_intent_shapes() -> impl Iterator { + fixtures::ALL_ORDER_KINDS.into_iter().flat_map(|kind| { + [false, true] + .into_iter() + .map(move |partially_fillable| sample_intent(kind, partially_fillable)) + }) } // Pin each width to the size of the `OrderIntent` field it encodes. The @@ -372,11 +400,6 @@ mod tests { EncodedOrderIntent::WIDTH_VALID_TO, size_of_val(&intent.valid_to) ); - assert_eq!(EncodedOrderIntent::WIDTH_KIND, size_of_val(&intent.kind)); - assert_eq!( - EncodedOrderIntent::WIDTH_PARTIALLY_FILLABLE, - size_of_val(&intent.partially_fillable) - ); assert_eq!( EncodedOrderIntent::WIDTH_APP_DATA, size_of_val(&intent.app_data) @@ -386,9 +409,48 @@ mod tests { } #[test] - fn roundtrip_all_kind_and_bool_combinations() { - for (kind, partially_fillable) in all_kind_and_fillable() { - let intent = sample_intent(kind, partially_fillable); + fn every_flag_owns_a_distinct_bit() { + let flags_of = |intent: &OrderIntent| EncodedOrderIntent::from(intent)[FLAGS_OFFSET]; + let cleared = OrderIntent { + partially_fillable: false, + kind: OrderKind::Sell, + ..sample_intent(OrderKind::Sell, false) + }; + assert_eq!(flags_of(&cleared), 0); + + let set_one_by_one = [ + ( + EncodedOrderIntent::FLAG_PARTIALLY_FILLABLE, + OrderIntent { + partially_fillable: true, + ..cleared.clone() + }, + ), + ( + EncodedOrderIntent::FLAG_KIND, + OrderIntent { + kind: OrderKind::Buy, + ..cleared.clone() + }, + ), + ]; + let mut seen = 0u8; + for (bit, intent) in set_one_by_one { + assert_eq!(bit.count_ones(), 1, "a flag must occupy a single bit"); + assert_eq!(seen & bit, 0, "two flags must not share a bit"); + assert!( + seen == 0 || bit < seen, + "each flag must be less significant than the ones before it" + ); + seen |= bit; + assert_eq!(flags_of(&intent), bit); + } + assert_eq!(seen, EncodedOrderIntent::FLAGS_MASK); + } + + #[test] + fn roundtrip_all_kind_and_flag_combinations() { + for intent in all_intent_shapes() { let encoded = EncodedOrderIntent::from(&intent); let (decoded, _uid) = EncodedOrderIntent::decode_and_hash(&encoded).expect("example must decode"); @@ -402,8 +464,8 @@ mod tests { // encode/decode, this test fails. #[test] fn decode_and_hash_uid_matches_encoded_hash() { - for (kind, partially_fillable) in all_kind_and_fillable() { - let encoded = EncodedOrderIntent::from(&sample_intent(kind, partially_fillable)); + for intent in all_intent_shapes() { + let encoded = EncodedOrderIntent::from(&intent); let (_intent, uid) = EncodedOrderIntent::decode_and_hash(&encoded).expect("example must decode"); assert_eq!(uid, encoded.hash()); @@ -421,44 +483,51 @@ mod tests { assert_eq!( first_differing_byte(sell_false.as_slice(), sell_true.as_slice()) - .expect("should have different partially fillable byte"), - PARTIALLY_FILLABLE_OFFSET + .expect("should have different flags byte"), + FLAGS_OFFSET ); assert_eq!( first_differing_byte(buy_true.as_slice(), sell_true.as_slice()) - .expect("should have different kind byte"), - KIND_OFFSET + .expect("should have different flags byte"), + FLAGS_OFFSET ); } #[test] - fn decode_rejects_out_of_range_kind() { + fn decode_accepts_defined_flag_bits_only() { let encoded = EncodedOrderIntent::from(&sample_intent(OrderKind::Sell, false)); let mut bytes: [u8; EncodedOrderIntent::SIZE] = *encoded; - for bad in 0x02u8..=0xff { - bytes[KIND_OFFSET] = bad; - let err = EncodedOrderIntent::decode_and_hash(&bytes) - .expect_err("should reject out of range kind"); - assert_eq!(err, ProgramError::InvalidInstructionData); - } - } - - #[test] - fn decode_rejects_non_boolean_partially_fillable() { - let encoded = EncodedOrderIntent::from(&sample_intent(OrderKind::Sell, false)); - let mut bytes: [u8; EncodedOrderIntent::SIZE] = *encoded; - for bad in 0x02u8..=0xff { - bytes[PARTIALLY_FILLABLE_OFFSET] = bad; - let err = EncodedOrderIntent::decode_and_hash(&bytes) - .expect_err("should reject out of range partially fillable"); - assert_eq!(err, ProgramError::InvalidInstructionData); + for flags in u8::MIN..=u8::MAX { + bytes[FLAGS_OFFSET] = flags; + let decoded = EncodedOrderIntent::decode_and_hash(&bytes); + if flags & !EncodedOrderIntent::FLAGS_MASK != 0 { + assert_eq!( + decoded.err(), + Some(ProgramError::InvalidInstructionData), + "flags {flags:#04x} sets a reserved bit and must be rejected", + ); + } else { + let (intent, _uid) = decoded.expect("defined flag bits must decode"); + assert_eq!( + intent.partially_fillable, + flags & EncodedOrderIntent::FLAG_PARTIALLY_FILLABLE != 0, + ); + assert_eq!( + intent.kind, + if flags & EncodedOrderIntent::FLAG_KIND == 0 { + OrderKind::Sell + } else { + OrderKind::Buy + }, + ); + } } } #[test] fn uid_digest_regression() { let intent = sample_intent(OrderKind::Buy, true); - let expected = hex!("7ce7c6a74671090771fa33851387444064aca759ce55b80708723076722f5e00"); + let expected = hex!("d2a82e919ec3d5e8b21c512cf14251e98bf79cdf01f0a2bdd0ecbed3007a9761"); assert_eq!(intent.uid(), Hash::from(expected)); } @@ -489,10 +558,8 @@ mod tests { 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe, // valid_to (0xdead_beef, LE u32) 0xef, 0xbe, 0xad, 0xde, - // kind (Buy = 1) - 0x01, - // partially_fillable (true = 1) - 0x01, + // flags (partially_fillable | kind (Buy = 1)) + 0b00000011, // app_data ([0x44; 32]) 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, @@ -508,19 +575,9 @@ mod tests { use super::*; use crate::data::intent::fixtures::{ - arb_order_intent, arb_order_kind, KIND_OFFSET, PARTIALLY_FILLABLE_OFFSET, + arb_flags_byte, arb_invalid_flags_byte, arb_order_intent, FLAGS_OFFSET, }; - // Any byte not decoding to a valid order type. - fn arb_bad_order_kind_byte() -> impl Strategy { - 2u8..=255 - } - - // Any byte not decoding to a valid bool. - fn arb_bad_bool_byte() -> impl Strategy { - 2u8..=255 - } - proptest! { // For any `OrderIntent`, encoding an intent into an encoded // intent and then decoding it with `decode_and_hash()` returns @@ -535,50 +592,27 @@ mod tests { prop_assert_eq!(uid, encoded.hash()); } - // For any bytes whose `kind` and `partially_fillable` slots - // are valid, `decode_and_hash` and then re-encoding produces - // back the original bytes. + // For any bytes whose flags slot is valid, `decode_and_hash` and + // then re-encoding produces back the original bytes. #[test] fn bytes_roundtrip( mut bytes in any::<[u8; EncodedOrderIntent::SIZE]>(), - kind in arb_order_kind(), - partially_fillable in any::(), + flags in arb_flags_byte(), ) { - bytes[KIND_OFFSET] = kind as u8; - bytes[PARTIALLY_FILLABLE_OFFSET] = partially_fillable as u8; + bytes[FLAGS_OFFSET] = flags; let (intent, _uid) = EncodedOrderIntent::decode_and_hash(&bytes) .map_err(|e| TestCaseError::fail(format!("decode failed: {e:?}")))?; prop_assert_eq!(*EncodedOrderIntent::from(&intent), bytes); } - // For any bytes with an invalid `kind` byte (and a valid - // `partially_fillable`), `decode_and_hash` returns - // `InvalidInstructionData`. - #[test] - fn rejects_invalid_kind_byte( - mut bytes in any::<[u8; EncodedOrderIntent::SIZE]>(), - bad_kind in arb_bad_order_kind_byte(), - partially_fillable in any::(), - ) { - bytes[KIND_OFFSET] = bad_kind; - bytes[PARTIALLY_FILLABLE_OFFSET] = partially_fillable as u8; - prop_assert_eq!( - EncodedOrderIntent::decode_and_hash(&bytes), - Err(ProgramError::InvalidInstructionData), - ); - } - - // Symmetric: any bytes with an out-of-range - // `partially_fillable` byte (and a valid `kind`) return - // `InvalidInstructionData`. + // Symmetric: any bytes whose flags byte carries a reserved bit + // return `InvalidInstructionData`. #[test] - fn rejects_invalid_partially_fillable_byte( + fn rejects_reserved_flag_bits( mut bytes in any::<[u8; EncodedOrderIntent::SIZE]>(), - kind in arb_order_kind(), - bad_pf in arb_bad_bool_byte(), + bad_flags in arb_invalid_flags_byte(), ) { - bytes[KIND_OFFSET] = kind as u8; - bytes[PARTIALLY_FILLABLE_OFFSET] = bad_pf; + bytes[FLAGS_OFFSET] = bad_flags; prop_assert_eq!( EncodedOrderIntent::decode_and_hash(&bytes), Err(ProgramError::InvalidInstructionData), diff --git a/interface/src/data/order.rs b/interface/src/data/order.rs index d362e7d4..20d53066 100644 --- a/interface/src/data/order.rs +++ b/interface/src/data/order.rs @@ -88,7 +88,7 @@ impl OrderAccount { } } -/// Canonical 201-byte representation of an [`OrderAccount`]. The bytes +/// Canonical 200-byte representation of an [`OrderAccount`]. The bytes /// written to/read from the order PDA's data area. /// /// Layout: one character per byte, cell widths proportional to field size, @@ -105,7 +105,7 @@ impl OrderAccount { /// ││││with- │re- │ created_by │ intent (EncodedOrderIntent) │ /// ││││drawn │ceived │ │ │ /// └┴┴┴───────┴───────┴───────────────────────────────┴─────────────────...─────────────────┘ -/// 0 1 2 3 11 19 51 ... 201 +/// 0 1 2 3 11 19 51 ... 200 /// ``` #[derive(Clone, Debug, Deref, Eq, PartialEq)] pub struct EncodedOrderAccount([u8; Self::SIZE]); @@ -120,7 +120,7 @@ impl EncodedOrderAccount { const W_CREATED_BY: usize = size_of::(); const W_INTENT: usize = EncodedOrderIntent::SIZE; - pub const SIZE: usize = 201; + pub const SIZE: usize = 200; /// Single-byte account discriminator. See [`SettlementAccount`]. pub const DISCRIMINATOR: u8 = SettlementAccount::OrderAccount.discriminator(); @@ -317,7 +317,7 @@ mod tests { use super::fixtures::{sample_account, CANCELLED_OFFSET, DISCRIMINATOR_OFFSET, INTENT_OFFSET}; use super::*; use crate::data::intent::{ - fixtures::{sample_intent, KIND_OFFSET, PARTIALLY_FILLABLE_OFFSET}, + fixtures::{sample_intent, FLAGS_OFFSET}, OrderKind, }; @@ -390,9 +390,9 @@ mod tests { (&EncodedOrderIntent::from(&sample_account_base.intent)).into(); // Hack: xoring each byte makes sure all bytes are different. // In general, it isn't guaranteed that the result encodes to a - // valid intent, but in this case we know it because the only bytes - // that may fail decoding are `kind` and `partially_fillable`, both - // of which stay valid if flipped with `^0x01`. + // valid intent, but in this case we know it because the only byte + // that may fail decoding is the flags byte, and `^0x01` only flips + // its `kind` bit, never a reserved one. let bitwise_different_encoded_intent: [u8; EncodedOrderIntent::SIZE] = encoded_intent.map(|b| b ^ 0x01); sample_account_base.intent = @@ -430,13 +430,12 @@ mod tests { fn decode_propagates_invalid_intent() { let mut bytes: [u8; EncodedOrderAccount::SIZE] = EncodedOrderAccount::from(sample_account(false)).into(); - // Corrupt the `kind` byte inside the intent slot: the intent - // decoder rejects it and the order-account decode surfaces that - // failure as `InvalidAccountData`. - let kind_offset = INTENT_OFFSET + KIND_OFFSET; - bytes[kind_offset] = 0x02; + // Set a reserved bit of the flags byte inside the intent slot: the + // intent decoder rejects it and the order-account decode surfaces + // that failure as `InvalidAccountData`. + bytes[INTENT_OFFSET + FLAGS_OFFSET] = 0xff; let err = OrderAccount::try_from(bytes) - .expect_err("an invalid intent kind byte must propagate as a decode failure"); + .expect_err("an invalid intent flags byte must propagate as a decode failure"); assert_eq!(err, ProgramError::InvalidAccountData); } @@ -598,7 +597,7 @@ mod tests { use ::proptest::{prelude::*, test_runner::TestCaseError}; use super::*; - use crate::data::{intent::fixtures::arb_order_kind, order::fixtures::arb_order_account}; + use crate::data::{intent::fixtures::arb_flags_byte, order::fixtures::arb_order_account}; proptest! { // For any `OrderAccount`, encode then decode returns the same @@ -618,13 +617,11 @@ mod tests { fn bytes_roundtrip( mut bytes in any::<[u8; EncodedOrderAccount::SIZE]>(), cancelled in any::(), - kind in arb_order_kind(), - partially_fillable in any::(), + flags in arb_flags_byte(), ) { bytes[DISCRIMINATOR_OFFSET] = EncodedOrderAccount::DISCRIMINATOR; bytes[CANCELLED_OFFSET] = cancelled as u8; - bytes[INTENT_OFFSET + KIND_OFFSET] = kind as u8; - bytes[INTENT_OFFSET + PARTIALLY_FILLABLE_OFFSET] = partially_fillable as u8; + bytes[INTENT_OFFSET + FLAGS_OFFSET] = flags; let account = OrderAccount::try_from(bytes) .map_err(|e| TestCaseError::fail(format!("decode failed: {e:?}")))?; prop_assert_eq!(*EncodedOrderAccount::from(account), bytes); diff --git a/interface/src/instruction/create_order.rs b/interface/src/instruction/create_order.rs index c3172560..1708ecc6 100644 --- a/interface/src/instruction/create_order.rs +++ b/interface/src/instruction/create_order.rs @@ -40,7 +40,7 @@ use crate::{data::intent::EncodedOrderIntent, SettlementInstruction}; /// instruction reverts with `AccountAlreadyInitialized`. Recreating the same /// order is only possible after its PDA has been closed. /// -/// Wire format: `[discriminator=2, ..150 intent bytes]`, 151 bytes. +/// Wire format: `[discriminator=2, ..149 intent bytes]`, 150 bytes. /// Required accounts: /// `[owner (S), created_by (W,S), order_pda (W), system_program (R)]`. /// The system program needs to be available but doesn't need to be at that @@ -84,7 +84,7 @@ impl<'a, A> InstructionInputParsing<'a, A> for CreateOrderInput<'a, A> { const DISCRIMINATOR: SettlementInstruction = SettlementInstruction::CreateOrder; fn parse_body(instruction_data: &'a [u8], accounts: &'a [A]) -> Result { - // Body (discriminator already stripped): exactly the 150 intent bytes. + // Body (discriminator already stripped): exactly the 149 intent bytes. if instruction_data.len() != EncodedOrderIntent::SIZE { return Err(ProgramError::InvalidInstructionData); } @@ -126,7 +126,7 @@ pub mod fixtures { /// and the system program. pub const NUM_ACCOUNTS: usize = 4; - /// Canonical 150-byte intent payload for a valid sell order owned by + /// Canonical 149-byte intent payload for a valid sell order owned by /// [`DEFAULT_OWNER`]. pub fn valid_intent_bytes() -> [u8; EncodedOrderIntent::SIZE] { (&EncodedOrderIntent::from(&OrderIntent { From 2e6bdb90a54b0f16ff17f35e74bc0e849c601cc9 Mon Sep 17 00:00:00 2001 From: Kaze <230549489+kaze-cow@users.noreply.github.com> Date: Wed, 26 Aug 2026 17:16:56 +0900 Subject: [PATCH 02/11] remove unnecessary comment --- interface/src/data/intent.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/data/intent.rs b/interface/src/data/intent.rs index 84ec0c71..a7fce106 100644 --- a/interface/src/data/intent.rs +++ b/interface/src/data/intent.rs @@ -5,8 +5,7 @@ //! - [`OrderIntent`] is the idiomatic Rust representation. //! - [`EncodedOrderIntent`] is its canonical byte representation: the only //! thing sent on the wire and also the data encoding used to generate the -//! order UID. There, `kind` and `partially_fillable` share a single flags -//! byte. +//! order UID. //! //! Conversion is asymmetric: [`EncodedOrderIntent`]`::from(OrderIntent)` is //! infallible, but decoding raw bytes via [`OrderIntent`]`::try_from` returns From 770c911e5950c0c263698027ddccba694ad8b327 Mon Sep 17 00:00:00 2001 From: Kaze <230549489+kaze-cow@users.noreply.github.com> Date: Wed, 26 Aug 2026 17:49:36 +0900 Subject: [PATCH 03/11] refactor into `Flags` struct --- DESIGN.md | 13 +- interface/src/data/intent.rs | 186 ++++++++++++---------- programs/settlement/src/create_order.rs | 12 +- programs/settlement/src/settle/begin.rs | 4 +- programs/settlement/tests/common/order.rs | 14 +- test-cli/src/cmd/create_order.rs | 8 +- 6 files changed, 134 insertions(+), 103 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index 4468c674..d8f51f04 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -124,17 +124,20 @@ struct OrderIntent { buy_amount: u64 // Unix timestamp valid_to: u32 + flags: Flags + // Usual app data field, it isn't directly used in the program. + app_data: [u8; 32] +} + +struct Flags { // Either Buy or Sell kind: OrderKind partially_fillable: bool - // Usual app data field, it isn't directly used in the program. - app_data: [u8; 32] } ``` -The intent's `kind` and `partially_fillable` share a single flags byte in the -encoded form, one bit each, with the remaining bits reserved and required to be -zero. +The fields grouped in `Flags` share a single byte in the encoded form, one bit +each, with the remaining bits reserved and required when decoding to be zero. Differences with Ethereum: diff --git a/interface/src/data/intent.rs b/interface/src/data/intent.rs index a7fce106..b1423e52 100644 --- a/interface/src/data/intent.rs +++ b/interface/src/data/intent.rs @@ -1,6 +1,6 @@ //! Order intents and their canonical byte representation. //! -//! Two types live here: +//! The intent has two representations: //! //! - [`OrderIntent`] is the idiomatic Rust representation. //! - [`EncodedOrderIntent`] is its canonical byte representation: the only @@ -30,6 +30,66 @@ pub enum OrderKind { Buy = 1, } +/// Collection of [`OrderIntent`] fields that can be represented as a single bit. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)] +pub struct Flags { + /// Whether `sell_amount` or `buy_amount` is the exact figure; the + /// other side is treated as the limit (minimum to receive for `Sell`, + /// maximum to spend for `Buy`). + pub kind: OrderKind, + + /// If `true`, the order may be filled across multiple settlements; + /// proceeds and consumption scale proportionally with the amount of + /// the sell side that's been used. If `false`, a single settlement + /// must consume the full sell amount (fill-or-kill). + pub partially_fillable: bool, +} + +impl Flags { + // The bit each field occupies, from the most significant defined bit + // down: one per boolean field, plus `kind`. + const PARTIALLY_FILLABLE: u8 = 1 << 1; + const KIND: u8 = 1 << 0; + + /// Every bit the encoding defines; the others are reserved. + const DEFINED: u8 = Self::PARTIALLY_FILLABLE | Self::KIND; +} + +impl From for [u8; 1] { + /// The canonical flags byte. Reserved bits are left clear. + fn from(flags: Flags) -> Self { + // `wrapping_neg` transforms a true `bool` (effectively 1) into 0xff + let mask = |value: u8| value.wrapping_neg(); + [ + Flags::PARTIALLY_FILLABLE & mask(flags.partially_fillable as u8) + | Flags::KIND & mask(flags.kind as u8), + ] + } +} + +impl TryFrom<[u8; 1]> for Flags { + type Error = ProgramError; + + /// Decodes a flags byte, rejecting any reserved bit with + /// [`ProgramError::InvalidInstructionData`]. A reserved bit carries no + /// meaning to this version of the program, so accepting it would give the + /// same flags several encodings, and with them several UIDs. + fn try_from(bytes: [u8; 1]) -> Result { + let [byte] = bytes; + if byte & !Self::DEFINED != 0 { + return Err(ProgramError::InvalidInstructionData); + } + Ok(Flags { + kind: if byte & Self::KIND == 0 { + OrderKind::Sell + } else { + OrderKind::Buy + }, + partially_fillable: byte & Self::PARTIALLY_FILLABLE != 0, + }) + } +} + #[derive(Clone, Debug, Eq, PartialEq, Default)] pub struct OrderIntent { /// Account authorized to create and invalidate this order and whose @@ -64,16 +124,9 @@ pub struct OrderIntent { /// The order cannot be executed after expiration. pub valid_to: u32, - /// Whether `sell_amount` or `buy_amount` is the exact figure; the - /// other side is treated as the limit (minimum to receive for `Sell`, - /// maximum to spend for `Buy`). - pub kind: OrderKind, - - /// If `true`, the order may be filled across multiple settlements; - /// proceeds and consumption scale proportionally with the amount of - /// the sell side that's been used. If `false`, a single settlement - /// must consume the full sell amount (fill-or-kill). - pub partially_fillable: bool, + /// The settings the encoding packs bit by bit into a single byte; see + /// [`Flags`]. + pub flags: Flags, /// Opaque 32 bytes set by the order creator. Not interpreted by the /// settlement program; used off-chain for metadata such as the @@ -99,10 +152,6 @@ pub struct OrderIntent { /// 117 /// ``` /// -/// The flags byte packs `kind` and the intent's booleans, one bit each, from -/// the most significant defined bit down: `partially_fillable`, then `kind` -/// ([`OrderKind::Sell`] = 0, [`OrderKind::Buy`] = 1). Every other bit is -/// reserved and must be zero. #[derive(Clone, Debug, Deref, Eq, PartialEq)] pub struct EncodedOrderIntent([u8; Self::SIZE]); @@ -117,13 +166,6 @@ impl EncodedOrderIntent { const WIDTH_FLAGS: usize = size_of::(); const WIDTH_APP_DATA: usize = size_of::<[u8; 32]>(); - // The bits of the flags byte, from the most significant defined bit down: - // one per boolean field of `OrderIntent`, plus `kind`. - const FLAG_PARTIALLY_FILLABLE: u8 = 1 << 1; - const FLAG_KIND: u8 = 1 << 0; - - const FLAGS_MASK: u8 = Self::FLAG_PARTIALLY_FILLABLE | Self::FLAG_KIND; - pub const SIZE: usize = 149; /// Canonical hash of the bytes. @@ -150,15 +192,6 @@ pub fn hash_bytes(bytes: &[u8; EncodedOrderIntent::SIZE]) -> Hash { solana_sha256_hasher::hashv(&[bytes.as_slice()]) } -/// The intent's `kind` and booleans packed into their canonical flags byte. -/// Reserved bits are left clear. -fn flags_byte(intent: &OrderIntent) -> u8 { - // `wrapping_neg` transforms a true `bool` (effectively 1) into 0xffffffff - let mask = |value: u8| value.wrapping_neg(); - EncodedOrderIntent::FLAG_PARTIALLY_FILLABLE & mask(intent.partially_fillable as u8) - | EncodedOrderIntent::FLAG_KIND & mask(intent.kind as u8) -} - impl From<&EncodedOrderIntent> for [u8; EncodedOrderIntent::SIZE] { fn from(encoded: &EncodedOrderIntent) -> Self { encoded.0 @@ -187,7 +220,7 @@ impl From<&OrderIntent> for EncodedOrderIntent { *sell_amount = intent.sell_amount.to_le_bytes(); *buy_amount = intent.buy_amount.to_le_bytes(); *valid_to = intent.valid_to.to_le_bytes(); - *flags = [flags_byte(intent)]; + *flags = intent.flags.into(); *app_data = intent.app_data; Self(out) } @@ -216,14 +249,6 @@ impl TryFrom<&[u8; EncodedOrderIntent::SIZE]> for OrderIntent { EncodedOrderIntent::WIDTH_APP_DATA ]; - let [flags] = *flags; - // A reserved bit carries no meaning to this version of the program, so - // accepting it would give the same intent several encodings, and with - // them several UIDs. - if flags & !EncodedOrderIntent::FLAGS_MASK != 0 { - return Err(ProgramError::InvalidInstructionData); - } - Ok(OrderIntent { owner: Pubkey::new_from_array(*owner), buy_token_account: Pubkey::new_from_array(*buy_token), @@ -231,12 +256,7 @@ impl TryFrom<&[u8; EncodedOrderIntent::SIZE]> for OrderIntent { sell_amount: u64::from_le_bytes(*sell_amount), buy_amount: u64::from_le_bytes(*buy_amount), valid_to: u32::from_le_bytes(*valid_to), - kind: if flags & EncodedOrderIntent::FLAG_KIND == 0 { - OrderKind::Sell - } else { - OrderKind::Buy - }, - partially_fillable: flags & EncodedOrderIntent::FLAG_PARTIALLY_FILLABLE != 0, + flags: Flags::try_from(*flags)?, app_data: *app_data, }) } @@ -263,7 +283,7 @@ impl OrderIntent { pub mod fixtures { use proptest::{prelude::*, strategy::Union}; - use super::{flags_byte, EncodedOrderIntent, OrderIntent, OrderKind, Pubkey}; + use super::{Flags, OrderIntent, OrderKind, Pubkey}; /// Every valid [`OrderKind`]. pub const ALL_ORDER_KINDS: [OrderKind; 2] = [OrderKind::Sell, OrderKind::Buy]; @@ -279,8 +299,10 @@ pub mod fixtures { sell_amount: 0x0123_4567_89ab_cdef, buy_amount: 0xfedc_ba98_7654_3210, valid_to: 0xdead_beef, - kind, - partially_fillable, + flags: Flags { + kind, + partially_fillable, + }, app_data: [0x44; 32], } } @@ -290,21 +312,18 @@ pub mod fixtures { Union::new(ALL_ORDER_KINDS.map(Just)) } - /// The canonical flags byte for the given `kind` and booleans, as the - /// encoder writes it. Lets a test pin the flags slot of otherwise arbitrary - /// bytes. - pub fn intent_flags_byte(partially_fillable: bool, kind: OrderKind) -> u8 { - flags_byte(&OrderIntent { - partially_fillable, + /// Any valid [`Flags`]. + pub fn arb_flags() -> impl Strategy { + (arb_order_kind(), any::()).prop_map(|(kind, partially_fillable)| Flags { kind, - ..Default::default() + partially_fillable, }) } - /// Any flags byte the decoder accepts. + /// Any flags byte the decoder accepts. Lets a test pin the flags slot of + /// otherwise arbitrary bytes. pub fn arb_flags_byte() -> impl Strategy { - (any::(), arb_order_kind()) - .prop_map(|(partially_fillable, kind)| intent_flags_byte(partially_fillable, kind)) + arb_flags().prop_map(|flags| <[u8; 1]>::from(flags)[0]) } /// Any flags byte the decoder rejects: one carrying at least one bit @@ -312,11 +331,11 @@ pub mod fixtures { pub fn arb_invalid_flags_byte() -> impl Strategy { ( any::().prop_filter("at least one reserved bit must be set", |reserved| { - reserved & !EncodedOrderIntent::FLAGS_MASK != 0 + reserved & !Flags::DEFINED != 0 }), arb_flags_byte(), ) - .prop_map(|(reserved, defined)| (reserved & !EncodedOrderIntent::FLAGS_MASK) | defined) + .prop_map(|(reserved, defined)| (reserved & !Flags::DEFINED) | defined) } /// Any valid [`OrderIntent`]. @@ -328,12 +347,11 @@ pub mod fixtures { any::(), any::(), any::(), - arb_order_kind(), - any::(), + arb_flags(), any::<[u8; 32]>(), ) .prop_map( - |(owner, buy_tok, sell_tok, sell_amount, buy_amount, valid_to, kind, pf, app)| { + |(owner, buy_tok, sell_tok, sell_amount, buy_amount, valid_to, flags, app)| { OrderIntent { owner: Pubkey::new_from_array(owner), buy_token_account: Pubkey::new_from_array(buy_tok), @@ -341,8 +359,7 @@ pub mod fixtures { sell_amount, buy_amount, valid_to, - kind, - partially_fillable: pf, + flags, app_data: app, } }, @@ -409,32 +426,31 @@ mod tests { #[test] fn every_flag_owns_a_distinct_bit() { - let flags_of = |intent: &OrderIntent| EncodedOrderIntent::from(intent)[FLAGS_OFFSET]; - let cleared = OrderIntent { - partially_fillable: false, + let byte = |flags: Flags| <[u8; 1]>::from(flags)[0]; + let cleared = Flags { kind: OrderKind::Sell, - ..sample_intent(OrderKind::Sell, false) + partially_fillable: false, }; - assert_eq!(flags_of(&cleared), 0); + assert_eq!(byte(cleared), 0); let set_one_by_one = [ ( - EncodedOrderIntent::FLAG_PARTIALLY_FILLABLE, - OrderIntent { + Flags::PARTIALLY_FILLABLE, + Flags { partially_fillable: true, - ..cleared.clone() + ..cleared }, ), ( - EncodedOrderIntent::FLAG_KIND, - OrderIntent { + Flags::KIND, + Flags { kind: OrderKind::Buy, - ..cleared.clone() + ..cleared }, ), ]; let mut seen = 0u8; - for (bit, intent) in set_one_by_one { + for (bit, flags) in set_one_by_one { assert_eq!(bit.count_ones(), 1, "a flag must occupy a single bit"); assert_eq!(seen & bit, 0, "two flags must not share a bit"); assert!( @@ -442,9 +458,9 @@ mod tests { "each flag must be less significant than the ones before it" ); seen |= bit; - assert_eq!(flags_of(&intent), bit); + assert_eq!(byte(flags), bit); } - assert_eq!(seen, EncodedOrderIntent::FLAGS_MASK); + assert_eq!(seen, Flags::DEFINED); } #[test] @@ -499,7 +515,7 @@ mod tests { for flags in u8::MIN..=u8::MAX { bytes[FLAGS_OFFSET] = flags; let decoded = EncodedOrderIntent::decode_and_hash(&bytes); - if flags & !EncodedOrderIntent::FLAGS_MASK != 0 { + if flags & !Flags::DEFINED != 0 { assert_eq!( decoded.err(), Some(ProgramError::InvalidInstructionData), @@ -508,12 +524,12 @@ mod tests { } else { let (intent, _uid) = decoded.expect("defined flag bits must decode"); assert_eq!( - intent.partially_fillable, - flags & EncodedOrderIntent::FLAG_PARTIALLY_FILLABLE != 0, + intent.flags.partially_fillable, + flags & Flags::PARTIALLY_FILLABLE != 0, ); assert_eq!( - intent.kind, - if flags & EncodedOrderIntent::FLAG_KIND == 0 { + intent.flags.kind, + if flags & Flags::KIND == 0 { OrderKind::Sell } else { OrderKind::Buy diff --git a/programs/settlement/src/create_order.rs b/programs/settlement/src/create_order.rs index 2ee64c88..b7d6c592 100644 --- a/programs/settlement/src/create_order.rs +++ b/programs/settlement/src/create_order.rs @@ -72,7 +72,7 @@ pub fn process_create_order( #[cfg(test)] mod tests { - use cow_settlement_interface::data::intent::{OrderIntent, OrderKind}; + use cow_settlement_interface::data::intent::{Flags, OrderIntent, OrderKind}; use cow_settlement_interface::instruction::create_order::fixtures::{ default_order_data, valid_intent_bytes, DEFAULT_OWNER, NUM_ACCOUNTS, }; @@ -107,11 +107,17 @@ mod tests { fn process_create_order_rejects_invalid_encoded_intent() { let intent: OrderIntent = (&valid_intent_bytes()).try_into().expect("should be valid"); let intent_bytes_buy = EncodedOrderIntent::from(&OrderIntent { - kind: OrderKind::Buy, + flags: Flags { + kind: OrderKind::Buy, + ..intent.flags + }, ..intent }); let intent_bytes_sell = EncodedOrderIntent::from(&OrderIntent { - kind: OrderKind::Sell, + flags: Flags { + kind: OrderKind::Sell, + ..intent.flags + }, ..intent }); fn first_differing_byte(lhs: &[u8], rhs: &[u8]) -> Option { diff --git a/programs/settlement/src/settle/begin.rs b/programs/settlement/src/settle/begin.rs index e9aad481..f744aab6 100644 --- a/programs/settlement/src/settle/begin.rs +++ b/programs/settlement/src/settle/begin.rs @@ -358,11 +358,11 @@ fn validated_final_amounts( .checked_add(amount_out) .ok_or(SettlementError::AmountReceivedOverflow)?; - let (filled, order_amount) = match intent.kind { + let (filled, order_amount) = match intent.flags.kind { OrderKind::Sell => (amount_withdrawn, intent.sell_amount), OrderKind::Buy => (amount_received, intent.buy_amount), }; - if filled != order_amount && !intent.partially_fillable { + if filled != order_amount && !intent.flags.partially_fillable { return Err(SettlementError::OrderNotExactlyFilled); } else if filled > order_amount { return Err(SettlementError::FillExceedsOrderAmount); diff --git a/programs/settlement/tests/common/order.rs b/programs/settlement/tests/common/order.rs index 0b2235eb..ed2f7f4b 100644 --- a/programs/settlement/tests/common/order.rs +++ b/programs/settlement/tests/common/order.rs @@ -1,6 +1,8 @@ //! On-chain order construction shared by the settlement integration tests. -use cow_settlement_client::cow_settlement_interface::data::intent::{OrderIntent, OrderKind}; +use cow_settlement_client::cow_settlement_interface::data::intent::{ + Flags, OrderIntent, OrderKind, +}; use cow_settlement_client::instructions::CreateOrder; use litesvm::LiteSVM; use solana_sdk::{ @@ -21,8 +23,10 @@ pub fn sample_intent(owner: Pubkey, sell_token_account: Pubkey, salt: u8) -> Ord sell_amount: 1_000_000, buy_amount: 2_000_000, valid_to: 0xdead_beef, - kind: OrderKind::Sell, - partially_fillable: true, + flags: Flags { + kind: OrderKind::Sell, + partially_fillable: true, + }, app_data: [salt; 32], } } @@ -102,13 +106,13 @@ impl<'a> OrderBuilder<'a> { /// Set the order's kind (`Sell` or `Buy`). Defaults to `Sell`. pub fn kind(mut self, kind: OrderKind) -> Self { - self.intent.kind = kind; + self.intent.flags.kind = kind; self } /// Set whether the order may be filled partially. Defaults to `true`. pub fn partially_fillable(mut self, partially_fillable: bool) -> Self { - self.intent.partially_fillable = partially_fillable; + self.intent.flags.partially_fillable = partially_fillable; self } diff --git a/test-cli/src/cmd/create_order.rs b/test-cli/src/cmd/create_order.rs index 13d79bcc..9190020f 100644 --- a/test-cli/src/cmd/create_order.rs +++ b/test-cli/src/cmd/create_order.rs @@ -2,7 +2,7 @@ use anyhow::Context as _; use clap::{Args as ClapArgs, Parser}; use cow_settlement_client::{ cow_settlement_interface::{ - data::intent::{OrderIntent, OrderKind}, + data::intent::{Flags, OrderIntent, OrderKind}, pda::order::find_order_pda, }, instructions::CreateOrder, @@ -162,8 +162,10 @@ fn execute(ctx: Context, parsed: ParsedOrder, common: CommonArgs) -> anyhow::Res sell_amount, buy_amount, valid_to: common.valid_to, - kind, - partially_fillable: common.partially_fillable, + flags: Flags { + kind, + partially_fillable: common.partially_fillable, + }, app_data: [0u8; 32], }; From 130b2c9e68a0f5c920710f87dc2b055520fad96e Mon Sep 17 00:00:00 2001 From: Kaze <230549489+kaze-cow@users.noreply.github.com> Date: Wed, 26 Aug 2026 17:53:04 +0900 Subject: [PATCH 04/11] simplify comment --- interface/src/data/intent.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/data/intent.rs b/interface/src/data/intent.rs index b1423e52..eb3f53dc 100644 --- a/interface/src/data/intent.rs +++ b/interface/src/data/intent.rs @@ -46,8 +46,7 @@ pub struct Flags { } impl Flags { - // The bit each field occupies, from the most significant defined bit - // down: one per boolean field, plus `kind`. + // The bit each field occupies const PARTIALLY_FILLABLE: u8 = 1 << 1; const KIND: u8 = 1 << 0; From a188c8089f6abbfe2025be6c849570cd82b503f5 Mon Sep 17 00:00:00 2001 From: Kaze <230549489+kaze-cow@users.noreply.github.com> Date: Wed, 26 Aug 2026 18:02:43 +0900 Subject: [PATCH 05/11] use clearer flag encoding --- interface/src/data/intent.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/interface/src/data/intent.rs b/interface/src/data/intent.rs index eb3f53dc..e32c5ea6 100644 --- a/interface/src/data/intent.rs +++ b/interface/src/data/intent.rs @@ -57,12 +57,14 @@ impl Flags { impl From for [u8; 1] { /// The canonical flags byte. Reserved bits are left clear. fn from(flags: Flags) -> Self { - // `wrapping_neg` transforms a true `bool` (effectively 1) into 0xff - let mask = |value: u8| value.wrapping_neg(); - [ - Flags::PARTIALLY_FILLABLE & mask(flags.partially_fillable as u8) - | Flags::KIND & mask(flags.kind as u8), - ] + let mut byte = 0; + if flags.partially_fillable { + byte |= Flags::PARTIALLY_FILLABLE; + } + if flags.kind == OrderKind::Buy { + byte |= Flags::KIND; + } + [byte] } } From ef01a19f12783db380e4e70f46300a25cc00e8d5 Mon Sep 17 00:00:00 2001 From: Kaze <230549489+kaze-cow@users.noreply.github.com> Date: Wed, 26 Aug 2026 18:13:02 +0900 Subject: [PATCH 06/11] update `sample_intent` usage should accept flags to allow for covering the edge cases --- client/src/parse.rs | 4 +- interface/src/data/intent.rs | 54 ++++++++++++++-------- interface/src/data/order.rs | 14 ++---- interface/src/instruction/create_order.rs | 6 +-- programs/settlement/src/settle/begin.rs | 6 ++- programs/settlement/tests/create_order.rs | 4 +- programs/settlement/tests/reclaim_order.rs | 4 +- 7 files changed, 53 insertions(+), 39 deletions(-) diff --git a/client/src/parse.rs b/client/src/parse.rs index 42171860..d923e00f 100644 --- a/client/src/parse.rs +++ b/client/src/parse.rs @@ -71,7 +71,7 @@ mod tests { BeginSettle, CreateBuffers, CreateOrder, FinalizeSettle, Initialize, InitializedIntent, }; use cow_settlement_interface::{ - data::intent::{fixtures::sample_intent, OrderKind}, + data::intent::fixtures::sample_intent, fixtures::pubkey_from_seed, instruction::{ fixtures::fake_account_from_array, reclaim_buffer::ReclaimBuffer, @@ -85,7 +85,7 @@ mod tests { fn build(instruction: SettlementInstruction) -> Instruction { let program_id = pubkey_from_seed("program id"); let payer = pubkey_from_seed("payer"); - let intent = sample_intent(OrderKind::Sell, false); + let intent = sample_intent(Default::default()); match instruction { SettlementInstruction::Initialize => Initialize { program_id, diff --git a/interface/src/data/intent.rs b/interface/src/data/intent.rs index e32c5ea6..eec1db0c 100644 --- a/interface/src/data/intent.rs +++ b/interface/src/data/intent.rs @@ -292,7 +292,7 @@ pub mod fixtures { // Hardcoded but verified in a sanity-check test. pub const FLAGS_OFFSET: usize = 116; - pub fn sample_intent(kind: OrderKind, partially_fillable: bool) -> OrderIntent { + pub fn sample_intent(flags: Flags) -> OrderIntent { OrderIntent { owner: Pubkey::new_from_array([0x11; 32]), buy_token_account: Pubkey::new_from_array([0x22; 32]), @@ -300,10 +300,7 @@ pub mod fixtures { sell_amount: 0x0123_4567_89ab_cdef, buy_amount: 0xfedc_ba98_7654_3210, valid_to: 0xdead_beef, - flags: Flags { - kind, - partially_fillable, - }, + flags, app_data: [0x44; 32], } } @@ -377,11 +374,14 @@ mod tests { // Every shape an `OrderIntent` can take on its validated axes: the `kind` // enum and the `partially_fillable` flag bit. - fn all_intent_shapes() -> impl Iterator { + fn all_flag_shapes() -> impl Iterator { fixtures::ALL_ORDER_KINDS.into_iter().flat_map(|kind| { - [false, true] - .into_iter() - .map(move |partially_fillable| sample_intent(kind, partially_fillable)) + [false, true].into_iter().map(move |partially_fillable| { + sample_intent(Flags { + kind, + partially_fillable, + }) + }) }) } @@ -394,7 +394,7 @@ mod tests { // Any `OrderIntent` works: `size_of_val` only consults the field // type, never the data. - let intent = sample_intent(OrderKind::Sell, false); + let intent = sample_intent(Default::default()); assert_eq!(EncodedOrderIntent::WIDTH_OWNER, size_of_val(&intent.owner)); assert_eq!( @@ -466,7 +466,7 @@ mod tests { #[test] fn roundtrip_all_kind_and_flag_combinations() { - for intent in all_intent_shapes() { + for intent in all_flag_shapes() { let encoded = EncodedOrderIntent::from(&intent); let (decoded, _uid) = EncodedOrderIntent::decode_and_hash(&encoded).expect("example must decode"); @@ -480,7 +480,7 @@ mod tests { // encode/decode, this test fails. #[test] fn decode_and_hash_uid_matches_encoded_hash() { - for intent in all_intent_shapes() { + for intent in all_flag_shapes() { let encoded = EncodedOrderIntent::from(&intent); let (_intent, uid) = EncodedOrderIntent::decode_and_hash(&encoded).expect("example must decode"); @@ -493,9 +493,21 @@ mod tests { fn first_differing_byte(lhs: &[u8], rhs: &[u8]) -> Option { lhs.iter().zip(rhs).position(|(l, r)| l != r) } - let sell_false: EncodedOrderIntent = (&sample_intent(OrderKind::Sell, false)).into(); - let sell_true: EncodedOrderIntent = (&sample_intent(OrderKind::Sell, true)).into(); - let buy_true: EncodedOrderIntent = (&sample_intent(OrderKind::Buy, true)).into(); + let sell_false: EncodedOrderIntent = (&sample_intent(Flags { + kind: OrderKind::Sell, + partially_fillable: false, + })) + .into(); + let sell_true: EncodedOrderIntent = (&sample_intent(Flags { + kind: OrderKind::Sell, + partially_fillable: true, + })) + .into(); + let buy_true: EncodedOrderIntent = (&sample_intent(Flags { + kind: OrderKind::Buy, + partially_fillable: true, + })) + .into(); assert_eq!( first_differing_byte(sell_false.as_slice(), sell_true.as_slice()) @@ -511,7 +523,7 @@ mod tests { #[test] fn decode_accepts_defined_flag_bits_only() { - let encoded = EncodedOrderIntent::from(&sample_intent(OrderKind::Sell, false)); + let encoded = EncodedOrderIntent::from(&sample_intent(Default::default())); let mut bytes: [u8; EncodedOrderIntent::SIZE] = *encoded; for flags in u8::MIN..=u8::MAX { bytes[FLAGS_OFFSET] = flags; @@ -542,14 +554,20 @@ mod tests { #[test] fn uid_digest_regression() { - let intent = sample_intent(OrderKind::Buy, true); + let intent = sample_intent(Flags { + kind: OrderKind::Buy, + partially_fillable: true, + }); let expected = hex!("d2a82e919ec3d5e8b21c512cf14251e98bf79cdf01f0a2bdd0ecbed3007a9761"); assert_eq!(intent.uid(), Hash::from(expected)); } #[test] fn encoding_regression() { - let encoded = EncodedOrderIntent::from(&sample_intent(OrderKind::Buy, true)); + let encoded = EncodedOrderIntent::from(&sample_intent(Flags { + kind: OrderKind::Buy, + partially_fillable: true, + })); let encoding: [u8; EncodedOrderIntent::SIZE] = *encoded; #[rustfmt::skip] let expected: [u8; EncodedOrderIntent::SIZE] = [ diff --git a/interface/src/data/order.rs b/interface/src/data/order.rs index 20d53066..f94fc90f 100644 --- a/interface/src/data/order.rs +++ b/interface/src/data/order.rs @@ -263,10 +263,7 @@ pub mod fixtures { use proptest::prelude::*; use super::{OrderAccount, Pubkey}; - use crate::data::intent::{ - fixtures::{arb_order_intent, sample_intent}, - OrderKind, - }; + use crate::data::intent::fixtures::{arb_order_intent, sample_intent}; // Hardcoded but verified in a sanity-check test. pub const DISCRIMINATOR_OFFSET: usize = 0; @@ -281,7 +278,7 @@ pub mod fixtures { amount_withdrawn: 0x0112_2334_4556_6778, amount_received: 0x899a_abbc_cdde_eff0, created_by: Pubkey::new_from_array([0x43; 32]), - intent: sample_intent(OrderKind::Sell, false), + intent: sample_intent(Default::default()), } } @@ -316,10 +313,7 @@ mod tests { use super::fixtures::{sample_account, CANCELLED_OFFSET, DISCRIMINATOR_OFFSET, INTENT_OFFSET}; use super::*; - use crate::data::intent::{ - fixtures::{sample_intent, FLAGS_OFFSET}, - OrderKind, - }; + use crate::data::intent::fixtures::{sample_intent, FLAGS_OFFSET}; // Pin each width to the size of the `OrderAccount` field it encodes. The // widths summing to `SIZE` is enforced separately, at compile time, by the @@ -566,7 +560,7 @@ mod tests { let cancelled = true; let amount_withdrawn = 1337; let amount_received = 31337; - let intent = sample_intent(OrderKind::Sell, false); + let intent = sample_intent(Default::default()); let created_by = Pubkey::new_from_array([0x42u8; 32]); let mut buffer = [0u8; EncodedOrderAccount::SIZE]; diff --git a/interface/src/instruction/create_order.rs b/interface/src/instruction/create_order.rs index 1708ecc6..1b49af7d 100644 --- a/interface/src/instruction/create_order.rs +++ b/interface/src/instruction/create_order.rs @@ -115,9 +115,7 @@ pub mod fixtures { use solana_address::Address; use super::{CreateOrder, Instruction}; - use crate::data::intent::{ - fixtures::sample_intent, EncodedOrderIntent, OrderIntent, OrderKind, - }; + use crate::data::intent::{fixtures::sample_intent, EncodedOrderIntent, OrderIntent}; /// Owner baked into [`valid_intent_bytes`]' sample intent. pub const DEFAULT_OWNER: Address = Address::new_from_array([0x11; 32]); @@ -131,7 +129,7 @@ pub mod fixtures { pub fn valid_intent_bytes() -> [u8; EncodedOrderIntent::SIZE] { (&EncodedOrderIntent::from(&OrderIntent { owner: DEFAULT_OWNER, - ..sample_intent(OrderKind::Sell, true) + ..sample_intent(Default::default()) })) .into() } diff --git a/programs/settlement/src/settle/begin.rs b/programs/settlement/src/settle/begin.rs index f744aab6..4736de43 100644 --- a/programs/settlement/src/settle/begin.rs +++ b/programs/settlement/src/settle/begin.rs @@ -375,6 +375,7 @@ fn validated_final_amounts( mod tests { use super::*; use cow_settlement_interface::data::intent::fixtures::{arb_order_intent, sample_intent}; + use cow_settlement_interface::data::intent::Flags; use cow_settlement_interface::instruction::fixtures::fake_account; use cow_settlement_interface::instruction::settle::fixtures::arb_pushes; use cow_settlement_interface::instruction::settle::{FinalizeSettle, FinalizeSettleInput}; @@ -402,7 +403,10 @@ mod tests { OrderIntent { sell_amount: self.sell, buy_amount: self.buy, - ..sample_intent(self.kind, self.partially_fillable) + ..sample_intent(Flags { + kind: self.kind, + partially_fillable: self.partially_fillable, + }) } } } diff --git a/programs/settlement/tests/create_order.rs b/programs/settlement/tests/create_order.rs index 001b4b9b..ba5196d1 100644 --- a/programs/settlement/tests/create_order.rs +++ b/programs/settlement/tests/create_order.rs @@ -1,6 +1,6 @@ use cow_settlement_client::cow_settlement_interface::{ data::{ - intent::{fixtures, EncodedOrderIntent, OrderIntent, OrderKind}, + intent::{fixtures, EncodedOrderIntent, OrderIntent}, order::{EncodedOrderAccount, OrderAccount}, }, instruction::create_order::CreateOrder, @@ -24,7 +24,7 @@ mod common; fn sample_intent(owner: Pubkey) -> OrderIntent { OrderIntent { owner, - ..fixtures::sample_intent(OrderKind::Sell, true) + ..fixtures::sample_intent(Default::default()) } } diff --git a/programs/settlement/tests/reclaim_order.rs b/programs/settlement/tests/reclaim_order.rs index d641ac2f..2d4c3474 100644 --- a/programs/settlement/tests/reclaim_order.rs +++ b/programs/settlement/tests/reclaim_order.rs @@ -1,5 +1,5 @@ use cow_settlement_client::cow_settlement_interface::{ - data::intent::{fixtures::sample_intent, EncodedOrderIntent, OrderIntent, OrderKind}, + data::intent::{fixtures::sample_intent, EncodedOrderIntent, OrderIntent}, instruction::{create_order::CreateOrder, reclaim_order::ReclaimOrder}, pda::order::find_order_pda, SettlementError, @@ -23,7 +23,7 @@ fn reclaim_sample_intent(owner: Pubkey) -> OrderIntent { OrderIntent { owner, valid_to: VALID_TO, - ..sample_intent(OrderKind::Sell, true) + ..sample_intent(Default::default()) } } From 8a33f2e0874202fe3298e4fa34b69325dd76603c Mon Sep 17 00:00:00 2001 From: Kaze <230549489+kaze-cow@users.noreply.github.com> Date: Wed, 26 Aug 2026 18:26:45 +0900 Subject: [PATCH 07/11] try to simplify the arb_flags* methods --- interface/src/data/intent.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/interface/src/data/intent.rs b/interface/src/data/intent.rs index eec1db0c..5fd0c935 100644 --- a/interface/src/data/intent.rs +++ b/interface/src/data/intent.rs @@ -318,22 +318,16 @@ pub mod fixtures { }) } - /// Any flags byte the decoder accepts. Lets a test pin the flags slot of - /// otherwise arbitrary bytes. + /// Any flags byte the decoder accepts. pub fn arb_flags_byte() -> impl Strategy { - arb_flags().prop_map(|flags| <[u8; 1]>::from(flags)[0]) + any::().prop_map(|byte| byte & Flags::DEFINED) } - /// Any flags byte the decoder rejects: one carrying at least one bit - /// outside those the encoding defines. + /// Any flags byte the decoder rejects. pub fn arb_invalid_flags_byte() -> impl Strategy { - ( - any::().prop_filter("at least one reserved bit must be set", |reserved| { - reserved & !Flags::DEFINED != 0 - }), - arb_flags_byte(), - ) - .prop_map(|(reserved, defined)| (reserved & !Flags::DEFINED) | defined) + any::().prop_filter("must have at least one bit that is undefined", |byte| { + byte & !Flags::DEFINED > 0 + }) } /// Any valid [`OrderIntent`]. From 2b00821dad659b6526eaee0542d64e1c79b46b7e Mon Sep 17 00:00:00 2001 From: Kaze <230549489+kaze-cow@users.noreply.github.com> Date: Wed, 26 Aug 2026 18:31:43 +0900 Subject: [PATCH 08/11] add a test for flags width --- interface/src/data/intent.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/src/data/intent.rs b/interface/src/data/intent.rs index 5fd0c935..c8b1377b 100644 --- a/interface/src/data/intent.rs +++ b/interface/src/data/intent.rs @@ -411,6 +411,11 @@ mod tests { EncodedOrderIntent::WIDTH_VALID_TO, size_of_val(&intent.valid_to) ); + assert_eq!( + EncodedOrderIntent::WIDTH_FLAGS, + // in truth if there was a problem here it would actually cause a compilation error + size_of_val::<[u8; 1]>(&Flags::default().into()) + ); assert_eq!( EncodedOrderIntent::WIDTH_APP_DATA, size_of_val(&intent.app_data) From 3d35eec75e3ffc9cb05ec59b9333f6036837b786 Mon Sep 17 00:00:00 2001 From: Kaze <230549489+kaze-cow@users.noreply.github.com> Date: Wed, 26 Aug 2026 18:37:23 +0900 Subject: [PATCH 09/11] remove unnecessary else condition --- interface/src/data/intent.rs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/interface/src/data/intent.rs b/interface/src/data/intent.rs index c8b1377b..17569f09 100644 --- a/interface/src/data/intent.rs +++ b/interface/src/data/intent.rs @@ -533,20 +533,6 @@ mod tests { Some(ProgramError::InvalidInstructionData), "flags {flags:#04x} sets a reserved bit and must be rejected", ); - } else { - let (intent, _uid) = decoded.expect("defined flag bits must decode"); - assert_eq!( - intent.flags.partially_fillable, - flags & Flags::PARTIALLY_FILLABLE != 0, - ); - assert_eq!( - intent.flags.kind, - if flags & Flags::KIND == 0 { - OrderKind::Sell - } else { - OrderKind::Buy - }, - ); } } } From 0cb67be0606d9248561ed258022cad262d375dd1 Mon Sep 17 00:00:00 2001 From: Kaze <230549489+kaze-cow@users.noreply.github.com> Date: Wed, 26 Aug 2026 19:43:57 +0900 Subject: [PATCH 10/11] fix bench report --- bench-report.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/bench-report.json b/bench-report.json index 36e8bc43..10ec4e1a 100644 --- a/bench-report.json +++ b/bench-report.json @@ -27,22 +27,22 @@ "create_buffers/happy_path_creates_initialized_buffer_token_account": 10340, "create_buffers/happy_path_creates_multiple_buffers_in_one_instruction": 21731, "create_buffers/max_buffers_in_one_instruction": 176947, - "create_order/happy_path_creates_order_pda_with_expected_body": 3416, + "create_order/happy_path_creates_order_pda_with_expected_body": 3428, "initialize/happy_path_initializes_state_pda_with_expected_data": 4526, "reclaim_buffer/funded_buffer_is_skipped": 6299, "reclaim_buffer/happy_path_reclaims_empty_buffer_to_the_authority_itself": 7447, "reclaim_buffer/max_buffers_in_one_instruction": 136501, "reclaim_buffer/reclaims_multiple_buffers_skipping_funded": 18043, - "reclaim_order/happy_path_returns_lamports_and_closes_pda": 2125, + "reclaim_order/happy_path_returns_lamports_and_closes_pda": 2135, "settle/finalizes_with_no_pushes": 7043, - "settle/pulls_from_multiple_orders": 19738, - "settle/pulls_funds_to_destination": 13411, - "settle/pulls_to_multiple_destinations": 14558, - "settle/pushes_a_single_order": 12261, - "settle/pushes_several_orders_from_different_buffers": 17439, - "settle/pushes_several_orders_from_one_buffer": 17440, - "settle/settles_a_single_order": 12279, - "settle/settles_multiple_orders": 22661, + "settle/pulls_from_multiple_orders": 19754, + "settle/pulls_funds_to_destination": 13419, + "settle/pulls_to_multiple_destinations": 14566, + "settle/pushes_a_single_order": 12269, + "settle/pushes_several_orders_from_different_buffers": 17455, + "settle/pushes_several_orders_from_one_buffer": 17456, + "settle/settles_a_single_order": 12287, + "settle/settles_multiple_orders": 22685, "transfer_authority/manager_can_transfer_manager": 3170, "transfer_authority/manager_can_transfer_reclaim_authority": 3172, "transfer_authority/reclaim_authority_can_transfer_itself": 3175 From f8f097a3b87a138193f28683ca928af81f5eae75 Mon Sep 17 00:00:00 2001 From: Kaze <230549489+kaze-cow@users.noreply.github.com> Date: Wed, 26 Aug 2026 21:09:28 +0900 Subject: [PATCH 11/11] fix bench --- bench-report.json | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/bench-report.json b/bench-report.json index 870eb053..c4676e9f 100644 --- a/bench-report.json +++ b/bench-report.json @@ -24,28 +24,28 @@ "transfer_authority/reclaim_authority_can_transfer_itself": 4 }, "compute_units": { - "create_buffers/happy_path_creates_initialized_buffer_token_account": 10335, - "create_buffers/happy_path_creates_multiple_buffers_in_one_instruction": 21722, - "create_buffers/max_buffers_in_one_instruction": 176884, - "create_order/happy_path_creates_order_pda_with_expected_body": 4971, - "initialize/happy_path_initializes_state_pda_with_expected_data": 4521, - "reclaim_buffer/funded_buffer_is_skipped": 6290, - "reclaim_buffer/happy_path_reclaims_empty_buffer_to_the_authority_itself": 7441, - "reclaim_buffer/max_buffers_in_one_instruction": 136495, - "reclaim_buffer/reclaims_multiple_buffers_skipping_funded": 18034, - "reclaim_order/happy_path_returns_lamports_and_closes_pda": 2179, - "settle/finalizes_with_no_pushes": 7042, - "settle/pulls_from_multiple_orders": 19872, - "settle/pulls_funds_to_destination": 13489, - "settle/pulls_to_multiple_destinations": 14636, - "settle/pushes_a_single_order": 12337, - "settle/pushes_several_orders_from_different_buffers": 17571, - "settle/pushes_several_orders_from_one_buffer": 17572, - "settle/settles_a_single_order": 12355, - "settle/settles_multiple_orders": 22849, - "transfer_authority/manager_can_transfer_manager": 3164, - "transfer_authority/manager_can_transfer_reclaim_authority": 3166, - "transfer_authority/reclaim_authority_can_transfer_itself": 3169 + "create_buffers/happy_path_creates_initialized_buffer_token_account": 10340, + "create_buffers/happy_path_creates_multiple_buffers_in_one_instruction": 21731, + "create_buffers/max_buffers_in_one_instruction": 176947, + "create_order/happy_path_creates_order_pda_with_expected_body": 4976, + "initialize/happy_path_initializes_state_pda_with_expected_data": 4526, + "reclaim_buffer/funded_buffer_is_skipped": 6299, + "reclaim_buffer/happy_path_reclaims_empty_buffer_to_the_authority_itself": 7447, + "reclaim_buffer/max_buffers_in_one_instruction": 136501, + "reclaim_buffer/reclaims_multiple_buffers_skipping_funded": 18043, + "reclaim_order/happy_path_returns_lamports_and_closes_pda": 2183, + "settle/finalizes_with_no_pushes": 7062, + "settle/pulls_from_multiple_orders": 19921, + "settle/pulls_funds_to_destination": 13524, + "settle/pulls_to_multiple_destinations": 14675, + "settle/pushes_a_single_order": 12366, + "settle/pushes_several_orders_from_different_buffers": 17608, + "settle/pushes_several_orders_from_one_buffer": 17609, + "settle/settles_a_single_order": 12384, + "settle/settles_multiple_orders": 22894, + "transfer_authority/manager_can_transfer_manager": 3170, + "transfer_authority/manager_can_transfer_reclaim_authority": 3172, + "transfer_authority/reclaim_authority_can_transfer_itself": 3175 }, "transaction_bytes": { "create_buffers/happy_path_creates_initialized_buffer_token_account": 303,