Skip to content

Conversation

@ALPAC-4
Copy link
Collaborator

@ALPAC-4 ALPAC-4 commented Jan 27, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling for deregistered bridges in reward vesting, preventing incorrect reward calculations and strengthening stage/claim validations.
  • New Features

    • Added a public capability to update bridge addresses and improved snapshot handling to support deregistered bridges.
  • Tests

    • Expanded tests for deregistered-bridge flows, added expected-failure annotations, and stronger balance/claim assertions.
  • Chores

    • Updated development configuration addresses.

@coderabbitai
Copy link

coderabbitai bot commented Jan 27, 2026

Walkthrough

Refactors snapshot loading to copy semantics, adds deregistered-bridge handling and stricter stage validation, exposes new constants and an update_bridge_address entry, adjusts reward/vesting flows to handle deregistered state, updates tests, and changes development addresses in Move.toml.

Changes

Cohort / File(s) Summary
Configuration
Move.toml
Updated dev-addresses: vip 0x2 → 0x3, vesting 0x3 → 0x4.
Tests
sources/test.move
Renamed fail_calim_deregistered_bridge_rewardcalim_deregistered_bridge_reward; added #[expected_failure(abort_code = 0xD0007)] to test_submit_snapshot_and_fund_reward_with_deregistered_bridge; extended claim test to fund additional stages and assert final balance.
Vesting module
sources/vesting.move
Added U64_MAX constant; normalize l2_score to 0 when initial reward is zero before emitting vesting events; added assertion ensuring value.start_stage < claim_info.start_stage in previous-vestings batch claim.
Core VIP module
sources/vip.move
Added EZERO_STAGE and U64_MAX constants; made Snapshot copyable; replaced load_snapshot_imut with load_snapshot_copy and updated call sites; introduced deregistered-bridge mock snapshot path; extended get_user_funded_reward_internal signature with is_deregistered: bool; added update_bridge_address entry; tightened stage/claim validations and propagated deregistered state through reward/vesting flows and tests.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant VIP as vip::module
    participant ModuleStore
    participant Bridge
    participant Vesting

    Client->>VIP: submit_claim(request with bridge_id, stage)
    VIP->>ModuleStore: load_snapshot_copy(stage, bridge_id, version)
    ModuleStore-->>VIP: Snapshot or mock (if deregistered)
    VIP->>Bridge: query bridge registration/state
    Bridge-->>VIP: registered? / last_version
    alt bridge deregistered
        VIP->>Vesting: compute vesting with is_deregistered = true
        Vesting-->>VIP: vesting events (UserVestingCreate/Finalized)
    else bridge active
        VIP->>Vesting: compute vesting with is_deregistered = false
        Vesting-->>VIP: vesting events and fund transfers
    end
    VIP-->>Client: claim result / emitted events
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through snapshots, made copies anew,
Deregistered bridges found paths that are true,
Constants tucked in a cozy nest,
Stages now ordered, claims put to test,
Hooray — I nibble bugs and bake logic stew! 🍃

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Feat/delisted vesting' partially relates to the changeset. While vesting-related changes are prominent (new constants, event normalization, stage validation in vesting.move), the primary changes across the PR involve deregistered bridge handling, snapshot loading refactoring, reward calculations, and a new bridge address update function in vip.move. The title uses 'delisted' instead of 'deregistered', which is technically imprecise terminology. Consider a more descriptive title like 'Support deregistered bridge vesting claims' or 'Handle vesting for deregistered bridges' to better capture the broader scope of changes involving bridge deregistration logic.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/delisted-vesting

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
sources/vip.move (2)

1499-1517: Move the zero-stage guard before claimability checks.
check_user_reward_claimable can abort with ESTAGE_DATA_NOT_FOUND for stage 0, so the new EZERO_STAGE is unlikely to surface. Validating start_stage != 0 first also avoids unnecessary snapshot lookups.

🛠️ Proposed change
-        check_user_reward_claimable(
-            module_store,
-            account,
-            bridge_id,
-            version,
-            start_stage,
-            end_stage
-        );
-        let vesting_period = module_store.vesting_period;
-        let minimum_score_ratio = module_store.minimum_score_ratio;
-
-        add_tvl_snapshot_internal(module_store);
-
-        // ensure start stage is not 0
-        assert!(start_stage != 0, error::invalid_argument(EZERO_STAGE));
+        // ensure start stage is not 0
+        assert!(start_stage != 0, error::invalid_argument(EZERO_STAGE));
+        check_user_reward_claimable(
+            module_store,
+            account,
+            bridge_id,
+            version,
+            start_stage,
+            end_stage
+        );
+        let vesting_period = module_store.vesting_period;
+        let minimum_score_ratio = module_store.minimum_score_ratio;
+
+        add_tvl_snapshot_internal(module_store);

