Skip to content

Comments

Merge PR #14 (Test Infrastructure) into refactored pool creation branch#19

Merged
kgrgpg merged 64 commits intotracer-bulletfrom
merge-pr14-to-gio-refactor
Jun 25, 2025
Merged

Merge PR #14 (Test Infrastructure) into refactored pool creation branch#19
kgrgpg merged 64 commits intotracer-bulletfrom
merge-pr14-to-gio-refactor

Conversation

@kgrgpg
Copy link
Contributor

@kgrgpg kgrgpg commented Jun 23, 2025

This PR merges the test infrastructure from PR #14 into the refactored pool creation branch.

Changes Included

Important Notes

Conflicts Resolved

  • test_helpers.cdc - Kept the defaultTokenIdentifier constant
  • TidalProtocol.cdc - Used the refactored version (no functional changes needed)

Test Results

All 9 test suites pass successfully:

  • ✅ auto_borrow_behavior_test.cdc
  • ✅ platform_integration_test.cdc
  • ✅ pool_creation_workflow_test.cdc
  • ✅ position_lifecycle_happy_test.cdc
  • ✅ rebalance_overcollateralised_test.cdc
  • ✅ rebalance_undercollateralised_test.cdc
  • ✅ reserve_withdrawal_test.cdc
  • ✅ token_governance_addition_test.cdc
  • ✅ zero_debt_withdrawal_test.cdc

Supersedes PR #14

kgrgpg and others added 30 commits June 4, 2025 19:10
- TidalProtocol: 100% restoration of Dieter's AlpenFlow functionality
  - Oracle-based pricing and health calculations
  - Deposit rate limiting and position queues
  - Advanced health management functions
  - Async position updates
- MOET: Mock stablecoin for multi-token testing
- TidalPoolGovernance: Role-based governance system
- AlpenFlow_dete_original: Reference implementation

Note: Old tests removed as they're incompatible with new contracts.
Updated tests coming in follow-up PR.

No breaking changes. Foundation for multi-token lending protocol.
Kay-Zee and others added 12 commits June 19, 2025 15:56
- Added expected debt calculations and assertions in both overcollateralized and undercollateralized rebalance tests
- For overcollateralized: Assert that after price increase and rebalance, debt = effective_collateral / target_health (≈738.46 MOET)
- For undercollateralized: Assert that after price drop and rebalance, debt is reduced by the exact amount needed to restore target health (≈516.92 MOET)
- Added debug logging to verify calculations match actual values
- All tests now pass with precise value assertions as requested in PR review
…flow/TidalProtocol into cadence-testing-patterns-fork

# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
- Fixes fundsAvailableAboveTargetHealthAfterDepositing to handle zero debt case properly
- When there's no debt, use entire collateral amount instead of health ratio calculation
- Fixes repay_and_close_position.cdc to use availableBalance() instead of hardcoded 1000.0
- This resolves the issue where withdrawing all collateral caused health to drop to 1.0 (below minimum 1.1)
- Tests that when a position has no debt, availableBalance() returns full collateral amount
- Regression test for the bug where zero debt positions couldn't withdraw (availableBalance returned 0)
- Verifies the conditional logic: when effectiveDebtAfterDeposit == 0.0, use effectiveCollateralAfterDeposit
* update PositionDetails and PositionBalance comments and fields

* consolidate public methods to the bottom of the contract

* remove comment references to restored functionality

* update contract comments

* update contract comments and reorganize for clarity

* update contract and construct internal methods

* update contract comments

* revert changes to PositionDetails balances field

* update contract comments

* Update cadence/contracts/TidalProtocol.cdc

Co-authored-by: Joshua Hannan <joshua.hannan@flowfoundation.org>

* update interest index & balance type comments

---------

Co-authored-by: Joshua Hannan <joshua.hannan@flowfoundation.org>
Resolved conflicts by accepting structural changes from gio/refactor-pool-creation-updated branch,
which already includes all bug fixes from PR #14:
- Zero debt fix for fundsAvailableAboveTargetHealthAfterDepositing
- Division by zero fix in healthComputation
- Improved internal function naming with _ prefix
- Event emissions (Opened, Deposited, Withdrawn, Rebalanced)
- Better code organization and error messages

Preserved test infrastructure and documentation from PR #14
…references

