Skip to content

Commit 2b60a18

Browse files
committed
sdk: harden gas limit heuristics
1 parent 55b6518 commit 2b60a18

4 files changed

Lines changed: 126 additions & 14 deletions

File tree

sdk/rust/CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Changelog
22

3+
## v0.2.10 (2026-04-05)
4+
5+
### Changed
6+
- Replaced naive fixed gas allocation in order submission with deterministic contract-aware heuristics:
7+
- `placeOrders`: `550k + 175k * (n - 1)`
8+
- `replaceOrders`: `300k + 120k * cancels + 180k * places`
9+
- `cancelOrders`: `120k + 70k * count`
10+
- `cancelOrder`: `250k`
11+
- Added SDK logging for `gas_limit`, `gas_used`, and gas utilization percentage on order submission/confirmation paths.
12+
313
## v0.2.9 (2026-04-04)
414

515
### Fixed

sdk/rust/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/rust/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "strike-sdk"
3-
version = "0.2.9"
3+
version = "0.2.10"
44
edition = "2021"
55
description = "Rust SDK for Strike prediction markets on BNB Chain"
66
license = "MIT"

sdk/rust/src/chain/orders.rs

Lines changed: 114 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,16 @@ impl<'a> OrdersClient<'a> {
6464
.abi_encode();
6565

6666
let order_count = params.len();
67+
let gas_limit = gas_limit_place_orders(order_count);
6768
let mut tx = TransactionRequest::default()
6869
.to(self.config.addresses.order_book)
6970
.input(Bytes::from(calldata).into());
70-
tx.gas = Some(350_000 * order_count as u64);
71+
tx.gas = Some(gas_limit);
7172

7273
let pending = send_tx(self.provider, &self.nonce_sender, tx).await?;
7374

7475
let tx_hash = *pending.tx_hash();
75-
info!(orderbook_market_id, order_count, tx = %tx_hash, "placeOrders tx sent");
76+
info!(orderbook_market_id, order_count, gas_limit, tx = %tx_hash, "placeOrders tx sent");
7677

7778
let receipt = pending
7879
.get_receipt()
@@ -87,7 +88,15 @@ impl<'a> OrdersClient<'a> {
8788
}
8889

8990
let placed = parse_placed_orders(&receipt, orderbook_market_id);
90-
info!(orderbook_market_id, tx = %tx_hash, gas_used = receipt.gas_used, placed = placed.len(), "placeOrders confirmed");
91+
info!(
92+
orderbook_market_id,
93+
tx = %tx_hash,
94+
gas_limit,
95+
gas_used = receipt.gas_used,
96+
gas_utilization_pct = %format_gas_utilization_pct(receipt.gas_used, gas_limit),
97+
placed = placed.len(),
98+
"placeOrders confirmed"
99+
);
91100

92101
Ok(placed)
93102
}
@@ -126,16 +135,23 @@ impl<'a> OrdersClient<'a> {
126135
}
127136
.abi_encode();
128137

129-
let total_ops = cancel_ids.len() + params.len();
138+
let gas_limit = gas_limit_replace_orders(cancel_ids.len(), params.len());
130139
let mut tx = TransactionRequest::default()
131140
.to(self.config.addresses.order_book)
132141
.input(Bytes::from(calldata).into());
133-
tx.gas = Some(350_000 * total_ops as u64);
142+
tx.gas = Some(gas_limit);
134143

135144
let pending = send_tx(self.provider, &self.nonce_sender, tx).await?;
136145

137146
let tx_hash = *pending.tx_hash();
138-
info!(orderbook_market_id, cancels = cancel_ids.len(), places = params.len(), tx = %tx_hash, "replaceOrders tx sent");
147+
info!(
148+
orderbook_market_id,
149+
cancels = cancel_ids.len(),
150+
places = params.len(),
151+
gas_limit,
152+
tx = %tx_hash,
153+
"replaceOrders tx sent"
154+
);
139155

140156
let receipt = pending
141157
.get_receipt()
@@ -150,7 +166,16 @@ impl<'a> OrdersClient<'a> {
150166
}
151167

152168
let placed = parse_placed_orders(&receipt, orderbook_market_id);
153-
info!(orderbook_market_id, tx = %tx_hash, gas_used = receipt.gas_used, cancelled = cancel_ids.len(), placed = placed.len(), "replaceOrders confirmed");
169+
info!(
170+
orderbook_market_id,
171+
tx = %tx_hash,
172+
gas_limit,
173+
gas_used = receipt.gas_used,
174+
gas_utilization_pct = %format_gas_utilization_pct(receipt.gas_used, gas_limit),
175+
cancelled = cancel_ids.len(),
176+
placed = placed.len(),
177+
"replaceOrders confirmed"
178+
);
154179

155180
Ok(placed)
156181
}
@@ -184,22 +209,30 @@ impl<'a> OrdersClient<'a> {
184209
}
185210
.abi_encode();
186211

212+
let gas_limit = gas_limit_cancel_orders(order_ids.len());
187213
let mut tx = TransactionRequest::default()
188214
.to(self.config.addresses.order_book)
189215
.input(Bytes::from(calldata).into());
190-
tx.gas = Some(100_000 * order_ids.len() as u64);
216+
tx.gas = Some(gas_limit);
191217

