Skip to content

Comments

feat: zone (omniflix) offboarding logic#2018

Merged
joe-bowman merged 5 commits intomainfrom
feat/flix-offboard
Jan 7, 2026
Merged

feat: zone (omniflix) offboarding logic#2018
joe-bowman merged 5 commits intomainfrom
feat/flix-offboard

Conversation

@ajansari95
Copy link
Contributor

@ajansari95 ajansari95 commented Dec 18, 2025

  1. Summary
    Implements zone offboarding functionality to safely wind down liquid staking zones (e.g., when a chain is sunsetting like FLIX).
  2. Type of change
  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update
  1. Implementation details
    This PR introduces a governance-controlled multi-step process for offboarding zones:
    New Governance Messages:
  • MsgGovSetZoneOffboarding - Enables/disables offboarding mode for a zone. When enabled:

    • Sets zone.IsOffboarding = true
    • Sets zone.DepositsEnabled = false (blocks new deposits)
    • Sets zone.UnbondingEnabled = false (blocks user redemptions)
  • MsgGovCancelAllPendingRedemptions - Cancels all pending redemptions (QUEUED and UNBOND status) and refunds qAssets from escrow to users. Requires zone to be in offboarding mode.

  • MsgGovForceUnbondAllDelegations - Creates MsgUndelegate for all delegations and submits via ICA. Requires zone to be in offboarding mode.
    Additional Changes:

  • TriggerRedemptionRate skips redemption rate updates for offboarding zones

  • HandleUndelegate detects and routes offboarding unbonds to new HandleOffboardingUndelegate handler

  • New memo type offboard/<block_height> for offboarding unbond ICA transactions
    Files Changed:

  • Proto: Added is_offboarding field to Zone, 3 new message types with responses

  • Keeper: 3 new msg_server handlers, modified ibc_packet_handlers

  • Types: New events, attributes, memo functions, codec registration

  • Upgrades: v1.10.0 upgrade handler

  1. How to test/use
    Unit Tests:
    go test -v -run "TestKeeperTestSuite/TestGovSetZoneOffboarding" ./x/interchainstaking/keeper/...
    go test -v -run "TestKeeperTestSuite/TestGovCancelAllPendingRedemptions" ./x/interchainstaking/keeper/...
    go test -v -run "TestKeeperTestSuite/TestGovForceUnbondAllDelegations" ./x/interchainstaking/keeper/...
    Offboarding Flow:
  2. Submit governance proposal with MsgGovSetZoneOffboarding (enable offboarding)
  3. Submit governance proposal with MsgGovCancelAllPendingRedemptions (refund users)
  4. Submit governance proposal with MsgGovForceUnbondAllDelegations (unbond all)
  5. Wait for unbonding period (~21 days)
  6. Submit governance proposal with MsgGovExecuteICATx to transfer funds to multisig
    See architecture/adr-003-zone-offboarding.md for detailed documentation and example proposals.
  7. Checklist
  • Does the Readme need to be updated?
    • ADR-003 documentation added at architecture/adr-003-zone-offboarding.md
  1. Limitations (optional)
  • Steps must be executed in order (offboarding must be enabled before cancel/unbond)
  • Requires ICA channels to remain operational for unbonding and fund transfer
  • qAsset holders after offboarding complete need alternative distribution mechanism (multisig-managed)
  • Unbonding period delay before funds can be transferred to multisig

Summary by CodeRabbit

  • New Features

    • Zone offboarding: governance-controlled multi-step exit (toggle offboarding, cancel pending redemptions with refunds, and force unbond delegations via automated ICA).
  • Documentation

    • Added architectural ADR detailing the zone offboarding flow and governance proposal examples.
  • Reliability / Bug Fixes

    • Reduced hard failures for already-processed withdrawal records during packet handling; offboarding paths skip incompatible redemption triggers.
  • Tests

    • Expanded test coverage for offboarding flows, governance handlers, and missing-record scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

- Add MsgGovSetZoneOffboarding to enable/disable offboarding mode
- Add MsgGovCancelAllPendingRedemptions to refund pending redemptions
- Add MsgGovForceUnbondAllDelegations to force unbond all delegations
- Skip redemption rate updates for offboarding zones
- Add is_offboarding field to Zone proto
- Add upgrade handler for v1.10.0
- Add ADR-003 documentation
Closes: zone offboarding for Omniflix
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 18, 2025

📝 Walkthrough

Walkthrough

Adds a zone offboarding feature: new governance messages and proto types, zone is_offboarding flag, gov handlers to toggle offboarding/cancel redemptions/force unbond, ICA-driven offboarding undelegate flows, new upgrade entry for v1.10.0, tests, ADR, and related codec/event/ibc memo additions.

Changes

Cohort / File(s) Change summary
Upgrades
app/upgrades/types.go, app/upgrades/upgrades.go, app/upgrades/v1_10.go, go.mod
Added v1.10.0 (and rc) upgrade names and entries; added V0101000UpgradeHandler scaffold; upgrade list extended.
Design / Docs
architecture/adr-003-zone-offboarding.md
New ADR describing governance-controlled zone offboarding flow, messages, preconditions, and examples.
Proto: messages & proposals
proto/quicksilver/interchainstaking/v1/interchainstaking.proto, proto/quicksilver/interchainstaking/v1/messages.proto, proto/quicksilver/interchainstaking/v1/proposals.proto
Added Zone.is_offboarding field; added 3 gov Msg RPCs and corresponding request/response proto messages; imported coin proto.
Keeper: gov msg handlers
x/interchainstaking/keeper/msg_server.go, x/interchainstaking/keeper/msg_server_test.go
Added GovSetZoneOffboarding, GovCancelAllPendingRedemptions, GovForceUnbondAllDelegations handlers and tests; handles toggling offboarding, refunds of queued withdrawals, and ICA force-unbond.
IBC / packet handling
x/interchainstaking/keeper/ibc_packet_handlers.go, x/interchainstaking/keeper/ibc_packet_handlers_test.go
Introduced offboarding undelegate path and HandleOffboardingUndelegate; adjusted many handlers to treat missing withdrawal records as non-fatal (log and return nil); tests updated/added to reflect missing-record nil behavior.
Types / messages / codec / events
x/interchainstaking/types/msgs.go, x/interchainstaking/types/codec.go, x/interchainstaking/types/events.go, x/interchainstaking/types/ibc_packet.go
Added three MsgGov* message types and registrations; new event constants/attribute keys; new MsgTypeOffboardingUnbond and OffboardingUnbondMemo helper.

