Skip to content

Conversation

@northstar456
Copy link
Contributor

@northstar456 northstar456 commented Nov 17, 2025

Changelist

[Describe or list the changes made in this PR]

Test Plan

[Describe how this PR was tested (if applicable)]

Author/Reviewer Checklist

  • If this PR has changes that result in a different app state given the same prior state and transaction list, manually add the state-breaking label.
  • If the PR has breaking postgres changes to the indexer add the indexer-postgres-breaking label.
  • If this PR isn't state-breaking but has changes that modify behavior in PrepareProposal or ProcessProposal, manually add the label proposal-breaking.
  • If this PR is one of many that implement a specific feature, manually label them all feature:[feature-name].
  • If you wish to for mergify-bot to automatically create a PR to backport your change to a release branch, manually add the label backport/[branch-name].
  • Manually add any of the following labels: refactor, chore, bug.

Summary by CodeRabbit

  • New Features

    • Added deposit constraint validation that blocks withdrawals and transfers which would create a subaccount with an initial USDC deposit below 0.1 USDC, ensuring new subaccounts meet minimum deposit thresholds.
  • Other

    • Introduced a specific failure outcome to surface this constraint when it occurs.

@northstar456 northstar456 requested a review from a team as a code owner November 17, 2025 15:24
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 17, 2025

Walkthrough

Adds a minimum-deposit constraint check for subaccount updates: new constant and update result, a helper that flags USDC deposits below the minimum on new/zero-balance subaccounts, and integration of that check into the Withdrawal/Transfer validation flow to short-circuit further checks on violation.

Changes

Cohort / File(s) Summary
Type System & Constants
protocol/x/subaccounts/types/update.go
Adds ViolatesDepositConstraints to UpdateResult, maps it in updateResultStringMap, and declares MIN_SUBACCOUNT_INITIAL_DEPOSIT_QUANTUMS = 100_000 (0.1 USDC).
Deposit Constraint Validation & Integration
protocol/x/subaccounts/keeper/subaccount.go
Adds checkDepositConstraints(settledUpdates) that scans settled updates and marks updates as ViolatesDepositConstraints when they deposit USDC below the minimum into subaccounts with no USDC and no perp positions. Calls this helper early in internalCanUpdateSubaccountsWithLeverage for Withdrawal/Transfer flows and short-circuits further validation on violation.

Sequence Diagram

sequenceDiagram
    actor Client
    participant Validator as internalCanUpdateSubaccountsWithLeverage
    participant DepositCheck as checkDepositConstraints

    Client->>Validator: Submit Withdrawal/Transfer updates
    activate Validator

    rect rgb(235,245,235)
    Note over Validator,DepositCheck: Early deposit-constraint validation
    Validator->>DepositCheck: Pass settled updates
    activate DepositCheck
    DepositCheck->>DepositCheck: Iterate updates\n(USDC delta checks on zero-balance subaccounts)
    alt Violation found
        DepositCheck-->>Validator: Violations present
    else No violation
        DepositCheck-->>Validator: No violations
    end
    deactivate DepositCheck
    end

    alt Violations present
        Validator-->>Client: Reject (ViolatesDepositConstraints)
    else No violations
        Validator->>Validator: Continue TNC & outage checks
        Validator-->>Client: Proceed with update
    end
    deactivate Validator
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Review focus:
    • Correct use and placement of MIN_SUBACCOUNT_INITIAL_DEPOSIT_QUANTUMS
    • Correct detection of "zero-balance" subaccounts and perp-position checks
    • Proper propagation and handling of the new ViolatesDepositConstraints result in callers
    • Edge cases: multiple USDC updates, combined deposit/withdrawal in same batch

Poem

