Skip to content

Add minimum deposit balance#144

Merged
Kay-Zee merged 13 commits intomainfrom
kan/position-balance-limit
Feb 10, 2026
Merged

Add minimum deposit balance#144
Kay-Zee merged 13 commits intomainfrom
kan/position-balance-limit

Conversation

@Kay-Zee
Copy link
Copy Markdown
Member

@Kay-Zee Kay-Zee commented Feb 7, 2026

Closes: #143

Description

Re-enforces a minimum balance on a position.


For contributor use:

  • Targeted PR against master branch
  • Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
  • Code follows the standards mentioned here.
  • Updated relevant documentation
  • Re-reviewed Files changed in the Github PR explorer
  • Added appropriate labels

// open wrapped position (pushToDrawDownSink)
let openRes = executeTransaction(
"../transactions/flow-credit-market/position/create_position.cdc",
[minimum-0.1, FLOW_VAULT_STORAGE_PATH, true],
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

maybe continue to verify the same tx with minimum amount would pass

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

i do now continue with a successful create position

@Kay-Zee Kay-Zee marked this pull request as ready for review February 9, 2026 17:29
@Kay-Zee Kay-Zee requested a review from a team as a code owner February 9, 2026 17:29
Comment thread cadence/contracts/FlowCreditMarket.cdc Outdated
Comment on lines +2994 to +3002
// Ensure that the remaining balance meets the minimum requirement (or is zero)
let balanceRecord = position.balances[type]!
let interestIndex = balanceRecord.direction == BalanceDirection.Credit
? tokenState.creditInterestIndex
: tokenState.debitInterestIndex
let remainingBalance = FlowCreditMarket.scaledBalanceToTrueBalance(
balanceRecord.scaledBalance,
interestIndex: interestIndex
)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There's already a function PositionView.trueBalance which pretty much does this. Maybe reuse it

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

the main argument against using positionView here was that it technically copies all balances, to create the new view. With that said, a position is unlikely to have too many different balances, so maybe that won't really be an "impactful" optimization. Will change implementation, but make a note

Comment thread cadence/contracts/FlowCreditMarket.cdc
Comment thread cadence/contracts/FlowCreditMarket.cdc Outdated
Comment thread cadence/contracts/FlowCreditMarket.cdc Outdated
Comment thread cadence/contracts/FlowCreditMarket.cdc Outdated
Comment on lines +3007 to +3012
let minimumRequired = UFix128(tokenState.minimumTokenBalancePerPosition)

assert(
remainingBalance == 0.0 || remainingBalance >= minimumRequired,
message: "Withdrawal would leave position below minimum balance requirement of \(minimumRequired). Remaining balance would be \(remainingBalance)."
)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
let minimumRequired = UFix128(tokenState.minimumTokenBalancePerPosition)
assert(
remainingBalance == 0.0 || remainingBalance >= minimumRequired,
message: "Withdrawal would leave position below minimum balance requirement of \(minimumRequired). Remaining balance would be \(remainingBalance)."
)
assert(
positionSatisfiesMinimumCreditBalance(type, remainingBalance)
message: "Withdrawal would leave position below minimum balance requirement of \(minimumRequired). Remaining balance would be \(remainingBalance)."
)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

added, though i did have to do some type conversions. I think still worth though.

// Amount should now be exactly the minimum, so withdrawal should fail
let withdrawResFail = _executeTransaction(
"./transactions/position-manager/withdraw_from_position.cdc",
[positionId, FLOW_TOKEN_IDENTIFIER, amountAboveMin, true],
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
[positionId, FLOW_TOKEN_IDENTIFIER, amountAboveMin, true],
[positionId, FLOW_TOKEN_IDENTIFIER, minimum-0.1, true],

To make sure we are withdrawing a amount smaller than current balance, independent of amountAboveMin

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

will adjust, but going to just div by 2

Comment thread cadence/tests/test_helpers.cdc Outdated
Comment thread cadence/tests/test_helpers.cdc Outdated
Comment thread docs/deposit_capacity_mechanism.md Outdated
Comment on lines +356 to +357
1. **Set minimums based on token economics**: Higher-value tokens may need lower minimums in token units (e.g., 0.1 ETH) while lower-value tokens need higher minimums (e.g., 100 USDC)
2. **Consider gas/transaction costs**: Minimum should be high enough that position operations are economically justified
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Higher-value tokens may need lower minimums in token units

They just do need lower minimums, there is no may. :)

I think what we want to say is:

  • Conceptually the minimum balance should reflect a minimum balance value, denominated in the default token ($ / MOET). We have implemented this as a per-token balance, denominated in the token itself, because it is easier to implement and reason about.
    • Possibly there are per-token reasons why the minim value should be higher or lower (like volatility), but the dominating factor is that we need the dollar value of the balance to exceed some threshold

And governance should:

  • Pick a dollar-denominated minimum balance
  • Set each token minimum so that it equates to the dollar-denominated minimum balance, maybe with an added buffer for expected volatility
  • Periodically update the token minimums to match the dollar-denominated minimum (this could be automated)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

did another pass with you suggestions

Kay-Zee and others added 6 commits February 9, 2026 19:05
Co-authored-by: Jordan Schalm <jordan.schalm@gmail.com>
Co-authored-by: Jordan Schalm <jordan.schalm@gmail.com>
Co-authored-by: Jordan Schalm <jordan.schalm@gmail.com>
Co-authored-by: Jordan Schalm <jordan.schalm@gmail.com>
Copy link
Copy Markdown
Contributor

@nialexsan nialexsan left a comment

Choose a reason for hiding this comment

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

just a few nits

Comment thread cadence/contracts/FlowCreditMarket.cdc Outdated
/// @param balance: The balance amount to validate
/// @return true if the balance meets or exceeds the minimum requirement, false otherwise
access(self) view fun positionSatisfiesMinimumBalance(type: Type, balance: UFix128): Bool {
return balance >= UFix128(self.globalLedger[type]!.minimumTokenBalancePerPosition)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I have a slight preference toward including the empty balance case here, so that this validator function captures the full range of valid balances.

return balance == 0.0 || balance >= UFix128(self.globalLedger[type]!.minimumTokenBalancePerPosition)

Then we can simplify the assertion on line 3014:

assert(self.positionSatisfiesMinimumBalance(type: type, balance: remainingBalance), ...)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

my main problem with doing that was that technically, that would make this function "less" useful for the create position case. Since for that case, the balance == 0.0 is not a "true" case. Also, if i was to include the balance check, i'd probably also change the name of the function, as 0.0 obviously does NOT satisfy the minimum balance.

I think i'd still prefer to keep the balance == 0.0 check outside this function as it only applies in one of the locations where this function is used.

liobrasil and others added 2 commits February 10, 2026 18:11
Co-authored-by: Jordan Schalm <jordan.schalm@gmail.com>
Co-authored-by: Alex <12097569+nialexsan@users.noreply.github.com>
@Kay-Zee Kay-Zee merged commit 106b87f into main Feb 10, 2026
1 check passed
@Kay-Zee Kay-Zee deleted the kan/position-balance-limit branch February 10, 2026 23:29
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.

Enforce a minimum balance per position

6 participants