Fixed all test files and scripts to use the correct field name 'vaultType' instead of 'type' when accessing PositionBalance struct fields. This aligns with the struct definition in TidalProtocol.cdc and resolves test failures.

Updated files:
- cadence/scripts/tidal-protocol/get_position_details.cdc
- cadence/transactions/tidal-protocol/pool-management/repay_and_close_position.cdc
- cadence/tests/auto_borrow_behavior_test.cdc
- cadence/tests/rebalance_overcollateralised_test.cdc
- cadence/tests/rebalance_undercollateralised_test.cdc

All tests now pass successfully.
@sisyphusSmiling
Copy link
Contributor

Using this comment to reference the several comments made in #14 and avoid reiterating them line by line here. @kgrgpg can you confirm when those comments have been addressed please?

Critical fixes:
- Fixed DFBUtils naming conflict by using real DFBUtils from DeFiBlocks
- Removed incompatible emulator addresses from flow.json
- Made testing addresses consistent with 0x prefix

Best practice improvements:
- Moved repay_and_close_position.cdc to tests directory
- Refactored transaction to use prepare/execute blocks
- Applied principle of least privilege (BorrowValue authorization)
- Removed test logging from transaction
- Added specific MOET balance assertion in position_lifecycle_happy_test
- Removed unused get_position_details.cdc script

All tests passing successfully
@kgrgpg
Copy link
Contributor Author

kgrgpg commented Jun 23, 2025

✅ Addressed all review comments from PR #14

I've implemented all the feedback from @sisyphusSmiling on PR #14:

Critical Issues Fixed:

  • DFBUtils naming conflict: Now using the real DFBUtils from DeFiBlocks instead of the mock
  • flow.json emulator addresses: Removed incompatible emulator addresses
  • Testing addresses: Made all testing addresses consistent with 0x prefix

Best Practice Improvements:

  • Transaction refactoring:
    • Moved repay_and_close_position.cdc to tests directory
    • Applied prepare/execute pattern
    • Using least privilege authorization (BorrowValue)
    • Removed test logging
  • Test improvements: Added specific MOET balance assertion (~615.38) in position_lifecycle_happy_test
  • Cleanup: Removed unused get_position_details.cdc script

Test Results:

All 9 test suites pass ✅

This PR is now ready for review with all the improvements from PR #14's feedback incorporated.

@kgrgpg kgrgpg requested a review from sisyphusSmiling June 23, 2025 19:48
Comment on lines +75 to +77
// Verify the amount is approximately what we calculated (within 0.01 tolerance)
Test.assert(moetBalance >= expectedDebt - 0.01 && moetBalance <= expectedDebt + 0.01,
message: "Expected MOET debt to be approximately \(expectedDebt), but got \(moetBalance)")
Copy link
Contributor

Choose a reason for hiding this comment

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

Could also check the user's account state to ensure the funds actually made their way to the user's MOET Vault with the helper getBalance(address: user.address, vaultPublicPath: MOET.VaultPublicPath). That would ensure the minted funds are actually deposited to the drawDownSink and not just adjusted within the protocol balances.

Comment on lines +119 to +120
Test.assert(!hasMoetBalance,
message: "Should not have MOET balance when no auto-borrowing occurs")
Copy link
Contributor

Choose a reason for hiding this comment

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

Same note about checking user account state to ensure no funds were deposited without incrementing locally. Another check on this might be validating against the MOET totalSupply to ensure that no tokens were minted.

let flowBalanceBefore = getBalance(address: user.address, vaultPublicPath: /public/flowTokenReceiver)!
log("Flow balance BEFORE repay: ".concat(flowBalanceBefore.toString()))

/* --- NEW: repay MOET and close position --- */
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/* --- NEW: repay MOET and close position --- */
// repay MOET and close position

Comment on lines +70 to +71
Test.assert(healthAfterPriceChange > healthBefore) // got healthier due to price increase
Test.assert(healthAfterRebalance < healthAfterPriceChange) // health decreased after drawing down excess collateral
Copy link
Contributor

Choose a reason for hiding this comment

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

These directional checks make sense. Are we able to assert either the exact or minimum expected healthAfterPriceChange? I would think it should at least be greater than the minimum health if not the target health.

}

let tolerance: UFix64 = 0.01
Test.assert((actualDebt >= expectedDebt - tolerance) && (actualDebt <= expectedDebt + tolerance))
Copy link
Contributor

Choose a reason for hiding this comment

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