Sequence Diagram(s)

sequenceDiagram
  actor Gov
  participant Keeper as InterchainKeeper
  participant ICA as ICA (Tx Submission)
  participant HostChain as HostZone (Validators/Stake)
  participant IBCHandler as IBC Ack Handler

  Note over Gov,Keeper: Gov proposal execution
  Gov->>Keeper: GovForceUnbondAllDelegations(chain_id)
  Keeper->>Keeper: validate authority, ensure zone.is_offboarding
  Keeper->>ICA: submit undelegate ICA txs (offboarding memo)
  ICA->>HostChain: broadcast undelegate txs to validators
  HostChain-->>ICA: ack/tx hash / completion info
  ICA->>IBCHandler: relay ack packet (undelegate ack)
  IBCHandler->>IBCHandler: detect offboarding memo -> HandleOffboardingUndelegate
  IBCHandler->>Keeper: decrement waitgroup, update delegation state via ICQ, emit OffboardingUnbondAck event
  Keeper->>Gov: return unbonding_count, total_unbonded
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • v1.7.7 release #1821 — modifies upgrades (types and upgrade list) adding new upgrade names and entries (strong overlap with upgrade changes).
  • 1869 add ica exec gov #1872 — touches interchainstaking governance/ICA surface and message registrations (related to new gov Msgs and msg_server changes).
  • use gaia lsm types #1894 — modifies interchainstaking IBC/withdrawal/tokenized flows (related to ibc_packet_handlers and withdrawal handling changes).

Suggested labels

x/interchainquery, docs

Suggested reviewers

  • faddat
  • arhamchordia

Poem

🐰
I hopped through code at break of dawn,
Tucked offboarding flags upon each lawn.
Gov bells chimed, ICA took flight,
Migrations queued into the night.
A carrot cheer — v1.10’s in sight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: implementing zone offboarding logic, which is the primary objective of this PR.
Description check ✅ Passed The description comprehensively covers all required template sections with detailed implementation details, test instructions, and documentation updates.

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

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@ajansari95 ajansari95 changed the title Feat/flix offboard feat: zone (omniflix) offboarding logic Dec 18, 2025
Copy link
Contributor

@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: 1

🧹 Nitpick comments (5)
app/upgrades/v1_10.go (1)

11-30: Upgrade handler is standard and safe; minor logging nit

The handler pattern (log → optional work → mm.RunMigrations) matches other upgrades and is safe given there are no state migrations for the new offboarding functionality.

If you care about log precision when the same handler is used for "v1.10.0-rc.0" and "v1.10.0", you might consider logging the actual plan name or a more generic label (e.g., “v1.10.x upgrade”) instead of hard‑coding "v1.10.0".

architecture/adr-003-zone-offboarding.md (1)

26-89: Add a language to the fenced block for the flow diagram

markdownlint (MD040) is complaining about the fenced block that contains the ASCII “ZONE OFFBOARDING FLOW” diagram because it has no language specifier. Consider declaring it explicitly, e.g.:

-```
+```text
 ┌─────────────────────────────────────────────────────────────────────────────┐
 │                         ZONE OFFBOARDING FLOW                               │
 ...
-```
+```

This should clear the linter without changing the rendered output.

x/interchainstaking/keeper/ibc_packet_handlers.go (1)

891-949: Consider adding delegation record update before ICQ.

The function decrements the waitgroup, then issues an ICQ query and increments the waitgroup, but unlike HandleUndelegate, it doesn't update the local delegation record synchronously. While the ICQ callback will eventually update the delegation, there may be a window where the local state is inconsistent.

Additionally, ensure the zone state is correctly persisted. The zone is passed by pointer, and SetZone is called at line 936, which should persist the waitgroup changes.

Consider adding local delegation update for consistency:
+	// Update local delegation record optimistically
+	delegation, found := k.GetDelegation(ctx, zone.ChainId, undelegateMsg.DelegatorAddress, undelegateMsg.ValidatorAddress)
+	if found {
+		delegation.Amount = delegation.Amount.Sub(undelegateMsg.Amount)
+		if delegation.Amount.IsPositive() {
+			k.SetDelegation(ctx, zone.ChainId, delegation)
+		} else {
+			k.DeleteDelegation(ctx, zone.ChainId, delegation)
+		}
+	}
+
 	// Update the delegation record via ICQ
 	delAddr, err := addressutils.AccAddressFromBech32(undelegateMsg.DelegatorAddress, "")
x/interchainstaking/keeper/msg_server_test.go (1)

1829-1871: Consider adding a test case for cancellation with both QUEUED and UNBOND records.

The handler iterates both WithdrawStatusQueued and WithdrawStatusUnbond records. The current "with records" test only creates a WithdrawStatusQueued record. Consider adding a test that verifies both types are cancelled.

x/interchainstaking/keeper/msg_server.go (1)

562-568: Consider documenting behavior when disabling offboarding.

When IsOffboarding is set to false, the zone state is updated but DepositsEnabled and UnbondingEnabled are not restored. This appears intentional (manual re-enablement via separate governance actions), but documenting this asymmetry would help operators understand the workflow.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 88a6ac7 and 0e8c661.

