Skip to content

feat(wallet): policy- and budget-validated atomic batch execution (issue #90) - #192

Open
merciiiqode wants to merge 4 commits into
ASTROIDX556:mainfrom
merciiiqode:issue90astroid-contract
Open

feat(wallet): policy- and budget-validated atomic batch execution (issue #90)#192
merciiiqode wants to merge 4 commits into
ASTROIDX556:mainfrom
merciiiqode:issue90astroid-contract

Conversation

@merciiiqode

Copy link
Copy Markdown

Closes #90

Summary

Adds batch validation of execution calls to the wallet contract. The existing
raw batch_execute stays for un-gated sub-calls; the new
batch_execute_validated gates every action against the policy and budget
contracts before any value moves, executes the batch atomically, and returns
an aggregated receipt.

What was added

  • BatchAction — a sub-call plus validation metadata: policy_id
    (envelope id, asset/recipient/amount checked against check_transfer),
    budget_id (envelope consumed via consume), asset, recipient and
    amount. An empty id skips that gate for the action.
  • batch_execute_validated — two-phase entry point (wallet, Role::Agent):
    1. validate every action and aggregate its value with checked math in a
      single iteration pass (one budget consumption per envelope, no
      speculative pre-flights → minimized gas), then
    2. execute the sub-calls sequentially.
      Any failure — policy denial, budget overrun, cumulative i128 overflow,
      or a failing sub-call — reverts the entire transaction (atomicity). On
      success it returns a BatchReceipt { executed, total_amount, budget_remaining } and publishes ("wallet", "batch_validated").
  • set_policy / set_budget (contract admin) wire the gate contracts,
    mirroring the treasury convention.
  • BatchReceipt — the aggregated result of the run.

Atomicity

Soroban's runtime rollback guarantees no partial effect: policy/budget
checks and the cumulative-value calculation all happen in phase 1, before any
sub-call fires, so a denied batch moves nothing and does not debit budgets.

Acceptance criteria

  • Batch execution entry point in contracts/wallet
  • Iterate actions verifying policy and budget limits for each sub-call
  • Execute actions sequentially, return aggregated results (BatchReceipt), revert on failure
  • Comprehensive test coverage for successful and failing batch scenarios

Verification

  • cargo test --package astroid-wallet — 56 tests pass (10 new)
  • cargo test --workspace — 342 tests pass
  • cargo clippy --workspace -- -D warnings — clean
  • cargo fmt --all --check — clean

Note: branches back to the accumulated repair/feature work (PRs #189, #190,
#191) because upstream main does not compile; merge those first.

…cel flows

The repo was committed in a broken mid-merge state that did not compile
(duplicated error enum variants beyond the XDR limit, merged functions,
unclosed delimiters, stale helper signatures). Restore it so
`cargo test --workspace`, `cargo fmt` and clippy all pass, and finish
the escrow work this branch was opened for (issue ASTROIDX556#80).

shared:
- rewrite Error enum as exactly 50 #[contracterror] variants; add
  GraceActive / EscrowNotExpired / EscrowAlreadySettled / ReserveViolation
- fix safe-arithmetic test expectations (Overflow for i128::MIN-1; mul
  round-trips below i128::MAX)

escrow (issue ASTROIDX556#80):
- implement expiration deadline handling: post-deadline/grace refunds
  (refund, reclaim, refund_timelock, expire) return ESCROW_EXPIRED and
  graceful errors, plus sender-only clawback cancel with EscrowNotExpired /
  GraceActive / EscrowAlreadySettled transitions into Cancelled
- reconstruct multi-asset escrow (create, timelock, scheduled, milestones,
  override release, vesting/claiming views, structured events)
- reconcile tests to new cancel semantics; fix stale helper signatures

policy:
- un-merge whitelist/blocklist/category functions corrupted by the merge;
  repair check_transfer gates (recipient whitelist, dedupe blacklist checks)
- update register_policy helper to current 9-arg signature

wallet:
- Restore lost batch_execute (atomic sub-calls); sub-calls target external
  contracts (Soroban forbids self re-entry), gate on wallet Active state,
  surface callee errors via try_invoke_contract
- Fix Val (RawVal rename), ContractCall arg encoding, set_reserve_ratio auth

multisig: TooManySigners -> InvalidThreshold; budget/treasury: clippy fixes
…tion status

Issue ASTROIDX556#82 already had the core chaining machinery (prerequisite ids in
storage, acyclicity checks, ensure_dependencies_met enforcing the executed
state before execute). This completes the acceptance criteria that were
still missing:

- Emit structured events whenever a dependency chain is validated:
  ("proposal", "dep_ok") when every prerequisite has executed, and
  ("proposal", "dep_fail") with the id of the first unmet prerequisite
  when execution is blocked.
- Add an is_executed view exposing has_executed() (Executed | Closed) so
  downstream contracts can query a proposal's completion status directly.
- Tests: blocked execution emits dep_fail (no dep_ok), satisfied chains
  emit dep_ok, end-to-end completion via is_executed including a Failed
  proposal never counting as executed.

Closes ASTROIDX556#82
Adds a validated batch path to the wallet alongside the existing raw
batch_execute. Each BatchAction carries the external sub-call plus the
metadata needed to gate it: policy envelope id (asset/recipient/amount are
checked against the wired policy contract) and budget envelope id (consumed
from the wired budget contract). Empty ids skip the corresponding gate.

batch_execute_validated is two-phase:
  1. Validate every action and aggregate its value with checked math in a
     single iteration pass (one budget consumption per envelope, no
     speculative pre-flights).
  2. Execute the sub-calls sequentially.
Any failure — policy denial, budget overrun, cumulative overflow, failing
sub-call — reverts the whole transaction, so validation and execution are
atomic. On success an aggregated BatchReceipt (executed, total_amount,
budget_remaining) is returned and ("wallet", "batch_validated") is
published.

Contract-level set_policy/set_budget (admin) wire the gates, mirroring the
treasury convention. Tests cover success, policy denial rollback, budget
insufficiency rollback, cumulative overflow, unwired-gate refusal,
envelope-less execution, mixed-asset aggregation, and the standard empty /
role / frozen gates.

Closes ASTROIDX556#90
@drips-wave

drips-wave Bot commented Aug 30, 2026

Copy link
Copy Markdown

@merciiiqode Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Cjay-Cyber-2

Copy link
Copy Markdown
Contributor

resolve conflict on this issue

@K1NGD4VID

Copy link
Copy Markdown
Contributor

resolve conflict on this issue @merciiiqode

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: implement batch validation of execution calls in wallet contract

3 participants