diff --git a/cadence/contracts/FlowALPv1.cdc b/cadence/contracts/FlowALPv1.cdc index ec6ed8b..9afb8bc 100644 --- a/cadence/contracts/FlowALPv1.cdc +++ b/cadence/contracts/FlowALPv1.cdc @@ -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 @@ -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( @@ -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: @@ -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() diff --git a/cadence/tests/pool_pause_test.cdc b/cadence/tests/pool_pause_test.cdc index 61d2bb3..a5be56c 100644 --- a/cadence/tests/pool_pause_test.cdc +++ b/cadence/tests/pool_pause_test.cdc @@ -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()) + Test.expect(pauseEvents.length, Test.equal(1)) // --------------------------------------------------------- // Can't deposit to an existing position @@ -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()) @@ -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()) + Test.expect(unpauseEvents.length, Test.equal(1)) // --------------------------------------------------------- // Depositing to position should now succeed @@ -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( @@ -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)) + // --------------------------------------------------------- + + // 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()) + + }