@@ -20,11 +20,13 @@ use fixed_math::math;
2020use sui::{bcs, hash::blake2b256, table::{Self , Table }};
2121
2222const EInsufficientPayoutTerms : u64 = 0 ;
23+ const EMaxPayoutTreeNodes : u64 = 1 ;
2324
2425/// Sparse payout-liability tree keyed by finite strike tick.
2526public struct StrikePayoutTree has store {
2627 root: Option <u64 >,
2728 nodes: Table <u64 , PayoutNode >,
29+ node_count: u64 ,
2830 base: PayoutTerms ,
2931}
3032
@@ -138,6 +140,7 @@ public(package) fun new(ctx: &mut TxContext): StrikePayoutTree {
138140 StrikePayoutTree {
139141 root: option::none (),
140142 nodes: table::new (ctx),
143+ node_count: 0 ,
141144 base: payout_terms (0 , 0 ),
142145 }
143146}
@@ -150,12 +153,27 @@ public(package) fun insert_range(
150153 quantity: u64 ,
151154 floor_shares: u64 ,
152155) {
153- tree.apply_range (
154- lower_tick,
155- higher_tick,
156- payout_terms (quantity, floor_shares),
157- true ,
156+ let terms = payout_terms (quantity, floor_shares);
157+ if (terms.quantity == 0 && terms.floor_shares == 0 ) return ;
158+
159+ let mut new_nodes = 0 ;
160+ if (lower_tick != 0 && !tree.nodes.contains (lower_tick)) {
161+ new_nodes = new_nodes + 1 ;
162+ };
163+ if (
164+ higher_tick != constants::pos_inf_tick !()
165+ && higher_tick != lower_tick
166+ && !tree.nodes.contains (higher_tick)
167+ ) {
168+ new_nodes = new_nodes + 1 ;
169+ };
170+
171+ assert ! (
172+ tree.node_count + new_nodes <= constants::max_payout_tree_nodes !(),
173+ EMaxPayoutTreeNodes ,
158174 );
175+
176+ tree.apply_range (lower_tick, higher_tick, terms, true );
159177}
160178
161179/// Remove interval payout terms for the order tick range `(lower_tick, higher_tick]`.
@@ -181,7 +199,12 @@ public(package) fun debug_contains_node(tree: &StrikePayoutTree, tick: u64): boo
181199
182200#[test_only]
183201public (package ) fun debug_node_count (tree: &StrikePayoutTree ): u64 {
184- debug_node_count_subtree (&tree.nodes, tree.root)
202+ tree.node_count
203+ }
204+
205+ #[test_only]
206+ public (package ) fun debug_set_node_count (tree: &mut StrikePayoutTree , node_count: u64 ) {
207+ tree.node_count = node_count;
185208}
186209
187210fun apply_range (
@@ -212,6 +235,7 @@ fun apply_boundary_delta(
212235 is_start: bool ,
213236 add: bool ,
214237) {
238+ let had_node = tree.nodes.contains (tick);
215239 let new_root = apply_at (
216240 &mut tree.nodes,
217241 tree.root,
@@ -221,6 +245,13 @@ fun apply_boundary_delta(
221245 add,
222246 );
223247 tree.root = new_root;
248+
249+ let has_node = tree.nodes.contains (tick);
250+ if (!had_node && has_node) {
251+ tree.node_count = tree.node_count + 1 ;
252+ } else if (had_node && !has_node) {
253+ tree.node_count = tree.node_count - 1 ;
254+ };
224255}
225256
226257fun apply_at (
@@ -548,16 +579,6 @@ fun is_empty_node(node: PayoutNode): bool {
548579 is_zero_terms (node.local_start) && is_zero_terms (node.local_end)
549580}
550581
551- #[test_only]
552- fun debug_node_count_subtree (nodes: &Table <u64 , PayoutNode >, root: Option <u64 >): u64 {
553- if (root.is_none ()) return 0 ;
554- let tick = *root.borrow ();
555- let node = nodes[tick];
556- 1
557- + debug_node_count_subtree (nodes, node.left)
558- + debug_node_count_subtree (nodes, node.right)
559- }
560-
561582fun apply_terms_delta (value: &mut PayoutTerms , delta: PayoutTerms , add: bool ) {
562583 if (add) {
563584 value.quantity = value.quantity + delta.quantity;
0 commit comments