Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions cadence/contracts/FlowALPv1.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -1613,8 +1613,10 @@ access(all) contract FlowALPv1 {
return self.paused
}

/// Returns whether liquidations are paused - liquidations have an additional warmup after a global pause,
/// to allow users time to improve position health and avoid liquidation.
/// Returns whether withdrawals and liquidations are paused.
/// Both have a warmup period after a global pause is ended, to allow users time to improve position health and avoid liquidation.
/// The warmup period provides an opportunity for users to deposit to unhealthy positions before liquidations start,
/// and also disallows withdrawing while liquidations are disabled, because liquidations can be needed to satisfy withdrawal requests.
access(all) view fun isPausedOrWarmup(): Bool {
if self.paused {
return true
Expand Down Expand Up @@ -2857,10 +2859,10 @@ access(all) contract FlowALPv1 {
///
/// Callers should be careful that the withdrawal does not put their position under its target health,
/// especially if the position doesn't have a configured `topUpSource` from which to repay borrowed funds
// in the event of undercollaterlization.
/// in the event of undercollaterlization.
access(EPosition) fun withdraw(pid: UInt64, amount: UFix64, type: Type): @{FungibleToken.Vault} {
pre {
!self.isPaused(): "Withdrawal, deposits, and liquidations are paused by governance"
!self.isPausedOrWarmup(): "Withdrawals are paused by governance"
}
// Call the enhanced function with pullFromTopUpSource = false for backward compatibility
return <- self.withdrawAndPull(
Expand All @@ -2884,7 +2886,7 @@ access(all) contract FlowALPv1 {
pullFromTopUpSource: Bool
): @{FungibleToken.Vault} {
pre {
!self.isPaused(): "Withdrawal, deposits, and liquidations are paused by governance"
!self.isPausedOrWarmup(): "Withdrawals are paused by governance"
self.positions[pid] != nil:
"Invalid position ID \(pid) - could not find an InternalPosition with the requested ID in the Pool"
self.globalLedger[type] != nil:
Expand Down Expand Up @@ -3431,6 +3433,10 @@ access(all) contract FlowALPv1 {
} else if balanceSheet.health > position.targetHealth {
// The position is overcollateralized,
// we'll withdraw funds to match the target health and offer it to the sink.
if self.isPausedOrWarmup() {
// Withdrawals (including pushing to the drawDownSink) are disabled during the warmup period
return
}
if let drawDownSink = position.drawDownSink {
let drawDownSink = drawDownSink as auth(FungibleToken.Withdraw) &{DeFiActions.Sink}
let sinkType = drawDownSink.getSinkType()
Expand Down
25 changes: 21 additions & 4 deletions cadence/tests/pool_pause_test.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ fun test_pool_pause_deposit_withdrawal() {
// Pause the pool
let pauseRes = setPoolPauseState(signer: PROTOCOL_ACCOUNT, pause: true)
Test.expect(pauseRes, Test.beSucceeded())
let pauseEvents = Test.eventsOfType(Type<FlowALPv1.PoolPaused>())
Test.expect(pauseEvents.length, Test.equal(1))
// ---------------------------------------------------------

// Can't deposit to an existing position
Expand All @@ -69,7 +71,7 @@ fun test_pool_pause_deposit_withdrawal() {
// Can't withdraw from existing position
let withdrawRes = _executeTransaction(
"./transactions/position-manager/withdraw_from_position.cdc",
[0, FLOW_TOKEN_IDENTIFIER, 50.0, false],
[0, FLOW_TOKEN_IDENTIFIER, initialDepositAmount/2.0, false],
user1
)
Test.expect(withdrawRes, Test.beFailed())
Expand All @@ -86,6 +88,8 @@ fun test_pool_pause_deposit_withdrawal() {
// Unpause the pool
let unpauseRes = setPoolPauseState(signer: PROTOCOL_ACCOUNT, pause: false)
Test.expect(unpauseRes, Test.beSucceeded())
let unpauseEvents = Test.eventsOfType(Type<FlowALPv1.PoolUnpaused>())
Test.expect(unpauseEvents.length, Test.equal(1))
// ---------------------------------------------------------

// Depositing to position should now succeed
Expand All @@ -96,13 +100,13 @@ fun test_pool_pause_deposit_withdrawal() {
)
Test.expect(depositRes2, Test.beSucceeded())

// Withdrawing from position should now succeed
// Withdrawing from position should still fail during warmup period
let withdrawRes2 = _executeTransaction(
"./transactions/position-manager/withdraw_from_position.cdc",
[0 as UInt64, FLOW_TOKEN_IDENTIFIER, 50.0, false],
[0 as UInt64, FLOW_TOKEN_IDENTIFIER, initialDepositAmount/2.0, false],
user1
)
Test.expect(withdrawRes2, Test.beSucceeded())
Test.expect(withdrawRes2, Test.beFailed())

// Creating new position (for user2) should now succeed
let openRes2 = _executeTransaction(
Expand All @@ -112,4 +116,17 @@ fun test_pool_pause_deposit_withdrawal() {
)
Test.expect(openRes2, Test.beSucceeded())

// Wait for the warmup period to end (the default warmupSec of FlowALPv1.Pool is 300.0)
Test.moveTime(by: Fix64(300.0))
Copy link
Member

Choose a reason for hiding this comment

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

nit:

I think the most ideal way to get this number is actually from the unpause event. If that's not easy to do in the testing setup currently, can at least reference where this number comes from, i.e. here in the defaults

// ---------------------------------------------------------

// Withdrawing from position should now succeed
let withdrawRes3 = _executeTransaction(
"./transactions/position-manager/withdraw_from_position.cdc",
[0 as UInt64, FLOW_TOKEN_IDENTIFIER, initialDepositAmount/2.0, false],
user1
)
Test.expect(withdrawRes3, Test.beSucceeded())


}
Loading