Noting again regarding checking user account state. I'm assuming we'd also want to ensure the MOET was actually pushed to the drawDownSink which in the position setup is configured to direct borrowed funds to their MOET Vault.

// drop price
setMockOraclePrice(signer: protocolAccount, forTokenIdentifier: flowTokenIdentifier, price: initialPrice * (1.0 - priceDropPct))

let availableAfterPriceChange = getAvailableBalance(pid: 0, vaultIdentifier: defaultTokenIdentifier, pullFromTopUpSource: true, beFailed: false)
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see this value used anywhere. It'd be good to include at least a directional if not exact assertion on the available balance after the price adjustment.

let healthAfterPriceChangeVal: UFix64 = healthAfterPriceChange
let target: UFix64 = 1.3

let requiredPaydown: UFix64 = (target - healthAfterPriceChangeVal) * effectiveCollateralAfterDrop / (target * target)
Copy link
Contributor

Choose a reason for hiding this comment

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

A comment on this calculation would be helpful

Comment on lines +87 to +90
log("Health after price change: ".concat(healthAfterPriceChange.toString()))
log("Required paydown: ".concat(requiredPaydown.toString()))
log("Expected debt: ".concat(expectedDebt.toString()))
log("Actual debt: ".concat(actualDebt.toString()))
Copy link
Contributor

Choose a reason for hiding this comment

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

We'll want to also ensure that the debt was actually repaid and not just altered in the ledger. Checking user's MOET balance as well as the available balance via the position IMO are important for this test case as well.

- auto_borrow_behavior_test: verify user MOET vault receives borrowed funds
- auto_borrow_behavior_test: ensure no MOET minted when pushToDrawDownSink=false
- position_lifecycle_happy_test: update comment style from /* */ to //
- rebalance_overcollateralised_test: add minimum health assertion and user balance check
- rebalance_undercollateralised_test: add explanatory comment for paydown calculation

All tests passing successfully.
@kgrgpg
Copy link
Contributor Author

kgrgpg commented Jun 24, 2025

✅ All review comments have been addressed

Thank you for the thorough review @sisyphusSmiling! I've implemented all the suggested improvements to enhance test coverage. Here's what was done:

auto_borrow_behavior_test.cdc

  • Line 75-77: Added verification that borrowed MOET funds actually reach the user's MOET Vault through the drawDownSink
  • Line 119-120: Added checks to ensure no MOET is minted to user's account when pushToDrawDownSink=false

position_lifecycle_happy_test.cdc

  • ✅ Changed comment style from /* --- NEW: repay MOET and close position --- */ to // repay MOET and close position

rebalance_overcollateralised_test.cdc

  • Line 70-71: Added assertion that healthAfterPriceChange >= 1.5 to verify minimum expected health after price increase
  • ✅ Added verification that the borrowed MOET after rebalance reaches the user's Vault

rebalance_undercollateralised_test.cdc

  • Available balance check: Already present - verifies availableAfterPriceChange < availableBeforePriceChange
  • Required paydown calculation: Added explanatory comment describing the formula derivation
  • User balance check: Already present - verifies that MOET was actually pulled from user's Vault during paydown

All tests are passing successfully. The enhanced assertions ensure that funds are properly transferred to/from user accounts and not just adjusted in the protocol's internal accounting.

Copy link
Contributor

@sisyphusSmiling sisyphusSmiling 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 couple smaller suggestions and callouts

Test.assert(health >= 1.29 && health <= 1.31,
message: "Expected health to be at target (1.3), but got \(health)")

// NEW: Verify the user actually received the borrowed MOET in their Vault (draw-down sink)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// NEW: Verify the user actually received the borrowed MOET in their Vault (draw-down sink)
// Verify the user actually received the borrowed MOET in their Vault (draw-down sink)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

27ed50e Fixed

Test.assert(!hasMoetBalance,
message: "Should not have MOET balance when no auto-borrowing occurs")

// NEW: Ensure user's MOET balance remains unchanged (i.e. no tokens minted)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// NEW: Ensure user's MOET balance remains unchanged (i.e. no tokens minted)
// Ensure user's MOET balance remains unchanged (i.e. no tokens minted)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

27ed50e Fixed


let healthAfterPriceChange = getPositionHealth(pid: 0, beFailed: false)