⛔ Files ignored due to path filters (5)
  • go.work.sum is excluded by !**/*.sum
  • x/interchainstaking/types/interchainstaking.pb.go is excluded by !**/*.pb.go
  • x/interchainstaking/types/messages.pb.go is excluded by !**/*.pb.go
  • x/interchainstaking/types/messages.pb.gw.go is excluded by !**/*.pb.gw.go
  • x/interchainstaking/types/proposals.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (15)
  • app/upgrades/types.go (1 hunks)
  • app/upgrades/upgrades.go (1 hunks)
  • app/upgrades/v1_10.go (1 hunks)
  • architecture/adr-003-zone-offboarding.md (1 hunks)
  • proto/quicksilver/interchainstaking/v1/interchainstaking.proto (1 hunks)
  • proto/quicksilver/interchainstaking/v1/messages.proto (1 hunks)
  • proto/quicksilver/interchainstaking/v1/proposals.proto (2 hunks)
  • x/interchainstaking/keeper/ibc_packet_handlers.go (8 hunks)
  • x/interchainstaking/keeper/ibc_packet_handlers_test.go (10 hunks)
  • x/interchainstaking/keeper/msg_server.go (2 hunks)
  • x/interchainstaking/keeper/msg_server_test.go (1 hunks)
  • x/interchainstaking/types/codec.go (2 hunks)
  • x/interchainstaking/types/events.go (2 hunks)
  • x/interchainstaking/types/ibc_packet.go (2 hunks)
  • x/interchainstaking/types/msgs.go (4 hunks)
🧰 Additional context used
🧠 Learnings (10)
📚 Learning: 2024-07-29T07:24:15.377Z
Learnt from: joe-bowman
Repo: quicksilver-zone/quicksilver PR: 1620
File: x/participationrewards/keeper/hooks.go:153-176
Timestamp: 2024-07-29T07:24:15.377Z
Learning: When computing IBC denoms, use `channel.PortId` and `counterpartychannel.PortId` instead of hardcoded strings for better accuracy.

Applied to files:

  • x/interchainstaking/types/ibc_packet.go
📚 Learning: 2025-01-28T19:03:57.668Z
Learnt from: arhamchordia
Repo: quicksilver-zone/quicksilver PR: 1803
File: x/supply/keeper/keeper_test.go:31-66
Timestamp: 2025-01-28T19:03:57.668Z
Learning: Avoid suggesting test cases that duplicate functionality already tested in msg_server_test.go, particularly for module account operations and balance transfers. Focus on unique keeper functionality instead.

Applied to files:

  • x/interchainstaking/keeper/ibc_packet_handlers_test.go
  • x/interchainstaking/keeper/msg_server_test.go
📚 Learning: 2024-07-29T07:24:15.377Z
Learnt from: joe-bowman
Repo: quicksilver-zone/quicksilver PR: 1099
File: x/interchainstaking/keeper/ibc_packet_handlers_test.go:1447-1447
Timestamp: 2024-07-29T07:24:15.377Z
Learning: The user has clarified that in the context of testing failure cases for unbondings, it is expected that withdrawal records will be marked as `requeued`.

Applied to files:

  • x/interchainstaking/keeper/ibc_packet_handlers_test.go
  • x/interchainstaking/keeper/ibc_packet_handlers.go
📚 Learning: 2024-10-15T13:03:53.675Z
Learnt from: joe-bowman
Repo: quicksilver-zone/quicksilver PR: 1099
File: x/interchainstaking/keeper/ibc_packet_handlers_test.go:1437-1437
Timestamp: 2024-10-15T13:03:53.675Z
Learning: In the context of testing failure cases for unbondings, it is expected that withdrawal records will be marked as `requeued`.

Applied to files:

  • x/interchainstaking/keeper/ibc_packet_handlers_test.go
  • x/interchainstaking/keeper/ibc_packet_handlers.go
📚 Learning: 2024-12-03T11:18:49.168Z
Learnt from: joe-bowman
Repo: quicksilver-zone/quicksilver PR: 1767
File: app/upgrades/v1_7.go:141-161
Timestamp: 2024-12-03T11:18:49.168Z
Learning: In the `V010705UpgradeHandler` function in `app/upgrades/v1_7.go`, the `OverrideRedemptionRateNoCap` method computes and sets `zone.RedemptionRate` at runtime, making it unnecessary to set it explicitly.

Applied to files:

  • app/upgrades/v1_10.go
  • x/interchainstaking/keeper/ibc_packet_handlers.go
  • app/upgrades/upgrades.go
  • app/upgrades/types.go
📚 Learning: 2024-11-19T14:54:21.046Z
Learnt from: joe-bowman
Repo: quicksilver-zone/quicksilver PR: 1747
File: app/upgrades/v1_7.go:44-49
Timestamp: 2024-11-19T14:54:21.046Z
Learning: In the Quicksilver codebase, panicking in an upgrade handler (e.g., using `panic(err)` within `V010702UpgradeHandler` in `app/upgrades/v1_7.go`) is acceptable to prevent the state from being committed when an error occurs during an upgrade.

Applied to files:

  • app/upgrades/v1_10.go
  • app/upgrades/upgrades.go
📚 Learning: 2024-07-29T07:24:15.377Z
Learnt from: joe-bowman
Repo: quicksilver-zone/quicksilver PR: 1132
File: x/interchainstaking/keeper/callbacks.go:662-664
Timestamp: 2024-07-29T07:24:15.377Z
Learning: The `DecrementWithdrawalWaitgroup` method in `x/interchainstaking/keeper/callbacks.go` includes underflow protection, ensuring safe decrement operations.

Applied to files:

  • x/interchainstaking/keeper/ibc_packet_handlers.go
📚 Learning: 2024-10-15T13:03:53.675Z
Learnt from: joe-bowman
Repo: quicksilver-zone/quicksilver PR: 1132
File: x/interchainstaking/keeper/callbacks.go:127-127
Timestamp: 2024-10-15T13:03:53.675Z
Learning: The `DecrementWithdrawalWaitgroup` method in `x/interchainstaking/keeper/callbacks.go` internally handles underflow protection, ensuring that the waitgroup count does not become negative.

