Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
44854d6
rebalance test edge cases
nialexsan Feb 23, 2026
d4c07bb
Merge branch 'main' into nialexsan/dust-debug-flowalp
nialexsan Feb 24, 2026
87f1fe4
close position
nialexsan Feb 25, 2026
94e6013
mock swap rounding
nialexsan Feb 25, 2026
79f79e4
remove doc
nialexsan Feb 25, 2026
1e6acc3
update ref
nialexsan Feb 25, 2026
21c6c5a
close position in strategy
nialexsan Feb 25, 2026
190e1c3
remove comments
nialexsan Feb 25, 2026
8e3c733
Merge branch 'main' into nialexsan/dust-debug-flowalp
nialexsan Feb 25, 2026
341461d
fix closing
nialexsan Feb 26, 2026
d154cea
switch to upgradeable change
nialexsan Feb 26, 2026
226c86a
add autobalancer
nialexsan Feb 26, 2026
6a6b85c
fix tracer strategy
nialexsan Feb 26, 2026
934319a
remove unused method
nialexsan Feb 26, 2026
1c61628
Apply suggestion from @nialexsan
nialexsan Feb 26, 2026
41b24de
update ref
nialexsan Feb 26, 2026
c26627c
sync implementation with FlowALP
nialexsan Mar 1, 2026
a92ce68
fix deposit rate
nialexsan Mar 1, 2026
884e899
split PMStrategies test
nialexsan Mar 1, 2026
3452d17
update FlowALP ref
nialexsan Mar 5, 2026
a763eac
revert ci/cd
nialexsan Mar 5, 2026
c5d2c55
fix: adapt strategies to FlowALPv0 API changes from submodule update
holyfuchs Mar 5, 2026
cbfbd04
address comments
nialexsan Mar 5, 2026
4d7b359
fix contract
nialexsan Mar 5, 2026
e2787e0
Fix zero-debt close path state tracking
liobrasil Mar 6, 2026
ca91632
Apply suggestion from @nialexsan
nialexsan Mar 6, 2026
9b105b1
chore: mark strategy config key helpers as view
liobrasil Mar 6, 2026
27edd22
fix: publish receiver capabilities in yield vault txns
liobrasil Mar 6, 2026
268bbaa
address PR comments
nialexsan Mar 7, 2026
2d8788b
combine tests
nialexsan Mar 7, 2026
6a2915e
remove duplicated methods
nialexsan Mar 7, 2026
9cf0944
address strategy comments
nialexsan Mar 8, 2026
49b0a17
assert first vault is collateral[C
nialexsan Mar 8, 2026
f88fff4
tweaks
nialexsan Mar 8, 2026
f74da70
tweak assertion
nialexsan Mar 8, 2026
068457d
tweak mock
nialexsan Mar 8, 2026
bfc1fc0
Merge branch 'main' into nialexsan/dust-debug-flowalp
liobrasil Mar 9, 2026
2dddb68
test: use canonical position health helper
liobrasil Mar 9, 2026
8ea43dd
abstract hardcoded values
nialexsan Mar 10, 2026
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
1 change: 1 addition & 0 deletions .github/workflows/cadence_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
pull_request:
branches:
- main
- nialexsan/pre-refactor

jobs:
tests:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/e2e_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
pull_request:
branches:
- main
- nialexsan/pre-refactor

jobs:
e2e-tests:
Expand Down
28 changes: 25 additions & 3 deletions cadence/contracts/FlowYieldVaults.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ access(all) contract FlowYieldVaults {
"Invalid Vault returns - requests \(ofToken.identifier) but returned \(result.getType().identifier)"
}
}
/// Closes the underlying position by repaying all debt and returning all collateral.
/// This method uses the AutoBalancer as a repayment source to swap yield tokens to debt tokens as needed.
/// Returns a Vault containing all collateral including any dust residuals.
access(FungibleToken.Withdraw) fun closePosition(collateralType: Type): @{FungibleToken.Vault}
}