// NEW: After a 20% price increase, health should be at least 1.5 (=960/615.38)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// NEW: After a 20% price increase, health should be at least 1.5 (=960/615.38)
// After a 20% price increase, health should be at least 1.5 (=960/615.38)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

27ed50e Fixed

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should also add cases including coverage for more extreme price changes such as 100% and 1,000%. The 10x increase on the demo call earlier didn't seem to rebalance to the target health and was still undercollateralized, but needs test case validation and coverage.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is indeed important. But similar tests where parameter values need to be changed fall more under fuzzy tests category. Will target these in future tests/PRs.

Copy link
Contributor

Choose a reason for hiding this comment

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

Same callout as the undercollateralized test file - it'd be helpful to cover more extreme price changes up to an order of magnitude.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is indeed important. But similar tests where parameter values need to be changed fall more under fuzzy tests category. Will target these in future tests/PRs.

Copy link
Contributor

Choose a reason for hiding this comment

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

Calling out that this is a framing and doesn't actually test anything.

Copy link
Contributor

Choose a reason for hiding this comment

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

Calling out that this is a a TODO and isn't actually executing anything meaningful.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is indeed as you mentioned.

Here’s the current situation for the reserve-withdrawal flow:

  1. cadence/tests/reserve_withdrawal_test.cdc
    • The file only sets up a pool and then calls the reserve-withdrawal transaction, but it has no assertions beyond a Test.expect that the transaction “succeeds”.
    • We created the scaffolding early to verify that the transaction script itself type-checks and can be signed/executed by the governance signer, but we haven’t added behavioural checks (e.g. before/after reserve balance, events, or account state).
    • Essentially it’s a placeholder so the repo builds green while governance-withdraw logic is still in flux.

  2. cadence/transactions/tidal-protocol/pool-governance/withdraw_reserve.cdc
    • The transaction currently just imports the protocol and calls TidalProtocol.withdrawReserve(...) with hard-coded/placeholder arguments.
    • Internally that call is still a TODO—the governance module will eventually restrict who may withdraw, emit ReserveWithdrawn events, and move tokens to the treasury multisig.
    • Until that implementation lands, the transaction is merely a stub to keep the on-chain interface stable for front-end work.

Why we left it this way

  • Governance module: we’re finalising the access-control and timelock design. Implementing the full withdrawal path now would require re-work once those decisions settle.
  • Continuous integration: the empty test guarantees that the stub transaction compiles after every change to contracts or imports. If the protocol, token identifiers, or helper functions change, CI will catch it because this test runs.

Next steps (tracked in #42)

  1. Implement PoolGovernance.withdrawReserve with:
    • role-based access (GovernanceAdmin)
    • event emission (ReserveWithdrawn)
    • balance checks against the pool’s accounting
  2. Flesh out withdraw_reserve.cdc to pass amount/receiver params.
  3. Expand reserve_withdrawal_test.cdc to:
    • Assert pre-/post-reserve balances
    • Verify event fields
    • Ensure only authorised signer can call the tx.

Until those pieces are complete the test remains a “framing” check rather than a behavioural test.


rebalancePosition(signer: protocolAccount, pid: 0, force: true, beFailed: false)

let healthAfterRebalance = getPositionHealth(pid: 0, beFailed: false)
Copy link
Contributor

Choose a reason for hiding this comment

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

I realized after approving that we don't assert that the health post-rebalance is above the minimum health. We should add that here and add a more extreme price drop rebalance scenario to ensure the protocol is rebalancing to at least the minimum health @kgrgpg.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added assertion. Extreme price drop will be implemented with fuzzy tests c6c8350

kgrgpg added 2 commits June 25, 2025 18:19
Clean up comment style by removing NEW: prefixes while maintaining
the same functionality and test coverage.
Per PR review, add assertion that health after rebalance is at least 1.1 (minimum threshold)
to ensure protocol rebalances positions to a safe state.
@kgrgpg
Copy link
Contributor Author

kgrgpg commented Jun 25, 2025

Closing since #19 (that was created with transfer of work) is ready to be merged

Base automatically changed from gio/refactor-pool-creation-updated to tracer-bullet June 25, 2025 18:47
@kgrgpg kgrgpg merged commit cc1b734 into tracer-bullet Jun 25, 2025
1 check passed
@kgrgpg kgrgpg deleted the merge-pr14-to-gio-refactor branch June 25, 2025 21:34
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.

3 participants