Applied to files:

  • x/interchainstaking/keeper/ibc_packet_handlers.go
📚 Learning: 2025-04-04T13:21:50.558Z
Learnt from: joe-bowman
Repo: quicksilver-zone/quicksilver PR: 1833
File: proto/osmosis/gamm/poolmodels/balancer/v1beta1/tx.proto:4-6
Timestamp: 2025-04-04T13:21:50.558Z
Learning: In the Quicksilver project, some proto files like "amino/amino.proto" are not directly included in the repository but are imported during compilation through buf's dependency resolution mechanism, which pulls them from external dependencies specified in the buf.yaml configuration.

Applied to files:

  • proto/quicksilver/interchainstaking/v1/proposals.proto
📚 Learning: 2025-04-04T13:22:01.808Z
Learnt from: joe-bowman
Repo: quicksilver-zone/quicksilver PR: 1833
File: proto/osmosis/lockup/tx.proto:4-4
Timestamp: 2025-04-04T13:22:01.808Z
Learning: In Cosmos SDK projects using Protocol Buffers, imports like "amino/amino.proto" may be provided through buf's dependency resolution system rather than existing as local files. Static analysis tools may incorrectly flag these as errors, but they are valid imports that will be resolved during compilation.

Applied to files:

  • proto/quicksilver/interchainstaking/v1/proposals.proto
🧬 Code graph analysis (6)
x/interchainstaking/keeper/ibc_packet_handlers_test.go (3)
utils/randomutils/random.go (1)
  • GenerateRandomHashAsHex (23-25)
x/interchainstaking/types/interchainstaking.pb.go (6)
  • UnbondingRecord (653-660)
  • UnbondingRecord (664-664)
  • UnbondingRecord (665-667)
  • Validator (816-830)
  • Validator (834-834)
  • Validator (835-837)
x/interchainstaking/types/ibc_packet.go (1)
  • EpochWithdrawalMemo (57-59)
x/interchainstaking/keeper/ibc_packet_handlers.go (8)
x/interchainstaking/types/withdrawal_record.go (3)
  • WithdrawStatusSend (16-16)
  • WithdrawStatusTokenize (13-13)
  • WithdrawStatusUnbond (15-15)
x/interchainstaking/types/ibc_packet.go (3)
  • ParseEpochMsgMemo (26-38)
  • MsgTypeOffboardingUnbond (16-16)
  • MsgTypeWithdrawal (11-11)
x/interchainstaking/keeper/keeper.go (2)
  • Keeper (45-62)
  • Keeper (138-140)
x/interchainstaking/keeper/intent.go (1)
  • Keeper (20-25)
x/interchainstaking/types/interchainstaking.pb.go (3)
  • Zone (34-67)
  • Zone (71-71)
  • Zone (72-74)
x/interchainstaking/types/zones.go (3)
  • Zone (110-112)
  • Zone (114-135)
  • Zone (205-221)
utils/addressutils/address.go (2)
  • AccAddressFromBech32 (45-51)
  • ValAddressFromBech32 (67-73)
x/interchainstaking/types/events.go (1)
  • EventTypeOffboardingUnbondAck (18-18)
app/upgrades/upgrades.go (2)
app/upgrades/types.go (2)
  • V0101000rc0UpgradeName (33-33)
  • V0101000UpgradeName (34-34)
app/upgrades/v1_10.go (1)
  • V0101000UpgradeHandler (16-31)
x/interchainstaking/keeper/msg_server_test.go (2)
x/interchainstaking/types/proposals.pb.go (15)
  • MsgGovSetZoneOffboarding (658-662)
  • MsgGovSetZoneOffboarding (666-666)
  • MsgGovSetZoneOffboarding (667-669)
  • MsgGovCancelAllPendingRedemptions (735-738)
  • MsgGovCancelAllPendingRedemptions (742-742)
  • MsgGovCancelAllPendingRedemptions (743-745)
  • MsgGovCancelAllPendingRedemptionsResponse (773-776)
  • MsgGovCancelAllPendingRedemptionsResponse (784-784)
  • MsgGovCancelAllPendingRedemptionsResponse (785-787)
  • MsgGovForceUnbondAllDelegations (831-834)
  • MsgGovForceUnbondAllDelegations (838-838)
  • MsgGovForceUnbondAllDelegations (839-841)
  • MsgGovForceUnbondAllDelegationsResponse (869-872)
  • MsgGovForceUnbondAllDelegationsResponse (878-878)
  • MsgGovForceUnbondAllDelegationsResponse (879-881)
x/interchainstaking/keeper/msg_server.go (1)
  • NewMsgServerImpl (27-29)
x/interchainstaking/types/msgs.go (1)
x/interchainstaking/types/proposals.pb.go (9)
  • MsgGovSetZoneOffboarding (658-662)
  • MsgGovSetZoneOffboarding (666-666)
  • MsgGovSetZoneOffboarding (667-669)
  • MsgGovCancelAllPendingRedemptions (735-738)
  • MsgGovCancelAllPendingRedemptions (742-742)
  • MsgGovCancelAllPendingRedemptions (743-745)
  • MsgGovForceUnbondAllDelegations (831-834)
  • MsgGovForceUnbondAllDelegations (838-838)
  • MsgGovForceUnbondAllDelegations (839-841)
x/interchainstaking/types/codec.go (1)
x/interchainstaking/types/proposals.pb.go (9)
  • MsgGovSetZoneOffboarding (658-662)
  • MsgGovSetZoneOffboarding (666-666)
  • MsgGovSetZoneOffboarding (667-669)
  • MsgGovCancelAllPendingRedemptions (735-738)
  • MsgGovCancelAllPendingRedemptions (742-742)
  • MsgGovCancelAllPendingRedemptions (743-745)
  • MsgGovForceUnbondAllDelegations (831-834)
  • MsgGovForceUnbondAllDelegations (838-838)
  • MsgGovForceUnbondAllDelegations (839-841)
