Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
6b1bbbd
FLOW-8: Implement minimal YieldVault views
liobrasil Feb 2, 2026
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
36158c3
add getNAVBalance to YieldVault, implementation in PMStrategiesV1
loic1 Feb 27, 2026
1285bef
update FlowALP submodule
loic1 Feb 27, 2026
c252eb4
revert submodule refactir
nialexsan Feb 28, 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
6afdb74
Update cadence/contracts/FlowYieldVaults.cdc
liobrasil Mar 2, 2026
11a3be4
Merge remote-tracking branch 'origin/main' into fix/FLOW-8-view-resol…
liobrasil Mar 2, 2026
296c238
feat(cadence): add YieldVault Display view
liobrasil Mar 2, 2026
490e4e8
Add forked rebalance scenarios 4 and 5.
RZhang05 Mar 4, 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
ae6ec8f
Merge pull request #158 from onflow/fix/FLOW-8-view-resolver-implemen…
liobrasil Mar 9, 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
f9daf70
Merge pull request #182 from onflow/nialexsan/dust-debug-flowalp
nialexsan Mar 10, 2026
301e670
Added script from observer
AndriiDiachuk Mar 10, 2026
aafc519
Merge remote-tracking branch 'origin/main' into loic/pkm-385-add-nav-…
nialexsan Mar 10, 2026
3393329
Merge pull request #189 from onflow/loic/pkm-385-add-nav-based-balanc…
nialexsan Mar 10, 2026
0f77332
Merge pull request #205 from onflow/andrii/add-script-used-in-observer
AndriiDiachuk Mar 11, 2026
9dbb0b2
Merge branch 'main' into forked-scen45
RZhang05 Mar 12, 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
119 changes: 112 additions & 7 deletions cadence/contracts/FlowYieldVaults.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import "FungibleToken"
import "Burner"
import "ViewResolver"
import "MetadataViews"
// DeFiActions
import "DeFiActions"
import "FlowYieldVaultsClosedBeta"
Expand Down Expand Up @@ -63,6 +64,53 @@ access(all) contract FlowYieldVaults {
owner: Address?
)

/* --- VIEWS --- */

/// YieldVaultInfo
///
/// Minimal view struct providing basic identification and configuration details for a YieldVault
access(all) struct YieldVaultInfo {
/// The YieldVault's ID (DeFiActions.UniqueIdentifier.id)
access(all) let id: UInt64
/// The YieldVault resource uuid
access(all) let uuid: UInt64
/// The type identifier of the Vault this YieldVault operates on
access(all) let vaultTypeIdentifier: String
/// The strategy type identifier for this YieldVault
access(all) let strategyTypeIdentifier: String
/// The YieldVault owner's address if available
access(all) let owner: Address?

init(
id: UInt64,
uuid: UInt64,
vaultTypeIdentifier: String,
strategyTypeIdentifier: String,
owner: Address?
) {
self.id = id
self.uuid = uuid
self.vaultTypeIdentifier = vaultTypeIdentifier
self.strategyTypeIdentifier = strategyTypeIdentifier
self.owner = owner
}
}

/// YieldVaultBalance
///
/// Minimal view struct providing the YieldVault's current available balance for the vault's denomination.
access(all) struct YieldVaultBalance {
/// The type identifier of the Vault this YieldVault operates on
access(all) let tokenTypeIdentifier: String
/// The current available balance for withdrawal
access(all) let availableBalance: UFix64

init(tokenTypeIdentifier: String, availableBalance: UFix64) {
self.tokenTypeIdentifier = tokenTypeIdentifier
self.availableBalance = availableBalance
}
}

/* --- CONSTRUCTS --- */

/// Strategy
Expand Down Expand Up @@ -91,6 +139,11 @@ access(all) contract FlowYieldVaults {
/// Returns the balance of the given token available for withdrawal. Note that this may be an estimate due to
/// the lack of guarantees inherent to DeFiActions Sources
access(all) fun availableBalance(ofToken: Type): UFix64
/// Returns the NAV-based balance of the given token. Defaults to availableBalance(); strategies backed by
/// ERC-4626 vaults should override to return convertToAssets(shares) instead of an AMM quote.
access(all) fun navBalance(ofToken: Type): UFix64 {
return self.availableBalance(ofToken: ofToken)
}
/// Deposits up to the balance of the referenced Vault into this Strategy
access(all) fun deposit(from: auth(FungibleToken.Withdraw) &{FungibleToken.Vault}) {
pre {
Expand All @@ -105,6 +158,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 @@ -259,6 +316,10 @@ access(all) contract FlowYieldVaults {
access(all) fun getYieldVaultBalance(): UFix64 {
return self._borrowStrategy().availableBalance(ofToken: self.vaultType)
}
/// Returns the NAV-based balance of the YieldVault's position via convertToAssets on the underlying ERC-4626 vault
access(all) fun getNAVBalance(): UFix64 {
return self._borrowStrategy().navBalance(ofToken: self.vaultType)
}
/// Burner.Burnable conformance - emits the BurnedYieldVault event when burned
access(contract) fun burnCallback() {
emit BurnedYieldVault(
Expand All @@ -272,12 +333,38 @@ access(all) contract FlowYieldVaults {
// Force unwrap to ensure burnCallback is called on the Strategy
Burner.burn(<-_strategy!)
}
/// TODO: FlowYieldVaults specific views
access(all) view fun getViews(): [Type] {
return []
return [
Type<MetadataViews.Display>(),
Type<YieldVaultInfo>(),
Type<YieldVaultBalance>()
]
}
/// TODO: FlowYieldVaults specific view resolution
access(all) fun resolveView(_ view: Type): AnyStruct? {
switch view {
case Type<MetadataViews.Display>():
return MetadataViews.Display(
name: "Yield Vault #\(self.id())",
description: "Yield vault for strategy \(self.getStrategyType()) and vault type \(self.getVaultTypeIdentifier())",
// Temporary placeholder thumbnail; replace with the final hosted URL when available.
thumbnail: MetadataViews.HTTPFile(
url: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='1' height='1'/>"
)
)
case Type<YieldVaultInfo>():
return YieldVaultInfo(
id: self.id(),
uuid: self.uuid,
vaultTypeIdentifier: self.vaultType.identifier,
strategyTypeIdentifier: self.getStrategyType(),
owner: self.owner?.address
)
case Type<YieldVaultBalance>():
return YieldVaultBalance(
tokenTypeIdentifier: self.vaultType.identifier,
availableBalance: self.getYieldVaultBalance()
)
}
return nil
}
/// Deposits the provided Vault to the Strategy
Expand Down Expand Up @@ -312,7 +399,7 @@ access(all) contract FlowYieldVaults {
}
/// Returns the strategy type identifier for this YieldVault
access(all) view fun getStrategyType(): String {
return self.strategy.getType().identifier
return self._borrowStrategy().getType().identifier
}
/// Withdraws the requested amount from the Strategy
access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @{FungibleToken.Vault} {
Expand Down Expand Up @@ -340,6 +427,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 +569,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
Loading
Loading