1532-1557: Skip merkle-proof verification for mock snapshots.
When a deregistered bridge returns a mock snapshot (empty merkle_root), a non‑zero l2_score will always fail proof validation before you override it to U64_MAX. Gating the proof check on !is_deregistered makes the API more resilient to caller input.

🛠️ Proposed change
-                // check merkle proof
-                let target_hash =
-                    score_hash(
-                        bridge_id,
-                        *stage,
-                        account_addr,
-                        *l2_score,
-                        snapshot.total_l2_score
-                    );
-                if (*l2_score != 0) {
-                    assert_merkle_proofs(
-                        *merkle_proof,
-                        snapshot.merkle_root,
-                        target_hash
-                    );
-                };
-
-                // if deregistered and snapshot is not submitted, update score to u64 max to reach minimum score
-                let is_deregistered = snapshot.merkle_root.length() == 0;
+                let is_deregistered = snapshot.merkle_root.length() == 0;
+                if (!is_deregistered) {
+                    let target_hash =
+                        score_hash(
+                            bridge_id,
+                            *stage,
+                            account_addr,
+                            *l2_score,
+                            snapshot.total_l2_score
+                        );
+                    if (*l2_score != 0) {
+                        assert_merkle_proofs(
+                            *merkle_proof,
+                            snapshot.merkle_root,
+                            target_hash
+                        );
+                    };
+                };
+
+                // if deregistered and snapshot is not submitted, update score to u64 max to reach minimum score
                 if (is_deregistered) {
                     l2_score = &U64_MAX
                 };
🧹 Nitpick comments (1)
sources/test.move (1)

1338-1341: Fix the test name typo (calimclaim).
Improves readability and makes test output/search more consistent.

📝 Proposed rename
-    fun calim_deregistered_bridge_reward(
+    fun claim_deregistered_bridge_reward(

beer-1
beer-1 previously approved these changes Jan 30, 2026
Copy link
Member

@beer-1 beer-1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@sources/vip.move`:
- Around line 1282-1299: The update_bridge_address function should explicitly
validate the bridge exists: after calling get_last_bridge_version (used in
update_bridge_address) check the is_registered boolean and if false abort with a
descriptive error (e.g., "bridge not found") instead of proceeding; do this
before constructing BridgeInfoKey and calling table::borrow_mut on
module_store.bridges, so the ModuleStore borrow and table access only occur for
registered bridges and return a clear error rather than a generic table abort.
- Around line 949-965: In check_user_reward_claimable, when last_claimed_stage
== 0 (first-time claimers) add an explicit assertion that start_stage >=
init_stage to mirror the deregistered-bridge check and prevent claims before
init_stage; locate the block that computes last_claimed_stage via
vesting::get_user_last_claimed_stage(account_addr, bridge_id, version) and
insert an assert for start_stage >= init_stage (use the same error code used for
invalid stages, e.g. EINVALID_CLAIMABLE_STAGE) before proceeding with snapshot
validation or other checks.
🧹 Nitpick comments (2)
sources/vip.move (2)

454-456: Fix typos in the comment.

The comment contains spelling errors: "Dereigstered" should be "Deregistered" and "submitt" should be "submit".

📝 Suggested fix
-        // Dereigstered rollup cannot submitt snapshot
+        // Deregistered rollup cannot submit snapshot

2062-2086: Consider using a more specific error code for the stage progression check.

The assertion at lines 2070-2075 validates that the next stage has started before allowing claims on a deregistered bridge. Using ESTAGE_DATA_NOT_FOUND for this check may be confusing since the actual intent is to ensure the stage is "complete" before claiming. Consider introducing a dedicated error constant like ESTAGE_NOT_FINALIZED for clarity.

Also, the mock snapshot returns create_time: 0 which causes is_after_challenge_period to return true immediately. This appears intentional for deregistered bridges but deserves a comment explaining this design choice.

Copy link
Member

@beer-1 beer-1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

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.

2 participants