🪛 Buf (1.61.0)
proto/quicksilver/interchainstaking/v1/proposals.proto

4-4: import "cosmos/base/v1beta1/coin.proto": file does not exist

(COMPILE)

🪛 markdownlint-cli2 (0.18.1)
architecture/adr-003-zone-offboarding.md

26-26: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: test quicksilver (amd64, windows)
  • GitHub Check: test quicksilver (darwin, arm64)
  • GitHub Check: test quicksilver (amd64, linux)
  • GitHub Check: Analyze
  • GitHub Check: lint
🔇 Additional comments (22)
proto/quicksilver/interchainstaking/v1/interchainstaking.proto (1)

59-59: Zone.is_offboarding field wiring looks correct

Adding a new bool is_offboarding = 32; with a fresh field number is backward‑compatible in proto3 and matches the ADR semantics. No issues from a schema/upgrade perspective.

x/interchainstaking/types/ibc_packet.go (1)

11-16: Offboarding memo type is consistent with existing memo patterns

MsgTypeOffboardingUnbond = "offboard" and OffboardingUnbondMemo follow the existing msgType/value memo convention (rebalance, withdrawal, unbondSend), so they should integrate cleanly with the existing parsing helpers and IBC ack handling.

Also applies to: 65-67

app/upgrades/types.go (1)

33-34: v1.10.0 upgrade name constants are wired correctly

The new V0101000rc0UpgradeName / V0101000UpgradeName constants cleanly extend the existing upgrade-name set and match the plan names used in upgrades.go. No behavioral concerns.

app/upgrades/upgrades.go (1)

27-29: Upgrade entries for v1.10.0 are correctly registered

Registering both "v1.10.0-rc.0" and "v1.10.0" with V0101000UpgradeHandler is consistent with the existing pattern and will correctly route both plans through the same upgrade logic.

x/interchainstaking/types/codec.go (1)

28-30: Codec registrations for new governance messages are complete

The three new governance messages are properly registered in both the legacy Amino codec and the interface registry as sdk.Msg implementations, which is necessary for gov‑v1 Msg proposals to work. No additional govv1beta1.Content registrations are needed since these are Msg‑style, not legacy content‑style proposals.

Also applies to: 48-50

x/interchainstaking/keeper/ibc_packet_handlers_test.go (2)

585-603: Updated tests correctly reflect non-fatal missing-record semantics

Renaming cases and flipping expectations to err == false for “no matching record / returns nil” paths in:

  • TestHandleWithdrawForUser (“no matching record - returns nil”),
  • TestHandleFailedUnbondSend (“no matching record - returns nil”, “second returns nil”),
  • TestHandleCompleteSend (“From DelegationAddress - missing record returns nil”),
  • TestHandleFailedBankSend (“no matching record - returns nil”),

accurately encode the new behavior where missing withdrawal records are treated as benign and simply skipped. Assertions still ensure that no unintended record status transitions occur.

This aligns with the ADR’s goal of making offboarding and replay scenarios more robust without surfacing spurious errors.

Also applies to: 1619-1637, 1641-1669, 4400-4410, 4513-4537


5120-5285: *New _MissingRecord tests give good coverage of nil-return paths

The added tests:

  • TestHandleWithdrawForUser_MissingRecord
  • TestHandleTokenizedShares_MissingRecord
  • TestHandleUndelegate_MissingWithdrawalRecord
  • TestHandleFailedUnbondSend_MissingRecord
  • TestHandleFailedUndelegate_MissingWithdrawalRecord

all cleanly exercise the “record not found → no error” branches, and where appropriate they also assert that unbonding records are still updated/cleaned up as intended. The setups are minimal and focused on the keeper behavior, not on msg_server logic, which fits prior guidance to avoid duplicating msg_server tests. Based on learnings, this separation between missing-record handling and real failure/requeue semantics looks solid.

proto/quicksilver/interchainstaking/v1/messages.proto (1)

100-123: LGTM!

The three new governance RPC methods follow the existing patterns in the file. HTTP annotations are consistent with the naming conventions used for other governance endpoints.

x/interchainstaking/types/events.go (2)

15-18: LGTM!

The new event type constants for the offboarding workflow are well-named and follow existing conventions.


40-47: LGTM!

The new attribute keys comprehensively cover the offboarding workflow telemetry needs and are consistent with existing attribute naming patterns.

x/interchainstaking/keeper/ibc_packet_handlers.go (3)

461-464: Verify this behavioral change is intentional.

Changing from returning an error to returning nil when a withdrawal record is not found makes this a non-fatal condition. This could mask issues where records are unexpectedly missing. The log message indicates this is expected ("may have already been processed"), but ensure this doesn't silently drop legitimate error cases.


801-811: LGTM - Offboarding memo check precedes standard withdrawal flow.

The logic correctly routes offboarding unbonds to the dedicated handler while maintaining the existing withdrawal flow for standard unbonds.


1542-1547: LGTM - Redemption rate updates correctly skipped for offboarding zones.

This prevents the redemption rate from being updated during offboarding, which aligns with the PR's intent to freeze the rate while winding down the zone.

x/interchainstaking/keeper/msg_server_test.go (2)

1666-1763: LGTM - Comprehensive test coverage for GovSetZoneOffboarding.

The test covers key scenarios including invalid authority, zone not found, enabling offboarding, and disabling offboarding. The post-checks verify the expected zone state changes (IsOffboarding, DepositsEnabled, UnbondingEnabled).


1957-1989: Good use of TxKeeper mock to verify ICA transaction submission.

The test properly verifies that the ICA transaction was submitted by checking len(txk.Txs) == 1 and validates the response fields.

x/interchainstaking/keeper/msg_server.go (3)

712-730: LGTM - Delegation aggregation and message construction.

The logic correctly skips zero-amount delegations and aggregates the total unbonded amount. The use of sdk.ZeroInt() for initialization is appropriate.


