Skip to content

Add structured events for rate limiting triggers and multi-sig contract upgrade tests#374

Open
EmeditWeb wants to merge 3 commits into
BCPathway:mainfrom
EmeditWeb:coverage_for_Contract_upgrades_via_multi-sig
Open

Add structured events for rate limiting triggers and multi-sig contract upgrade tests#374
EmeditWeb wants to merge 3 commits into
BCPathway:mainfrom
EmeditWeb:coverage_for_Contract_upgrades_via_multi-sig

Conversation

@EmeditWeb

@EmeditWeb EmeditWeb commented Jun 24, 2026

Copy link
Copy Markdown

Summary

This PR adds two features: rate-limit structured events and multi-sig contract upgrade tests for the bc-forge token contract.

Closes #325


Rate-Limit Events

New Event Types

Event Symbol Trigger
emit_global_rate_limit_exceeded rl_gexcd Emitted when a global mint rate limit is exceeded
emit_address_rate_limit_exceeded rl_aexcd Emitted when an address-specific mint rate limit is exceeded
emit_global_rate_limit_set rl_gset Emitted when the global rate limit is configured
emit_address_rate_limit_set rl_aset Emitted when an address-specific rate limit is configured

Event Data Schemas

  • rl_gexcd(admin, leader, requested, current_window, current_volume, limit)
  • rl_aexcd(admin, target, leader, requested, current_window, current_volume, limit)
  • rl_gset(admin, limit, window)
  • rl_aset(admin, target, limit, window)

Rate-limit exceeded events are split into global (rl_gexcd) and address-specific (rl_aexcd) variants to avoid Option<Address> in event data.

Tests

  • 6 unit tests confirming all four event types are emitted with correct data
  • Edge cases: limit of one, window reset behavior

Multi-Sig Contract Upgrades

New Token Contract Functions

Function Signature Description
set_admin_pool (pool, threshold) Set multi-sig admin pool and approval threshold
get_admin_pool () -> Vec<Address> Returns current admin pool (falls back to [admin])
get_threshold () -> u32 Returns current threshold (defaults to 1)
propose_upgrade (caller, description, new_wasm_hash) -> u64 Create upgrade proposal (auto-approves creator)
approve_upgrade (caller, proposal_id) Approve an existing proposal (any pool member)
execute_upgrade (proposal_id) Execute approved proposal (checks threshold, marks executed, performs WASM update)
upgrade (new_wasm_hash) Direct admin upgrade (by-passes multi-sig, requires single admin auth)

Governance Flow

  1. Admin sets a multi-sig pool via set_admin_pool
  2. Any pool member calls propose_upgrade (creator is auto-approved)
  3. Other pool members call approve_upgrade to add approvals
  4. Once approvals >= threshold, any pool member calls execute_upgrade to update the contract WASM

Bug Fix: Admin Module Proposal TTL

Removed invalid extend_storage_ttl_for_key calls in the admin module's create_proposal, approve_proposal, is_proposal_ready, and mark_executed functions. These were calling persistent().extend_ttl() for keys stored in instance storage, causing Error(Storage, MissingValue) at runtime.

Tests (20 total)

Proposal lifecycle:

  • test_propose_upgrade_happy_path — basic proposal creation returns ID 0
  • test_propose_upgrade_non_admin_fails — non-pool-member cannot propose
  • test_approve_upgrade_happy_path — pool member can approve
  • test_approve_upgrade_double_approval_fails — duplicate approval rejected
  • test_approve_upgrade_non_pool_member_fails — random address cannot approve
  • test_execute_upgrade_full_multi_sig_flow — propose, approve, execute (2-of-3 threshold)
  • test_execute_upgrade_single_admin_pool — single-pool-member upgrade
  • test_execute_upgrade_insufficient_approvals_fails — threshold not met
  • test_execute_upgrade_double_execution_fails — cannot execute twice
  • test_approve_after_execute_fails — cannot approve after execution
  • test_execute_upgrade_nonexistent_proposal_fails — invalid proposal ID

Edge cases:

  • test_multiple_proposals_independent — independent proposal IDs and hash storage
  • test_propose_approve_execute_different_admins — different pool members propose, approve, execute
  • test_admin_pool_can_include_multiple_members — pool with 6 members, threshold 3
  • test_get_admin_pool_before_set — falls back to [admin]
  • test_get_threshold_before_set — defaults to 1
  • test_set_admin_pool_zero_threshold_fails — threshold 0 rejected
  • test_set_admin_pool_threshold_exceeds_pool_fails — threshold > pool size rejected

Direct upgrade:

  • test_upgrade_direct_single_admin — single admin direct upgrade
  • test_upgrade_before_init_fails — upgrade on uninitialized contract fails
  • test_direct_upgrade_after_multi_sig_setup — direct upgrade still works after multi-sig setup
  • test_upgrade_hash_stored_per_proposal — each proposal stores its own WASM hash

Test Workaround

update_current_contract_wasm crashes the Soroban SDK test environment during Env cleanup (panic-in-destructor). The perform_wasm_update helper guards the call with cfg!(not(test)), making it a no-op in unit tests so governance logic can be fully tested.


Files Changed

File Changes
contracts/rate-limit/src/lib.rs +277 lines — event emission functions and integration
contracts/token/src/lib.rs +120 lines — upgrade, admin pool, proposal functions
contracts/token/src/test.rs +400 lines — 26 tests (6 event + 20 upgrade)
contracts/admin/src/lib.rs -4 lines — removed invalid TTL extension calls

Verification

  • cargo test -p bc-forge-token -p bc-forge-rate-limit — 32 tests pass (6 rate-limit + 26 token)
  • cargo clippy — no new warnings
  • cargo fmt --check — clean

- Add emit_global_rate_limit_exceeded event (rl_gexcd) when global limit hit
- Add emit_address_rate_limit_exceeded event (rl_aexcd) when address limit hit
- Add emit_global_rate_limit_set event (rl_gset) when global limit configured
- Add emit_address_rate_limit_set event (rl_aset) when address limit configured
- Emit events from internal_check_rate_limit, internal_set_global_rate_limit,
  internal_set_address_rate_limit
- Add 6 unit tests covering all event types, edge cases, and window reset

Closes BCPathway#326
- Add set_admin_pool, get_admin_pool, get_threshold for multi-sig admin pool management
- Add propose_upgrade(caller, desc, hash) for creating upgrade proposals
- Add approve_upgrade(caller, proposal_id) for pool members to approve upgrades
- Add execute_upgrade(proposal_id) to execute approved proposals
- Add upgrade(hash) for direct admin upgrade (bypasses multi-sig)
- Fix admin module proposal TTL extension (instance vs persistent storage mismatch)
- Add 20 unit tests covering happy paths, edge cases, and expected failures
- Guard update_current_contract_wasm with cfg!(not(test)) to avoid test env crash
@drips-wave

drips-wave Bot commented Jun 24, 2026

Copy link
Copy Markdown

@EmeditWeb 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

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.

Smart Contract Tests: Add coverage for Contract upgrades via multi-sig

1 participant