Skip to content

Commit ddfc04e

Browse files
committed
fix(router-multicall): resolve #903, #904, #905, #906
#903: remove duplicated/contradictory doc block on admin() — replace the double-entry (one claiming Panics, one claiming Errors) with a single clean doc comment that accurately reflects the Result return. #904: extend module-level Features list to mention simulate (dry-run), fail_fast (abort on first optional failure), and max_total_gas (pre- flight cumulative budget cap) — all core execute_batch behaviours that were documented on the function but omitted from the crate overview. #905: extract the 'budget_exceeded' and 'invoke_failed' string literals into named constants FAILURE_REASON_BUDGET_EXCEEDED and FAILURE_REASON_INVOKE_FAILED in router-common. Replace all inline occurrences in execute_batch and the budget_failure_count test helper so there is a single source of truth. #906: build CallResult once per iteration. Store the value to ledger before passing it to record_success/record_failure rather than constructing an identical second struct literal for the store_results path. CallResult already derives Clone; the second construction was an unintentional copy-paste duplicate.
1 parent 21a5207 commit ddfc04e

2 files changed

Lines changed: 29 additions & 21 deletions

File tree

contracts/router-common/src/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,20 @@ pub const EVENT_ROLE_ADMIN_SET: &str = "role_admin_set";
224224
/// Standard event topic for an address being un-blacklisted
225225
pub const EVENT_ADDRESS_UNBLACKLISTED: &str = "address_unblacklisted";
226226

227+
/// Failure reason string used when a call fails and an `instruction_budget` was set.
228+
///
229+
/// Passed as the payload of [`BatchItemError::Custom`] to distinguish budget-related
230+
/// failures from generic invocation failures. Use this constant instead of the raw
231+
/// string literal so that production code and tests share a single source of truth.
232+
pub const FAILURE_REASON_BUDGET_EXCEEDED: &str = "budget_exceeded";
233+
234+
/// Failure reason string used when a call fails and no `instruction_budget` was set.
235+
///
236+
/// Passed as the payload of [`BatchItemError::Custom`] for generic invocation failures.
237+
/// Use this constant instead of the raw string literal so that production code and
238+
/// tests share a single source of truth.
239+
pub const FAILURE_REASON_INVOKE_FAILED: &str = "invoke_failed";
240+
227241
/// Standard event topic for a role grant (pending or direct)
228242
pub const EVENT_ROLE_GRANT: &str = "role_grant";
229243

contracts/router-multicall/src/lib.rs

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
//! - Per-call success/failure tracking (non-atomic mode)
1111
//! - Atomic mode: revert all if any call fails
1212
//! - Call result storage for async inspection
13+
//! - `simulate` — dry-run mode: execute all calls without incrementing the batch counter
14+
//! - `fail_fast` — abort the batch on the first optional-call failure
15+
//! - `max_total_gas` — pre-flight cumulative instruction-budget cap to reject over-budget batches before execution
1316
//!
1417
//! ## Events (following naming convention: past tense verbs in snake_case)
1518
//! - `call_result` — Individual call result logged (caller, target, function, success)
@@ -278,34 +281,30 @@ impl RouterMulticall {
278281
success,
279282
};
280283

284+
if store_results && !simulate {
285+
env.storage().instance().set(
286+
&DataKey::BatchResult(batch_id, call_index),
287+
&call_result,
288+
);
289+
}
290+
281291
if success {
282292
result.record_success(call_index, call_result);
283293
} else {
284294
let failure_error = if call.instruction_budget.is_some() {
285295
router_common::BatchItemError::Custom(soroban_sdk::String::from_str(
286296
&env,
287-
"budget_exceeded",
297+
router_common::FAILURE_REASON_BUDGET_EXCEEDED,
288298
))
289299
} else {
290300
router_common::BatchItemError::Custom(soroban_sdk::String::from_str(
291301
&env,
292-
"invoke_failed",
302+
router_common::FAILURE_REASON_INVOKE_FAILED,
293303
))
294304
};
295305
result.record_failure(call_index, failure_error);
296306
}
297307

298-
if store_results && !simulate {
299-
env.storage().instance().set(
300-
&DataKey::BatchResult(batch_id, call_index),
301-
&router_common::CallResult {
302-
target: call.target.clone(),
303-
function: call.function.clone(),
304-
success,
305-
},
306-
);
307-
}
308-
309308
env.events().publish(
310309
(Symbol::new(&env, router_common::EVENT_CALL_RESULT),),
311310
(&caller, &call.target, &call.function, success, call_index),
@@ -437,21 +436,16 @@ impl RouterMulticall {
437436
.ok_or(MulticallError::NotInitialized)
438437
}
439438

440-
/// Get current admin.
439+
/// Get the current admin address.
441440
///
442441
/// # Arguments
443442
/// * `env` - The Soroban environment.
444443
///
445444
/// # Returns
446445
/// The [`Address`] of the current admin.
447446
///
448-
/// # Panics
449-
/// * Panics if the contract has not been initialized.
450-
///
451-
/// Get the current admin address.
452-
///
453447
/// # Errors
454-
/// Returns `MulticallError::NotInitialized` if the contract has not been initialized.
448+
/// * [`MulticallError::NotInitialized`] — if the contract has not been initialized.
455449
pub fn admin(env: Env) -> Result<Address, MulticallError> {
456450
env.storage()
457451
.instance()
@@ -603,7 +597,7 @@ mod tests {
603597
}
604598

605599
fn budget_failure_count(env: &Env, result: &router_common::BatchCallResult) -> u32 {
606-
let budget_msg = soroban_sdk::String::from_str(env, "budget_exceeded");
600+
let budget_msg = soroban_sdk::String::from_str(env, router_common::FAILURE_REASON_BUDGET_EXCEEDED);
607601
let mut count = 0u32;
608602
for i in 0..result.failures.len() {
609603
let failure = result.failures.get(i).unwrap();

0 commit comments

Comments
 (0)