745-748: The code is safe. The IncrementWithdrawalWaitgroup method includes built-in overflow protection that detects when the uint32 counter would exceed its maximum value and returns an error, which is properly checked at the call site. No additional bounds checking is needed.

Likely an incorrect or invalid review comment.


740-742: Batching implementation is correct.

The SubmitTx function properly batches messages using zone.MessagesPerTx (default 5) as the chunk size in ProdSubmitTx. Each batch is submitted as a separate ICA transaction via SendTx, which naturally constrains gas and message size per transaction. The withdrawal waitgroup increment (by total message count) correctly balances with the decrement-by-1 pattern in acknowledgement handlers, ensuring accurate tracking of in-flight messages.

proto/quicksilver/interchainstaking/v1/proposals.proto (2)

4-4: Static analysis false positive: import resolved via buf.

The import is flagged by static analysis but will be resolved during compilation through buf's dependency resolution mechanism specified in buf.yaml. This is standard for Cosmos SDK proto dependencies.

Based on learnings, this is a known pattern in Cosmos SDK projects.


165-211: LGTM! Well-structured governance message definitions.

The three new governance messages follow Cosmos SDK conventions:

  • Proper field types and numbering
  • Appropriate gogoproto annotations for SDK type generation
  • Clear documentation comments describing purpose and constraints
  • Response types correctly use nullable=false to prevent nil pointer issues

The definitions align well with the multi-step offboarding flow described in the PR objectives.

x/interchainstaking/types/msgs.go (2)

35-37: LGTM! Proper constant definitions and interface assertions.

The new message type constants follow the established naming convention, and interface assertions ensure compile-time verification that the messages correctly implement both sdk.Msg and legacytx.LegacyMsg interfaces.

Also applies to: 51-53, 66-68


715-807: LGTM! Implementations follow established patterns.

All three governance message implementations correctly:

  • Validate authority address and chain_id constraints in ValidateBasic (consistent with existing governance messages like MsgGovSetLsmCaps)
  • Use standard Cosmos SDK patterns for GetSignBytes, GetSigners, Route, and Type
  • Implement both sdk.Msg and legacytx.LegacyMsg interfaces

The validation logic is appropriately minimal at this layer, with business logic validation (e.g., checking if zone exists, verifying offboarding state) properly deferred to the keeper/handler layer.

@codecov
Copy link

codecov bot commented Dec 18, 2025

Codecov Report

❌ Patch coverage is 51.87970% with 128 lines in your changes missing coverage. Please review.
✅ Project coverage is 58.70%. Comparing base (2605099) to head (f65e602).

Files with missing lines Patch % Lines
x/interchainstaking/types/msgs.go 0.00% 48 Missing ⚠️
x/interchainstaking/keeper/ibc_packet_handlers.go 20.33% 44 Missing and 3 partials ⚠️
x/interchainstaking/keeper/msg_server.go 86.01% 12 Missing and 8 partials ⚠️
app/upgrades/v1_10.go 0.00% 6 Missing ⚠️
x/interchainstaking/types/codec.go 50.00% 3 Missing ⚠️
app/upgrades/upgrades.go 0.00% 2 Missing ⚠️
x/interchainstaking/types/ibc_packet.go 0.00% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2018      +/-   ##
==========================================
- Coverage   58.81%   58.70%   -0.11%     
==========================================
  Files         171      172       +1     
  Lines       12251    12509     +258     