/// StrategyComposer
Expand Down Expand Up @@ -340,6 +344,23 @@ access(all) contract FlowYieldVaults {

return <- res
}
/// Closes the YieldVault by repaying all debt on the underlying position and returning all collateral.
/// This method properly closes the FlowALP position by using the AutoBalancer to swap yield tokens
/// to MOET for debt repayment, then returns all collateral including any dust residuals.
access(FungibleToken.Withdraw) fun close(): @{FungibleToken.Vault} {
let collateral <- self._borrowStrategy().closePosition(collateralType: self.vaultType)

emit WithdrawnFromYieldVault(
id: self.uniqueID.id,
strategyType: self.getStrategyType(),
tokenType: collateral.getType().identifier,
amount: collateral.balance,
owner: self.owner?.address,
toUUID: collateral.uuid
)

return <- collateral
}
/// Returns an authorized reference to the encapsulated Strategy
access(self) view fun _borrowStrategy(): auth(FungibleToken.Withdraw) &{Strategy} {
return &self.strategy as auth(FungibleToken.Withdraw) &{Strategy}?
Expand Down Expand Up @@ -465,16 +486,17 @@ access(all) contract FlowYieldVaults {
let yieldVault = (&self.yieldVaults[id] as auth(FungibleToken.Withdraw) &YieldVault?)!
return <- yieldVault.withdraw(amount: amount)
}
/// Withdraws and returns all available funds from the specified YieldVault, destroying the YieldVault and access to any
/// Strategy-related wiring with it
/// Closes the YieldVault by repaying all debt and returning all collateral, then destroys the YieldVault.
/// This properly closes the underlying FlowALP position by using the AutoBalancer to swap yield tokens
/// to MOET for debt repayment, ensuring all collateral (including dust) is returned to the caller.
access(FungibleToken.Withdraw) fun closeYieldVault(_ id: UInt64): @{FungibleToken.Vault} {
pre {
self.yieldVaults[id] != nil:
"No YieldVault with ID \(id) found"
}

let yieldVault <- self._withdrawYieldVault(id: id)
let res <- yieldVault.withdraw(amount: yieldVault.getYieldVaultBalance())
let res <- yieldVault.close()
Burner.burn(<-yieldVault)
return <-res
}
Expand Down
14 changes: 14 additions & 0 deletions cadence/contracts/FlowYieldVaultsAutoBalancers.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,20 @@ access(all) contract FlowYieldVaultsAutoBalancers {
return self.account.capabilities.borrow<&DeFiActions.AutoBalancer>(publicPath)
}

/// Creates a source from an AutoBalancer for external use (e.g., position close operations).
/// This allows bypassing position topUpSource to avoid circular dependency issues.
///
/// @param id: The yield vault/AutoBalancer ID
/// @return Source that can withdraw from the AutoBalancer, or nil if not found
///
access(account) fun createExternalSource(id: UInt64): {DeFiActions.Source}? {
let storagePath = self.deriveAutoBalancerPath(id: id, storage: true) as! StoragePath
if let autoBalancer = self.account.storage.borrow<auth(DeFiActions.Get) &DeFiActions.AutoBalancer>(from: storagePath) {
return autoBalancer.createBalancerSource()
}
return nil
}

/// Checks if an AutoBalancer has at least one active (Scheduled) transaction.
/// Used by Supervisor to detect stuck yield vaults that need recovery.
///
Expand Down
188 changes: 186 additions & 2 deletions cadence/contracts/FlowYieldVaultsStrategiesV2.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ access(all) contract FlowYieldVaultsStrategiesV2 {
access(all) let univ3RouterEVMAddress: EVM.EVMAddress
access(all) let univ3QuoterEVMAddress: EVM.EVMAddress

access(all) let config: {String: AnyStruct}
access(all) let config: {String: AnyStruct}

/// Canonical StoragePath where the StrategyComposerIssuer should be stored
access(all) let IssuerStoragePath: StoragePath
Expand Down Expand Up @@ -81,7 +81,15 @@ access(all) contract FlowYieldVaultsStrategiesV2 {
access(self) var sink: {DeFiActions.Sink}
access(self) var source: {DeFiActions.Source}

init(id: DeFiActions.UniqueIdentifier, collateralType: Type, position: @FlowALPv0.Position) {
/// @TODO on the next iteration store yieldToMoetSwapper in the resource
/// Swapper used to convert yield tokens back to MOET for debt repayment
//access(self) let yieldToMoetSwapper: {DeFiActions.Swapper}

init(
id: DeFiActions.UniqueIdentifier,
collateralType: Type,
position: @FlowALPv0.Position
) {
self.uniqueID = id
self.sink = position.createSink(type: collateralType)
self.source = position.createSourceWithOptions(type: collateralType, pullFromTopUpSource: true)
Expand Down Expand Up @@ -110,6 +118,104 @@ access(all) contract FlowYieldVaultsStrategiesV2 {
}
return <- self.source.withdrawAvailable(maxAmount: maxAmount)
}
/// Closes the underlying FlowALP position by preparing repayment funds and closing with them.
///
/// This method:
/// 1. Calculates debt amount from position
/// 2. Creates external yield token source from AutoBalancer
/// 3. Swaps yield tokens → MOET via stored swapper
/// 4. Closes position with prepared MOET vault
///
/// This approach eliminates circular dependencies by preparing all funds externally
/// before calling the position's close method.
///
access(FungibleToken.Withdraw) fun closePosition(collateralType: Type): @{FungibleToken.Vault} {
pre {
self.isSupportedCollateralType(collateralType):
"Unsupported collateral type \(collateralType.identifier)"
}

// Step 1: Get debt amount from position using helper
let debtInfos = self.position.getTotalDebt()

// Step 2: Calculate total debt amount across all debt types
var totalDebtAmount: UFix64 = 0.0
for debtInfo in debtInfos {
totalDebtAmount = totalDebtAmount + debtInfo.amount
}

// Add a tiny buffer to ensure we overpay slightly and flip from Debit to Credit
// This works around FlowALPv0's recordDeposit logic where exact repayment keeps direction as Debit
let repaymentBuffer: UFix64 = 0.00000001 // 1e-8
totalDebtAmount = totalDebtAmount + repaymentBuffer

// Step 3: If no debt, pass empty vault array
if totalDebtAmount == 0.0 {
Copy link
Member

Choose a reason for hiding this comment

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

With the repaymentBuffer, this will never be true, so maybe this instead?

Suggested change
if totalDebtAmount == 0.0 {
if totalDebtAmount == repaymentBuffer {

or create a new variable, and then we can check if the original debt amount without buffer is 0

let totalDebtAmountWithBuffer = totalDebtAmount + repaymentBuffer

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

the close position logic should always overpay debt slightly

Copy link
Member

Choose a reason for hiding this comment

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

No. Look at this logic, totalDebtAmount should always > 0, and never be 0.0. So if totalDebtAmount == 0.0 will never become true.

            let repaymentBuffer: UFix64 = 0.00000001  // 1e-8
            totalDebtAmount = totalDebtAmount + repaymentBuffer

            // Step 3: If no debt, close with empty sources array
            if totalDebtAmount == 0.0 {

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it seems to be an artifact from one of the previous versions, there is no repayment buffer any more

let emptyVaults: @[{FungibleToken.Vault}] <- []
let resultVaults <- self.position.closePosition(
repaymentVaults: <-emptyVaults
)
// Extract the first vault (should be collateral)
assert(resultVaults.length > 0, message: "No vaults returned from closePosition")
Copy link
Member

Choose a reason for hiding this comment

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

If our position doesn't have any debt (true in this conditional), is it possible that it also doesn't have any collateral? What are the expected possible initial states prior to calling this function?

Copy link
Collaborator Author

@nialexsan nialexsan Mar 7, 2026

Choose a reason for hiding this comment

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

zero debt and zero collateral would be possible in one edge case, when there is actually no debt, and the collateral in the position is dust, that's rounded to 0

otherwise there should always be some collateral in the position
no debt in the position could be due to drawDownSink having 0 capacity (no liquidity in a swap pool), or token reserves being empty

added assertion

let collateralVault <- resultVaults.removeFirst()
Copy link
Member

Choose a reason for hiding this comment

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

If we ever have more than one collateral type in the future, we will destroy it here. I'd add an assertion that we got exactly one collateral vault back (if we're certain that is the only allowed initial state), or handle multiple collateral vaults being returned.

Copy link
Collaborator Author

@nialexsan nialexsan Mar 7, 2026

Choose a reason for hiding this comment

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

this strategy will work only with one collateral and one debt type at a time, I added assertions

destroy resultVaults
return <- collateralVault
}

// Step 4: Create external yield token source from AutoBalancer
let yieldTokenSource = FlowYieldVaultsAutoBalancers.createExternalSource(id: self.id()!)
?? panic("Could not create external source from AutoBalancer")

// Step 5: Retrieve yield→MOET swapper from contract config
let swapperKey = "yieldToMoetSwapper_".concat(self.id()!.toString())
Copy link
Member

Choose a reason for hiding this comment

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

better to create a swapperKeyByID(self.id()!) function

let yieldToMoetSwapper = FlowYieldVaultsStrategiesV2.config[swapperKey] as! {DeFiActions.Swapper}?
?? panic("No yield→MOET swapper found for strategy \(self.id()!)")

// Step 6: Use quoteIn to calculate exact yield token input needed for desired MOET output
// This bypasses SwapSource's branch selection issue where minimumAvailable
// underestimates due to RoundDown in quoteOut, causing insufficient output
// quoteIn rounds UP the input to guarantee exact output delivery
let quote = yieldToMoetSwapper.quoteIn(forDesired: totalDebtAmount, reverse: false)

// Step 7: Withdraw the calculated yield token amount
let yieldTokenVault <- yieldTokenSource.withdrawAvailable(maxAmount: quote.inAmount)

// Step 8: Swap with quote to get exact MOET output
// Swap honors the quote and delivers exactly totalDebtAmount
let moetVault <- yieldToMoetSwapper.swap(quote: quote, inVault: <-yieldTokenVault)

// Step 9: Close position with prepared MOET vault
let repaymentVaults: @[{FungibleToken.Vault}] <- [<-moetVault]
let resultVaults <- self.position.closePosition(
repaymentVaults: <-repaymentVaults
)

// Extract all returned vaults
assert(resultVaults.length > 0, message: "No vaults returned from closePosition")

// First vault should be collateral
var collateralVault <- resultVaults.removeFirst()
Copy link
Member

Choose a reason for hiding this comment

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

Better double check the collateral type?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

put a post check


// Handle any overpayment dust (MOET) by swapping back to collateral
while resultVaults.length > 0 {
let dustVault <- resultVaults.removeFirst()
if dustVault.balance > 0.0 && dustVault.getType() != collateralType {
// Swap overpayment back to collateral using configured swapper
let dustToCollateralSwapper = FlowYieldVaultsStrategiesV2.config["moetToCollateralSwapper_".concat(self.id()!.toString())] as! {DeFiActions.Swapper}?

Choose a reason for hiding this comment

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

Same idea here: create a function like moetSwapperKeyByID(self.id()!), or add an additional parameter to the swapperKeyByID which Leo suggested to specify which swapper it is

?? panic("No MOET→collateral swapper found for strategy \(self.id()!)")
let swappedCollateral <- dustToCollateralSwapper.swap(
quote: nil,
inVault: <-dustVault
)
collateralVault.deposit(from: <-swappedCollateral)
} else {
destroy dustVault
Copy link
Member

Choose a reason for hiding this comment

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

Note we will destroy a non-empty collateral vault regardless the amount. This could is techinically possible since the returned vaults can have pending deposits, and overpay vaults.

I think we should do:

if dustVault.balance == 0 { destroy dustVault }
if dustVault.getType == collateralType { collateralVault.deposit(from: <- dustVault) }
// dustVault.getType() != collateralType
// swap and then deposit
...

}
}

destroy resultVaults
return <- collateralVault
}
/// Executed when a Strategy is burned, cleaning up the Strategy's stored AutoBalancer
access(contract) fun burnCallback() {
FlowYieldVaultsAutoBalancers._cleanupAutoBalancer(id: self.id()!)
Expand Down Expand Up @@ -301,9 +407,43 @@ access(all) contract FlowYieldVaultsStrategiesV2 {
uniqueID: uniqueID
)

// Create Position source with CONSERVATIVE settings
Copy link
Member

Choose a reason for hiding this comment

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

Documentation seems misleading. Regardless of the value of pullFromTopUpSource, the position will maintain its health above the minimum during any withdrawals initiated by the source. Our choice of pullFromTopUpSource=false just means we won't service withdrawals from the Position's top-up source.

// pullFromTopUpSource: false ensures Position maintains health buffer
// This prevents Position from being pushed to minHealth (1.1) limit
let positionSource = position.createSourceWithOptions(
type: collateralType,
pullFromTopUpSource: false // ← CONSERVATIVE: maintain safety buffer
)

// Create Collateral -> Yield swapper (reverse of yieldToCollateralSwapper)
// Allows AutoBalancer to pull collateral, swap to yield token
let collateralToYieldSwapper = self._createCollateralToYieldSwapper(
collateralConfig: collateralConfig,
yieldTokenEVMAddress: tokens.yieldTokenEVMAddress,
yieldTokenType: tokens.yieldTokenType,
collateralType: collateralType,
uniqueID: uniqueID
)

// Create Position swap source for AutoBalancer deficit recovery
// When AutoBalancer value drops below deposits, pulls collateral from Position
let positionSwapSource = SwapConnectors.SwapSource(
swapper: collateralToYieldSwapper,
source: positionSource,
uniqueID: uniqueID
)

// Set AutoBalancer sink for overflow -> recollateralize
balancerIO.autoBalancer.setSink(positionSwapSink, updateSinkID: true)

// Set AutoBalancer source for deficit recovery -> pull from Position
// CONSERVATIVE: pullFromTopUpSource=false means Position maintains health buffer
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// CONSERVATIVE: pullFromTopUpSource=false means Position maintains health buffer

balancerIO.autoBalancer.setSource(positionSwapSource, updateSourceID: true)

// Store yield→MOET swapper in contract config for later access during closePosition
let swapperKey = "yieldToMoetSwapper_".concat(uniqueID.id.toString())

Choose a reason for hiding this comment

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

Also here - reasoning is it's better to have only one place (single source of truth) where we define this logic (correspondence between ID and swapper key), to help avoid typos or inconsistent usage

FlowYieldVaultsStrategiesV2.config[swapperKey] = yieldToMoetSwapper

switch type {
case Type<@FUSDEVStrategy>():
return <-create FUSDEVStrategy(
Expand Down Expand Up @@ -595,6 +735,50 @@ access(all) contract FlowYieldVaultsStrategiesV2 {
uniqueID: uniqueID
)
}

/// Creates a Collateral -> Yield token swapper using UniswapV3
/// This is the REVERSE of _createYieldToCollateralSwapper
/// Used by AutoBalancer to pull collateral from Position and swap to yield tokens
///
access(self) fun _createCollateralToYieldSwapper(
collateralConfig: FlowYieldVaultsStrategiesV2.CollateralConfig,
yieldTokenEVMAddress: EVM.EVMAddress,
yieldTokenType: Type,
collateralType: Type,
uniqueID: DeFiActions.UniqueIdentifier
): UniswapV3SwapConnectors.Swapper {
// Reverse the swap path: collateral -> yield (opposite of yield -> collateral)
Copy link
Member

Choose a reason for hiding this comment

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

Does this reverse computation happen a lot? Does it make sense to store the reversed path within the CollateralConfig?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

no, it's calculated only once during position creation, storing it separately would be redundant and it will increase risk of misconfiguration

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

we'll need to store another path though: moet to collateral

let forwardPath = collateralConfig.yieldToCollateralUniV3AddressPath
let reversedTokenPath: [EVM.EVMAddress] = []
Copy link
Member

Choose a reason for hiding this comment

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

var i = forwardPath.length
while i > 0 {
i = i - 1
reversedTokenPath.append(forwardPath[i])
}

// Reverse the fee path as well
let forwardFees = collateralConfig.yieldToCollateralUniV3FeePath
Copy link
Member

Choose a reason for hiding this comment

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

let reversedFeePath: [UInt32] = []
var j = forwardFees.length
while j > 0 {
j = j - 1
reversedFeePath.append(forwardFees[j])
}

// Verify the reversed path starts with collateral (ends with yield)
assert(
reversedTokenPath[reversedTokenPath.length - 1].equals(yieldTokenEVMAddress),
message: "Reversed path must end with yield token \(yieldTokenEVMAddress.toString())"
)

return self._createUniV3Swapper(
tokenPath: reversedTokenPath,
feePath: reversedFeePath,
inVault: collateralType, // ← Input is collateral
outVault: yieldTokenType, // ← Output is yield token
uniqueID: uniqueID
)
}
}

access(all) entitlement Configure
Expand Down
30 changes: 30 additions & 0 deletions cadence/contracts/PMStrategiesV1.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,16 @@ access(all) contract PMStrategiesV1 {
}
return <- self.source.withdrawAvailable(maxAmount: maxAmount)
}
/// Closes the position by withdrawing all available collateral.
/// For simple strategies without FlowALP positions, this just withdraws all available balance.
access(FungibleToken.Withdraw) fun closePosition(collateralType: Type): @{FungibleToken.Vault} {
pre {
self.isSupportedCollateralType(collateralType):
"Unsupported collateral type \(collateralType.identifier)"
}
let availableBalance = self.availableBalance(ofToken: collateralType)
return <- self.withdraw(maxAmount: availableBalance, ofToken: collateralType)
}
/// Executed when a Strategy is burned, cleaning up the Strategy's stored AutoBalancer
access(contract) fun burnCallback() {
FlowYieldVaultsAutoBalancers._cleanupAutoBalancer(id: self.id()!)
Expand Down Expand Up @@ -150,6 +160,16 @@ access(all) contract PMStrategiesV1 {
}
return <- self.source.withdrawAvailable(maxAmount: maxAmount)
}
/// Closes the position by withdrawing all available collateral.
/// For simple strategies without FlowALP positions, this just withdraws all available balance.
access(FungibleToken.Withdraw) fun closePosition(collateralType: Type): @{FungibleToken.Vault} {
pre {
self.isSupportedCollateralType(collateralType):
"Unsupported collateral type \(collateralType.identifier)"
}
let availableBalance = self.availableBalance(ofToken: collateralType)
return <- self.withdraw(maxAmount: availableBalance, ofToken: collateralType)
}
/// Executed when a Strategy is burned, cleaning up the Strategy's stored AutoBalancer
access(contract) fun burnCallback() {
FlowYieldVaultsAutoBalancers._cleanupAutoBalancer(id: self.id()!)
Expand Down Expand Up @@ -215,6 +235,16 @@ access(all) contract PMStrategiesV1 {
}
return <- self.source.withdrawAvailable(maxAmount: maxAmount)
}
/// Closes the position by withdrawing all available collateral.
/// For simple strategies without FlowALP positions, this just withdraws all available balance.
access(FungibleToken.Withdraw) fun closePosition(collateralType: Type): @{FungibleToken.Vault} {
pre {
self.isSupportedCollateralType(collateralType):
"Unsupported collateral type \(collateralType.identifier)"
}
let availableBalance = self.availableBalance(ofToken: collateralType)
return <- self.withdraw(maxAmount: availableBalance, ofToken: collateralType)
}
/// Executed when a Strategy is burned, cleaning up the Strategy's stored AutoBalancer
access(contract) fun burnCallback() {
FlowYieldVaultsAutoBalancers._cleanupAutoBalancer(id: self.id()!)
Expand Down
Loading