🐇 I nibble code and count each cent so fine,
A tiny quantum guards each newborn line.
If pennies fall short, I thump my little drum—
"Not yet," says the rabbit, "the vault stays mum." 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description follows the template structure but fails to fill critical sections; Changelist and Test Plan contain only placeholders without actual content. Fill in the Changelist section describing the new UpdateResult variant and MIN_SUBACCOUNT_INITIAL_DEPOSIT_QUANTUMS constant, and provide a Test Plan detailing how the deposit constraint logic was verified.
Title check ❓ Inconclusive The title is vague and generic, using placeholder-like language ('need to fix current test') that doesn't clearly convey the main change despite mentioning 'initial impl'. Revise the title to be more specific and descriptive, e.g., 'Add deposit constraint validation for new subaccounts' or 'Implement MIN_SUBACCOUNT_INITIAL_DEPOSIT check'.
✅ Passed checks (1 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/min-initial-deposit

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.

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3f8d74c and 5ccf442.

📒 Files selected for processing (2)
  • protocol/x/subaccounts/keeper/subaccount.go (2 hunks)
  • protocol/x/subaccounts/types/update.go (3 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: hwray
Repo: dydxprotocol/v4-chain PR: 2551
File: protocol/x/subaccounts/keeper/subaccount.go:852-865
Timestamp: 2024-11-15T16:00:11.304Z
Learning: The function `GetCrossInsuranceFundBalance` in `protocol/x/subaccounts/keeper/subaccount.go` already existed and was just moved in this PR; changes to its error handling may be out of scope.
📚 Learning: 2024-11-15T16:00:11.304Z
Learnt from: hwray
Repo: dydxprotocol/v4-chain PR: 2551
File: protocol/x/subaccounts/keeper/subaccount.go:852-865
Timestamp: 2024-11-15T16:00:11.304Z
Learning: The function `GetCrossInsuranceFundBalance` in `protocol/x/subaccounts/keeper/subaccount.go` already existed and was just moved in this PR; changes to its error handling may be out of scope.

Applied to files:

  • protocol/x/subaccounts/types/update.go
  • protocol/x/subaccounts/keeper/subaccount.go
📚 Learning: 2024-11-15T15:59:28.095Z
Learnt from: hwray
Repo: dydxprotocol/v4-chain PR: 2551
File: protocol/x/subaccounts/keeper/subaccount.go:833-850
Timestamp: 2024-11-15T15:59:28.095Z
Learning: The function `GetInsuranceFundBalance` in `protocol/x/subaccounts/keeper/subaccount.go` already existed and was just moved in this PR; changes to its error handling may be out of scope.

Applied to files:

  • protocol/x/subaccounts/types/update.go
  • protocol/x/subaccounts/keeper/subaccount.go
📚 Learning: 2024-11-15T16:17:29.092Z
Learnt from: hwray
Repo: dydxprotocol/v4-chain PR: 2551
File: protocol/x/clob/types/expected_keepers.go:86-90
Timestamp: 2024-11-15T16:17:29.092Z
Learning: The function `GetCrossInsuranceFundBalance` in `protocol/x/clob/types/expected_keepers.go` already existed and was just moved in this PR; changes to its error handling may be out of scope.

Applied to files:

  • protocol/x/subaccounts/keeper/subaccount.go
📚 Learning: 2025-10-06T20:00:48.549Z
Learnt from: anmolagrawal345
Repo: dydxprotocol/v4-chain PR: 3079
File: protocol/x/clob/types/leverage.go:10-56
Timestamp: 2025-10-06T20:00:48.549Z
Learning: In the leverage update flow for protocol/x/clob, the `ValidateUpdateLeverageMsg` function performs basic validation (non-nil checks, non-zero leverage, unique IDs, valid clob pair existence), while maximum leverage validation against `GetMaxLeverageForPerpetual` is intentionally deferred to the `UpdateLeverage` keeper method.

Applied to files:

  • protocol/x/subaccounts/keeper/subaccount.go
🧬 Code graph analysis (1)
protocol/x/subaccounts/keeper/subaccount.go (3)
protocol/x/subaccounts/types/settled_update.go (1)
  • SettledUpdate (7-17)
protocol/x/subaccounts/types/update.go (3)
  • UpdateResult (10-10)
  • MIN_SUBACCOUNT_INITIAL_DEPOSIT_QUANTUMS (163-163)
  • ViolatesDepositConstraints (75-75)
protocol/x/assets/types/genesis.go (1)
  • AssetUsdc (12-19)
⏰ 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). (28)
  • GitHub Check: build-and-push-testnet
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: benchmark
  • GitHub Check: build-and-push-mainnet
  • GitHub Check: container-tests
  • GitHub Check: test-race
  • GitHub Check: golangci-lint
  • GitHub Check: build
  • GitHub Check: test-coverage-upload
  • GitHub Check: unit-end-to-end-and-integration
  • GitHub Check: liveness-test
  • GitHub Check: check-sample-pregenesis-up-to-date
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
  • GitHub Check: Summary

Comment on lines 583 to +588
if updateType == types.Withdrawal || updateType == types.Transfer {
success, successPerUpdate = k.checkDepositConstraints(settledUpdates)
if !success {
return success, successPerUpdate, nil
}

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Include deposit updates in the minimum deposit gate

Right now we only invoke checkDepositConstraints for Withdrawal and Transfer. Any Deposit update skips this guard entirely, so a user can still open a fresh subaccount with a sub-0.1 USDC deposit via the deposit flow, which defeats the whole intent of MIN_SUBACCOUNT_INITIAL_DEPOSIT_QUANTUMS. Please run the same check for types.Deposit updates as well.

-	if updateType == types.Withdrawal || updateType == types.Transfer {
+	if updateType == types.Withdrawal || updateType == types.Transfer || updateType == types.Deposit {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if updateType == types.Withdrawal || updateType == types.Transfer {
success, successPerUpdate = k.checkDepositConstraints(settledUpdates)
if !success {
return success, successPerUpdate, nil
}
if updateType == types.Withdrawal || updateType == types.Transfer || updateType == types.Deposit {
success, successPerUpdate = k.checkDepositConstraints(settledUpdates)
if !success {
return success, successPerUpdate, nil
}
🤖 Prompt for AI Agents
In protocol/x/subaccounts/keeper/subaccount.go around lines 583 to 588, the
deposit minimum check is only applied for Withdrawal and Transfer updates so
Deposit updates bypass the MIN_SUBACCOUNT_INITIAL_DEPOSIT_QUANTUMS gate; include
types.Deposit in the conditional (or otherwise invoke k.checkDepositConstraints
when updateType == types.Deposit) so deposit updates run the same check, return
the same success, successPerUpdate, nil on failure, and preserve existing
control flow.

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

♻️ Duplicate comments (1)
protocol/x/subaccounts/keeper/subaccount.go (1)

587-587: Deposit updates bypass the minimum deposit constraint.

The checkDepositConstraints function is only invoked for Withdrawal and Transfer update types. Direct Deposit updates to new subaccounts bypass this gate entirely, allowing users to open fresh accounts with sub-0.1 USDC deposits and defeating the purpose of MIN_SUBACCOUNT_INITIAL_DEPOSIT_QUANTUMS.

Apply this diff to include deposits in the check:

-	if updateType == types.Withdrawal || updateType == types.Transfer {
+	if updateType == types.Withdrawal || updateType == types.Transfer || updateType == types.Deposit {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5ccf442 and 888f1f6.

📒 Files selected for processing (1)
  • protocol/x/subaccounts/keeper/subaccount.go (2 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2024-11-15T16:00:11.304Z
Learnt from: hwray
Repo: dydxprotocol/v4-chain PR: 2551
File: protocol/x/subaccounts/keeper/subaccount.go:852-865
Timestamp: 2024-11-15T16:00:11.304Z
Learning: The function `GetCrossInsuranceFundBalance` in `protocol/x/subaccounts/keeper/subaccount.go` already existed and was just moved in this PR; changes to its error handling may be out of scope.

Applied to files:

  • protocol/x/subaccounts/keeper/subaccount.go
📚 Learning: 2024-11-15T15:59:28.095Z
Learnt from: hwray
Repo: dydxprotocol/v4-chain PR: 2551
File: protocol/x/subaccounts/keeper/subaccount.go:833-850
Timestamp: 2024-11-15T15:59:28.095Z
Learning: The function `GetInsuranceFundBalance` in `protocol/x/subaccounts/keeper/subaccount.go` already existed and was just moved in this PR; changes to its error handling may be out of scope.

Applied to files:

  • protocol/x/subaccounts/keeper/subaccount.go
📚 Learning: 2024-11-15T16:17:29.092Z
Learnt from: hwray
Repo: dydxprotocol/v4-chain PR: 2551
File: protocol/x/clob/types/expected_keepers.go:86-90
Timestamp: 2024-11-15T16:17:29.092Z
Learning: The function `GetCrossInsuranceFundBalance` in `protocol/x/clob/types/expected_keepers.go` already existed and was just moved in this PR; changes to its error handling may be out of scope.

Applied to files:

  • protocol/x/subaccounts/keeper/subaccount.go
📚 Learning: 2025-10-06T20:00:48.549Z
Learnt from: anmolagrawal345
Repo: dydxprotocol/v4-chain PR: 3079
File: protocol/x/clob/types/leverage.go:10-56
Timestamp: 2025-10-06T20:00:48.549Z
Learning: In the leverage update flow for protocol/x/clob, the `ValidateUpdateLeverageMsg` function performs basic validation (non-nil checks, non-zero leverage, unique IDs, valid clob pair existence), while maximum leverage validation against `GetMaxLeverageForPerpetual` is intentionally deferred to the `UpdateLeverage` keeper method.

Applied to files:

  • protocol/x/subaccounts/keeper/subaccount.go
🧬 Code graph analysis (1)
protocol/x/subaccounts/keeper/subaccount.go (3)
protocol/x/subaccounts/types/settled_update.go (1)
  • SettledUpdate (7-17)
protocol/x/subaccounts/types/update.go (3)
  • UpdateResult (10-10)
  • MIN_SUBACCOUNT_INITIAL_DEPOSIT_QUANTUMS (163-163)
  • ViolatesDepositConstraints (75-75)
protocol/x/assets/types/genesis.go (1)
  • AssetUsdc (12-19)
⏰ 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). (4)
  • GitHub Check: test-sim-multi-seed-short
  • GitHub Check: test-race
  • GitHub Check: container-tests
  • GitHub Check: Summary
🔇 Additional comments (2)
protocol/x/subaccounts/keeper/subaccount.go (2)

551-551: Resolve the TODO or remove if no longer applicable.

The TODO suggests uncertainty about whether this constraint breaks existing valid flows. Please verify that this implementation doesn't inadvertently block legitimate use cases, or remove the TODO if validation is complete.


593-654: Code organization looks good.

The deposit constraint check is properly sequenced before the withdrawal/transfer blocking logic, maintaining a clear validation flow.


for i, u := range settledUpdates {
for _, assetUpdate := range u.AssetUpdates {
// Reject deposit if the account the account is new (no perp position, no usdc position) and the deposit/transfer
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix typo in comment.

The comment contains a repeated phrase.

Apply this diff:

-			// Reject deposit if the account the account is new (no perp position, no usdc position) and the deposit/transfer
+			// Reject deposit if the account is new (no perp position, no usdc position) and the deposit/transfer
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Reject deposit if the account the account is new (no perp position, no usdc position) and the deposit/transfer
// Reject deposit if the account is new (no perp position, no usdc position) and the deposit/transfer
🤖 Prompt for AI Agents
In protocol/x/subaccounts/keeper/subaccount.go around line 549, the comment
currently reads "Reject deposit if the account the account is new..." with a
duplicated phrase; remove the extra "the account" so the comment becomes "Reject
deposit if the account is new (no perp position, no usdc position) and the
deposit/transfer" to fix the typo and restore clarity.

Comment on lines +552 to +558
if assetUpdate.AssetId == assettypes.AssetUsdc.Id &&
assetUpdate.BigQuantumsDelta.Cmp(big.NewInt(types.MIN_SUBACCOUNT_INITIAL_DEPOSIT_QUANTUMS)) < 0 &&
u.SettledSubaccount.GetUsdcPosition().Sign() == 0 &&
len(u.SettledSubaccount.PerpetualPositions) == 0 {
successPerUpdate[i] = types.ViolatesDepositConstraints
success = false
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add positive delta check for clarity and efficiency.

The comparison assetUpdate.BigQuantumsDelta.Cmp(...) < 0 will evaluate true for any negative delta (e.g., withdrawals). While withdrawals from new accounts would fail elsewhere, checking negative deltas here wastes gas and reduces clarity. The constraint should only apply to incoming deposits/transfers (positive deltas).

Apply this diff:

 			if assetUpdate.AssetId == assettypes.AssetUsdc.Id &&
+				assetUpdate.BigQuantumsDelta.Sign() > 0 &&
 				assetUpdate.BigQuantumsDelta.Cmp(big.NewInt(types.MIN_SUBACCOUNT_INITIAL_DEPOSIT_QUANTUMS)) < 0 &&
 				u.SettledSubaccount.GetUsdcPosition().Sign() == 0 &&
 				len(u.SettledSubaccount.PerpetualPositions) == 0 {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if assetUpdate.AssetId == assettypes.AssetUsdc.Id &&
assetUpdate.BigQuantumsDelta.Cmp(big.NewInt(types.MIN_SUBACCOUNT_INITIAL_DEPOSIT_QUANTUMS)) < 0 &&
u.SettledSubaccount.GetUsdcPosition().Sign() == 0 &&
len(u.SettledSubaccount.PerpetualPositions) == 0 {
successPerUpdate[i] = types.ViolatesDepositConstraints
success = false
}
if assetUpdate.AssetId == assettypes.AssetUsdc.Id &&
assetUpdate.BigQuantumsDelta.Sign() > 0 &&
assetUpdate.BigQuantumsDelta.Cmp(big.NewInt(types.MIN_SUBACCOUNT_INITIAL_DEPOSIT_QUANTUMS)) < 0 &&
u.SettledSubaccount.GetUsdcPosition().Sign() == 0 &&
len(u.SettledSubaccount.PerpetualPositions) == 0 {
successPerUpdate[i] = types.ViolatesDepositConstraints
success = false
}
🤖 Prompt for AI Agents
In protocol/x/subaccounts/keeper/subaccount.go around lines 552 to 558, the
current check applies the minimum-initial-deposit constraint to any negative
delta as well as positive ones; change it to only run for positive deltas to
improve clarity and gas (first check assetUpdate.BigQuantumsDelta.Sign() > 0,
then compare with types.MIN_SUBACCOUNT_INITIAL_DEPOSIT_QUANTUMS), keeping the
rest of the condition the same and short-circuiting early to avoid unnecessary
Cmp calls for withdrawals.

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

Labels

Development

Successfully merging this pull request may close these issues.

2 participants