192218
let pending = send_tx(self.provider, &self.nonce_sender, tx).await?;
193219

194220
let tx_hash = *pending.tx_hash();
195-
info!(count = order_ids.len(), tx = %tx_hash, "cancelOrders tx sent");
221+
info!(count = order_ids.len(), gas_limit, tx = %tx_hash, "cancelOrders tx sent");
196222

197223
let receipt = pending
198224
.get_receipt()
199225
.await
200226
.map_err(|e| StrikeError::Contract(e.to_string()))?;
201227

202-
info!(tx = %tx_hash, gas_used = receipt.gas_used, count = order_ids.len(), "cancelOrders confirmed");
228+
info!(
229+
tx = %tx_hash,
230+
gas_limit,
231+
gas_used = receipt.gas_used,
232+
gas_utilization_pct = %format_gas_utilization_pct(receipt.gas_used, gas_limit),
233+
count = order_ids.len(),
234+
"cancelOrders confirmed"
235+
);
203236
Ok(())
204237
}
205238

@@ -208,24 +241,57 @@ impl<'a> OrdersClient<'a> {
208241
self.require_wallet()?;
209242

210243
let calldata = OrderBook::cancelOrderCall { orderId: order_id }.abi_encode();
244+
let gas_limit = gas_limit_cancel_order();
211245
let mut tx = TransactionRequest::default()
212246
.to(self.config.addresses.order_book)
213247
.input(Bytes::from(calldata).into());
214-
tx.gas = Some(200_000);
248+
tx.gas = Some(gas_limit);
215249

216250
let pending = send_tx(self.provider, &self.nonce_sender, tx).await?;
217251

218252
let tx_hash = *pending.tx_hash();
253+
info!(order_id = %order_id, gas_limit, tx = %tx_hash, "cancelOrder tx sent");
219254
let receipt = pending
220255
.get_receipt()
221256
.await
222257
.map_err(|e| StrikeError::Contract(e.to_string()))?;
223258

224-
info!(order_id = %order_id, tx = %tx_hash, gas_used = receipt.gas_used, "cancelOrder confirmed");
259+
info!(
260+
order_id = %order_id,
261+
tx = %tx_hash,
262+
gas_limit,
263+
gas_used = receipt.gas_used,
264+
gas_utilization_pct = %format_gas_utilization_pct(receipt.gas_used, gas_limit),
265+
"cancelOrder confirmed"
266+
);
225267
Ok(())
226268
}
227269
}
228270

271+
fn gas_limit_place_orders(order_count: usize) -> u64 {
272+
550_000 + 175_000 * (order_count.saturating_sub(1) as u64)
273+
}
274+
275+
fn gas_limit_replace_orders(cancel_count: usize, place_count: usize) -> u64 {
276+
300_000 + 120_000 * cancel_count as u64 + 180_000 * place_count as u64
277+
}
278+
279+
fn gas_limit_cancel_orders(order_count: usize) -> u64 {
280+
120_000 + 70_000 * order_count as u64
281+
}
282+
283+
fn gas_limit_cancel_order() -> u64 {
284+
250_000
285+
}
286+
287+
fn format_gas_utilization_pct(gas_used: u64, gas_limit: u64) -> String {
288+
if gas_limit == 0 {
289+
return "0.0".to_string();
290+
}
291+
292+
format!("{:.1}", gas_used as f64 / gas_limit as f64 * 100.0)
293+
}
294+
229295
/// Parse `OrderPlaced` and `OrderResting` events from a transaction receipt.
230296
/// Resting orders are placed far from the clearing price and emit `OrderResting`
231297
/// instead of `OrderPlaced`, but they're still live orders that need tracking.
@@ -255,3 +321,39 @@ fn parse_placed_orders(
255321
}
256322
placed
257323
}
324+
325+
#[cfg(test)]
326+
mod tests {
327+
use super::{
328+
gas_limit_cancel_order, gas_limit_cancel_orders, gas_limit_place_orders,
329+
gas_limit_replace_orders,
330+
};
331+
332+
#[test]
333+
fn gas_limit_place_orders_formula() {
334+
assert_eq!(gas_limit_place_orders(0), 550_000);
335+
assert_eq!(gas_limit_place_orders(1), 550_000);
336+
assert_eq!(gas_limit_place_orders(2), 725_000);
337+
assert_eq!(gas_limit_place_orders(3), 900_000);
338+
}
339+
340+
#[test]
341+
fn gas_limit_replace_orders_formula() {
342+
assert_eq!(gas_limit_replace_orders(0, 0), 300_000);
343+
assert_eq!(gas_limit_replace_orders(1, 0), 420_000);
344+
assert_eq!(gas_limit_replace_orders(0, 1), 480_000);
345+
assert_eq!(gas_limit_replace_orders(2, 3), 1_080_000);
346+
}
347+
348+
#[test]
349+
fn gas_limit_cancel_orders_formula() {
350+
assert_eq!(gas_limit_cancel_orders(0), 120_000);
351+
assert_eq!(gas_limit_cancel_orders(1), 190_000);
352+
assert_eq!(gas_limit_cancel_orders(3), 330_000);
353+
}
354+
355+
#[test]
356+
fn gas_limit_cancel_order_formula() {
357+
assert_eq!(gas_limit_cancel_order(), 250_000);
358+
}
359+
}

0 commit comments

Comments
 (0)