==========================================
+ Hits         7205     7343     +138     
- Misses       4275     4388     +113     
- Partials      771      778       +7     
Flag Coverage Δ
unittests 58.70% <51.87%> (-0.11%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
app/upgrades/types.go 0.00% <ø> (ø)
app/upgrades/upgrades.go 0.00% <0.00%> (ø)
x/interchainstaking/types/ibc_packet.go 60.86% <0.00%> (-5.80%) ⬇️
x/interchainstaking/types/codec.go 41.86% <50.00%> (+1.31%) ⬆️
app/upgrades/v1_10.go 0.00% <0.00%> (ø)
x/interchainstaking/keeper/msg_server.go 87.73% <86.01%> (-0.73%) ⬇️
x/interchainstaking/keeper/ibc_packet_handlers.go 63.13% <20.33%> (-2.03%) ⬇️
x/interchainstaking/types/msgs.go 64.67% <0.00%> (-10.86%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@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 @x/interchainstaking/keeper/msg_server.go:
- Around line 625-629: The code risks a panic by indexing amountsToRefund[0] and
only checks the first denom; update the logic in the cancel withdrawal flow to
first handle an empty amountsToRefund slice (return a clear error or no-op) and
then iterate over all coins in amountsToRefund, calling
k.BankKeeper.GetBalance(ctx,
k.AccountKeeper.GetModuleAddress(types.EscrowModuleAccount), coin.Denom) and
comparing with coin using IsLT; for any insufficient balance return a formatted
error that includes the specific denom and amounts (use escrowBalance.String()
and coin.String()) so every denomination is validated rather than only the
first.
- Line 717: Replace the deprecated sdk.ZeroInt() call used when initializing
totalUnbonded in msg_server.go with the math package used elsewhere: import
cosmossdk.io/math as sdkmath (match existing alias pattern) and change
totalUnbonded := sdk.NewCoin(zone.BaseDenom, sdk.ZeroInt()) to use
sdkmath.ZeroInt() so it becomes sdk.NewCoin(zone.BaseDenom, sdkmath.ZeroInt()).
🧹 Nitpick comments (2)
x/interchainstaking/keeper/msg_server.go (2)

632-643: All-or-nothing cancellation semantics.

The function now fails immediately on address parsing or transfer errors, ensuring all-or-nothing semantics (either all records are cancelled and refunded, or the entire operation is rolled back). This is consistent with the commit message's intent to "be defensive around refunds; barf if there are errors."

While this prevents partial state, consider adding a comment to document this behavior explicitly, as operators may expect progress even if some records fail.


749-749: Optional: Add overflow protection for message count.

The nolint:gosec comment indicates awareness of the potential overflow when casting len(msgs) to uint32. While unlikely in practice (would require over 4 billion delegations), consider adding an explicit check for defensive programming.

🔎 Proposed overflow check
+	// Ensure message count fits in uint32
+	if len(msgs) > 0xFFFFFFFF {
+		return nil, fmt.Errorf("too many unbonding messages: %d exceeds uint32 max", len(msgs))
+	}
+
 	// Increment withdrawal waitgroup for the unbonding messages
-	if err := zone.IncrementWithdrawalWaitgroup(k.Logger(ctx), uint32(len(msgs)), "offboarding unbond messages"); err != nil { //nolint:gosec
+	if err := zone.IncrementWithdrawalWaitgroup(k.Logger(ctx), uint32(len(msgs)), "offboarding unbond messages"); err != nil {
 		return nil, err
 	}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e8c661 and 718d0a7.

📒 Files selected for processing (1)
  • x/interchainstaking/keeper/msg_server.go
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2024-12-03T11:18:49.168Z
Learnt from: joe-bowman
Repo: quicksilver-zone/quicksilver PR: 1767
File: app/upgrades/v1_7.go:141-161
Timestamp: 2024-12-03T11:18:49.168Z
Learning: In the `V010705UpgradeHandler` function in `app/upgrades/v1_7.go`, the `OverrideRedemptionRateNoCap` method computes and sets `zone.RedemptionRate` at runtime, making it unnecessary to set it explicitly.

Applied to files:

  • x/interchainstaking/keeper/msg_server.go
📚 Learning: 2024-10-15T13:03:53.675Z
Learnt from: joe-bowman
Repo: quicksilver-zone/quicksilver PR: 1099
File: x/interchainstaking/keeper/ibc_packet_handlers_test.go:1447-1447
Timestamp: 2024-10-15T13:03:53.675Z
Learning: The user has clarified that in the context of testing failure cases for unbondings, it is expected that withdrawal records will be marked as `requeued`.

Applied to files:

  • x/interchainstaking/keeper/msg_server.go
📚 Learning: 2024-07-29T07:24:15.377Z
Learnt from: joe-bowman
Repo: quicksilver-zone/quicksilver PR: 1099
File: x/interchainstaking/keeper/ibc_packet_handlers_test.go:1437-1437
Timestamp: 2024-07-29T07:24:15.377Z
Learning: In the context of testing failure cases for unbondings, it is expected that withdrawal records will be marked as `requeued`.

Applied to files:

  • x/interchainstaking/keeper/msg_server.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: test quicksilver (amd64, windows)
  • GitHub Check: test quicksilver (darwin, arm64)
  • GitHub Check: test quicksilver (amd64, linux)
  • GitHub Check: Analyze
  • GitHub Check: lint
🔇 Additional comments (2)
x/interchainstaking/keeper/msg_server.go (2)

15-15: LGTM!

The stakingtypes import is correctly added to support the MsgUndelegate message creation in the GovForceUnbondAllDelegations function.


562-568: Verify intended behavior when disabling offboarding.

When IsOffboarding is set to false, the code does not re-enable DepositsEnabled or UnbondingEnabled. The test case for disabling offboarding (lines 1701-1738 in msg_server_test.go) only verifies that IsOffboarding becomes false, but does not assert the expected state of these flags. Confirm whether deposits and unbonding should remain disabled until explicitly re-enabled via separate governance actions, or whether they should be restored when offboarding is disabled. If the former is intended, update the function comment and test postCheck to document this behavior explicitly.

Copy link
Contributor

@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: 1

🤖 Fix all issues with AI agents
In @x/interchainstaking/keeper/msg_server.go:
- Around line 626-631: The escrow balance check currently only verifies the
first coin in amountsToRefund; update the logic in the message handler (where
amountsToRefund is used) to iterate over every coin in amountsToRefund and for
each coin call k.BankKeeper.GetBalance(ctx,
k.AccountKeeper.GetModuleAddress(types.EscrowModuleAccount), coin.Denom) and
ensure escrowBalance.IsLT(coin) is handled (returning the same formatted error
including escrowBalance and coin) before performing any refunds so all
denominations are defensively validated.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 718d0a7 and d5b9b1e.

📒 Files selected for processing (1)
  • x/interchainstaking/keeper/msg_server.go
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: joe-bowman
Repo: quicksilver-zone/quicksilver PR: 1767
File: app/upgrades/v1_7.go:141-161
Timestamp: 2024-12-03T11:18:49.168Z
Learning: In the `V010705UpgradeHandler` function in `app/upgrades/v1_7.go`, the `OverrideRedemptionRateNoCap` method computes and sets `zone.RedemptionRate` at runtime, making it unnecessary to set it explicitly.
📚 Learning: 2024-12-03T11:18:49.168Z
Learnt from: joe-bowman
Repo: quicksilver-zone/quicksilver PR: 1767
File: app/upgrades/v1_7.go:141-161
Timestamp: 2024-12-03T11:18:49.168Z
Learning: In the `V010705UpgradeHandler` function in `app/upgrades/v1_7.go`, the `OverrideRedemptionRateNoCap` method computes and sets `zone.RedemptionRate` at runtime, making it unnecessary to set it explicitly.

Applied to files:

  • x/interchainstaking/keeper/msg_server.go
📚 Learning: 2024-10-15T13:03:53.675Z
Learnt from: joe-bowman
Repo: quicksilver-zone/quicksilver PR: 1099
File: x/interchainstaking/keeper/ibc_packet_handlers_test.go:1447-1447
Timestamp: 2024-10-15T13:03:53.675Z
Learning: The user has clarified that in the context of testing failure cases for unbondings, it is expected that withdrawal records will be marked as `requeued`.

Applied to files:

  • x/interchainstaking/keeper/msg_server.go
📚 Learning: 2024-07-29T07:24:15.377Z
Learnt from: joe-bowman
Repo: quicksilver-zone/quicksilver PR: 1099
File: x/interchainstaking/keeper/ibc_packet_handlers_test.go:1437-1437
Timestamp: 2024-07-29T07:24:15.377Z
Learning: In the context of testing failure cases for unbondings, it is expected that withdrawal records will be marked as `requeued`.

Applied to files:

  • x/interchainstaking/keeper/msg_server.go
📚 Learning: 2024-07-29T07:24:15.377Z
Learnt from: joe-bowman
Repo: quicksilver-zone/quicksilver PR: 1132
File: x/interchainstaking/keeper/callbacks.go:662-664
Timestamp: 2024-07-29T07:24:15.377Z
Learning: The `DecrementWithdrawalWaitgroup` method in `x/interchainstaking/keeper/callbacks.go` includes underflow protection, ensuring safe decrement operations.

Applied to files:

  • x/interchainstaking/keeper/msg_server.go
📚 Learning: 2025-01-28T19:03:57.668Z
Learnt from: arhamchordia
Repo: quicksilver-zone/quicksilver PR: 1803
File: x/supply/keeper/keeper_test.go:31-66
Timestamp: 2025-01-28T19:03:57.668Z
Learning: Avoid suggesting test cases that duplicate functionality already tested in msg_server_test.go, particularly for module account operations and balance transfers. Focus on unique keeper functionality instead.

Applied to files:

  • x/interchainstaking/keeper/msg_server.go
📚 Learning: 2024-10-15T13:03:53.675Z
Learnt from: joe-bowman
Repo: quicksilver-zone/quicksilver PR: 1132
File: x/interchainstaking/keeper/callbacks.go:625-625
Timestamp: 2024-10-15T13:03:53.675Z
Learning: The `DecrementWithdrawalWaitgroup` method in `x/interchainstaking/keeper/callbacks.go` internally handles underflow protection, ensuring that the waitgroup count does not become negative.

Applied to files:

  • x/interchainstaking/keeper/msg_server.go
📚 Learning: 2024-10-15T13:03:53.675Z
Learnt from: joe-bowman
Repo: quicksilver-zone/quicksilver PR: 1183
File: x/interchainstaking/keeper/ibc_packet_handlers.go:713-717
Timestamp: 2024-10-15T13:03:53.675Z
Learning: The error handling strategy of logging errors without halting execution or propagating them further in the context of decrementing the withdrawal waitgroup is intentional and considered appropriate by the developers.

Applied to files:

  • x/interchainstaking/keeper/msg_server.go
📚 Learning: 2024-07-29T07:24:15.377Z
Learnt from: joe-bowman
Repo: quicksilver-zone/quicksilver PR: 1620
File: x/participationrewards/keeper/hooks.go:153-176
Timestamp: 2024-07-29T07:24:15.377Z
Learning: When computing IBC denoms, use `channel.PortId` and `counterpartychannel.PortId` instead of hardcoded strings for better accuracy.

Applied to files:

  • x/interchainstaking/keeper/msg_server.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: lint
  • GitHub Check: test quicksilver (darwin, arm64)
  • GitHub Check: Analyze
  • GitHub Check: test quicksilver (amd64, windows)
  • GitHub Check: test quicksilver (amd64, linux)
🔇 Additional comments (6)
x/interchainstaking/keeper/msg_server.go (6)

13-13: LGTM! Appropriate imports for offboarding functionality.

The cosmossdk.io/math import is correctly used for math.ZeroInt() on line 720, and stakingtypes is used for MsgUndelegate on line 729. Both are necessary for the new governance handlers.

Also applies to: 16-16


563-569: Verify: Should disabling offboarding re-enable deposits and unbonding?

When msg.IsOffboarding is set to false (disabling offboarding), the code does not restore zone.DepositsEnabled or zone.UnbondingEnabled. Currently, these flags remain in their previous state. If the intended behavior is to automatically re-enable these features when offboarding is disabled, add an else branch:

if msg.IsOffboarding {
    zone.DepositsEnabled = false
    zone.UnbondingEnabled = false
} else {
    zone.DepositsEnabled = true
    zone.UnbondingEnabled = true
}

If the current behavior (requiring separate governance actions to re-enable) is intentional, this is fine as-is.


634-645: Error handling prevents partial state correctly.

The handler returns errors immediately when address parsing (line 638) or coin transfer (line 644) fails, which prevents partial cancellations and ensures transactional integrity. This is the correct approach for governance operations that should be atomic.


752-755: LGTM! Waitgroup increment follows established patterns.

The waitgroup increment correctly accounts for the number of unbonding messages submitted via ICA. The gosec suppression is appropriate since len(msgs) is bounded by the number of delegations (which should be reasonable for any zone). Error handling ensures that failures are caught before the zone state is saved.


704-717: Excellent validation chain prevents invalid operations.

The handler validates all critical preconditions before submitting ICA transactions:

  1. Zone must be in offboarding mode (line 704)
  2. Delegation address must exist (line 709)
  3. Delegations must exist (lines 714-717)
  4. Non-zero delegations must exist after filtering (lines 739-741)

This defensive approach ensures governance operations fail early with clear error messages rather than submitting invalid ICA transactions.

Also applies to: 739-741


744-749: Offboarding memo handling is correctly implemented in IBC packet handlers.

The HandleUndelegate function in ibc_packet_handlers.go (lines 801-806) properly detects the offboarding memo pattern using ParseEpochMsgMemo(memo, types.MsgTypeOffboardingUnbond) and correctly routes to the dedicated HandleOffboardingUndelegate handler. The handler properly decrements the withdrawal waitgroup and manages state for offboarding unbonds. The memo format matches the constant definition.

@joe-bowman joe-bowman merged commit b414734 into main Jan 7, 2026
11 of 15 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Jan 7, 2026
5 tasks
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