Skip to content

fix(globe-wallet): disambiguate spend limits by full AssetInfo and enforce UserAssets registration (#82) - #108

Draft
s6pa1rta3n-lab wants to merge 1 commit into
Orbit-Wal:mainfrom
s6pa1rta3n-lab:fix-issue-82
Draft

fix(globe-wallet): disambiguate spend limits by full AssetInfo and enforce UserAssets registration (#82)#108
s6pa1rta3n-lab wants to merge 1 commit into
Orbit-Wal:mainfrom
s6pa1rta3n-lab:fix-issue-82

Conversation

@s6pa1rta3n-lab

Copy link
Copy Markdown

Summary

Resolves #82. Disambiguates spend limits and daily spent tracking in globe-wallet by scoping storage keys (DataKey::SpendLimit and DataKey::DailySpent) by full asset identity (AssetInfo containing both code and issuer) rather than plain String asset code. Also enforces that set_spend_limit and record_spend require the target asset to already be registered in the caller's UserAssets list (returning WalletError::AssetNotFound if missing).

Architectural Note: This fix lives strictly within contracts/globe-wallet/src/lib.rs because GlobeWallet owns user asset registration and spend limit accounting; token-wrapper was not touched by mistake this time.


Key Changes

  1. Full Asset Identity in Storage Keys:
    • Updated DataKey::SpendLimit(Address, AssetInfo) and DataKey::DailySpent(Address, AssetInfo).
    • Provided helper functions assets_match and find_user_asset to ensure case-insensitive code comparison with strict issuer matching.
  2. Registration Enforcement & Authorization:
    • set_spend_limit(env, user, asset, limit): Authenticates user, verifies asset exists in caller's UserAssets, validates against existing daily spend, updates DataKey::SpendLimit(user, asset), and removes any legacy key.
    • get_spend_limit(env, user, asset) -> i128: Resolves canonical asset info, reads DataKey::SpendLimit, and falls back to legacy (Symbol("SpendLimit"), user, code) if present.
    • record_spend(env, user, asset, amount): Validates positive amount, verifies asset exists in UserAssets, checks daily limit, records spent under DataKey::DailySpent(user, asset), extends TTL, and removes legacy keys.
    • send(env, user, token_id, asset, to, amount): Updated to accept AssetInfo.
  3. Migration & Backward Compatibility:
    • Added migrate_spend_limits(env, user) -> Result<u32, WalletError> to batch-migrate all legacy (Symbol, Address, String) keys for registered assets of a user to new AssetInfo keys.
    • Added fallback reads and lazy cleanup during runtime operations.
  4. Comprehensive Test Suite:
    • Added test_same_code_different_issuer_independent_spend_limits in globe-wallet to prove two same-code, different-issuer assets maintain independent daily budgets.
    • Added test_spend_limit_and_record_spend_reject_unregistered_asset.
    • Added test_case_variant_asset_shares_same_spend_limit_bucket.
    • Added test_migrate_spend_limits_and_legacy_fallback.
    • Updated all existing unit and reentrancy tests to use AssetInfo.

Verification: cargo test --workspace

running 90 tests
test tests::test_add_asset_overlong_code_fails ... ok
test tests::test_add_asset_empty_code_fails ... ok
test tests::test_accept_admin_emits_only_admin_transferred_not_recovery_completed ... ok
test tests::test_accept_by_wrong_address_fails ... ok
test tests::test_add_asset_case_variant_duplicate_fails ... ok
test tests::test_add_and_list_guardians ... ok
test tests::test_add_and_get_assets ... ok
test tests::test_add_allowed_token_requires_admin ... ok
test tests::test_add_duplicate_guardian_fails ... ok
test tests::test_add_duplicate_asset_fails ... ok
test tests::test_cancel_admin_transfer ... ok
test tests::test_cannot_initiate_second_recovery_while_one_pending ... ok
test tests::test_double_approval_rejected ... ok
test tests::test_daily_spent_survives_temporary_ttl_eviction ... ok
test tests::test_initialize ... ok
test tests::test_execute_recovery_emits_both_admin_transferred_and_recovery_completed ... ok
test tests::test_admin_can_cancel_recovery_even_after_quorum ... ok
test tests::test_case_variant_asset_shares_same_spend_limit_bucket ... ok
test tests::test_execute_recovery_rejects_new_admin_same_as_current_admin ... ok
test tests::test_execute_recovery_emits_recovery_completed_with_full_over_quorum_guardian_set ... ok
test tests::test_migrate_user_assets_within_limit_does_nothing ... ok
test tests::test_initialize_twice_fails ... ok
test tests::test_migrate_user_assets_requires_admin ... ok
test tests::test_native_code_with_issuer_is_contradictory_and_rejected ... ok
test tests::test_execute_upgrade_with_never_uploaded_hash_traps - should panic ... ok
test tests::test_non_native_code_without_issuer_is_underspecified_and_rejected ... ok
test tests::test_non_admin_cannot_add_guardian ... ok
test tests::test_no_limit_allows_any_spend ... ok
test tests::test_propose_upgrade_accepts_any_hash_without_validation ... ok
test tests::test_pending_admin_cleared_after_accept_admin ... ok
test tests::test_propose_upgrade_rejects_delay_below_minimum ... ok
test tests::test_propose_upgrade_accepts_delay_at_minimum - should panic ... ok
test tests::test_propose_and_execute_upgrade - should panic ... ok
test tests::test_migrate_spend_limits_and_legacy_fallback ... ok
test tests::test_propose_upgrade_rejects_zero_delay ... ok
test tests::test_propose_upgrade_requires_admin ... ok
test tests::test_propose_without_accept_keeps_admin_unchanged ... ok
test tests::test_max_guardians_limit ... ok
test tests::test_record_spend_boundary_drift_awareness ... ok
test tests::test_record_spend_boundary_last_second_of_day_accumulates ... ok
test tests::test_record_spend_boundary_first_second_of_new_day_resets ... ok
test tests::test_record_spend_bucket_is_integer_division ... ok
test tests::test_raise_spend_then_lower_limit ... ok
test tests::test_record_spend_exceeds_limit_fails ... ok
test tests::test_record_spend_negative_amount_fails ... ok
test tests::test_record_spend_exact_day_boundary ... ok
test tests::test_migrate_user_assets_trims_excess ... ok
test tests::test_record_spend_negative_amount_cannot_bypass_daily_limit ... ok
test tests::test_record_spend_overflow_does_not_poison_later_calls ... ok
test tests::test_record_spend_zero_amount_fails ... ok
test tests::test_record_spend_within_limit ... ok
test tests::test_record_spend_rejected_negative_amount_does_not_mutate_state ... ok
test tests::test_recovery_rejects_non_guardian ... ok
test tests::test_recovery_clears_any_in_flight_normal_admin_transfer ... ok
test tests::test_recovery_happy_path_2_of_3 ... ok
test tests::test_remove_asset ... ok
test tests::test_remove_allowed_token ... ok
test tests::test_remove_nonexistent_asset_fails ... ok
test tests::test_require_admin_not_initialized ... ok
test tests::test_remove_guardian_below_threshold_fails ... ok
test tests::test_revoke_recovery_approval_rejects_non_guardian ... ok
test tests::test_removed_guardian_cannot_initiate_recovery ... ok
test tests::test_remove_guardian_dequorated_proposal_can_requorum_with_fresh_timelock ... ok
test tests::test_remove_guardian_who_never_approved_leaves_proposal_untouched ... ok
test tests::test_removed_guardian_approval_no_longer_counts_toward_quorum ... ok
test tests::test_revoke_recovery_approval_rejects_removed_guardian ... ok
test tests::test_revoking_approval_below_threshold_resets_timelock ... ok
test tests::test_send_rejects_disallowed_token ... ok
test tests::test_send_over_daily_limit_fails_and_moves_no_tokens ... ok
test tests::test_send_rejects_when_token_wrapper_not_set ... ok
test tests::test_same_code_different_issuer_independent_spend_limits ... ok
test tests::test_set_recovery_config_accepts_delay_at_minimum_and_recovery_executes ... ok
test tests::test_send_happy_path_moves_tokens_and_records_spend ... ok
test tests::test_max_assets_limit ... ok
test tests::test_set_recovery_config_rejected_while_recovery_pending ... ok
test tests::test_set_recovery_config_rejects_delay_below_minimum ... ok
test tests::test_set_recovery_config_rejects_single_guardian_threshold ... ok
test tests::test_set_recovery_config_rejects_threshold_above_guardian_count ... ok
test tests::test_set_token_wrapper_requires_admin ... ok
test tests::test_set_recovery_config_rejects_zero_delay ... ok
test tests::test_send_rejects_reentrant_malicious_token ... ok
test tests::test_set_recovery_config_requires_min_guardians ... ok
test tests::test_spend_limit_and_record_spend_reject_unregistered_asset ... ok
test tests::test_spend_limit_set_and_get ... ok
test tests::test_upgrade_propose_double_fails ... ok
test tests::test_transfer_admin ... ok
test tests::test_spend_limit_ttl_extension_after_long_idle_period ... ok
test tests::test_upgrade_rejects_hash_mismatch ... ok
test tests::test_upgrade_requires_admin_and_ready_time ... ok
test tests::test_user_assets_ttl_extension_after_long_idle_period ... ok

test result: ok. 90 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.94s

     Running tests/record_spend_reentrancy.rs (target/debug/deps/record_spend_reentrancy-46ca4c83e14a7031)

running 1 test
test two_spends_in_one_host_invocation_accumulate ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s

     Running unittests src/lib.rs (target/debug/deps/token_wrapper-cb6a9fabefc08ee9)

running 11 tests
test tests::test_allowance_unset_pair_returns_zero ... ok
test tests::test_approve_past_expiry_fails ... ok
test tests::test_approve_negative_amount_fails ... ok
test tests::test_approve_and_allowance ... ok
test tests::test_transfer_from_expired_allowance_fails ... ok
test tests::test_transfer_from_insufficient_allowance_fails ... ok
test tests::test_approve_overwrites_previous_allowance ... ok
test tests::test_transfer_from_happy_path ... ok
test tests::test_transfer_from_zero_amount_fails ... ok
test tests::test_transfer_from_succeeds_exactly_at_expiry_ledger ... ok
test tests::test_transfer_from_rolls_back_allowance_when_underlying_transfer_fails ... ok

test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.35s

Payout Routing

  • EVM (Base/Arbitrum/Polygon/ETH): 0xF46C9F6d70C50BF81ef3588AB523a90a594a2F89
  • Stellar: GCL6OXAMLD75BMTINA6EMRUDWK5THQUSHMYNLSNBCJAPZJHNYJTUNIBC

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.

[Bug]: Issue #8's fix landed in the wrong contract — the SpendLimit/DailySpent code/issuer collision it describes is still live on main

1 participant