Skip to content

Commit ae6ec8f

Browse files
authored
Merge pull request #158 from onflow/fix/FLOW-8-view-resolver-implementation
FLOW-8: Implement minimal YieldVault views
2 parents a89895a + 296c238 commit ae6ec8f

6 files changed

Lines changed: 183 additions & 4 deletions

File tree

cadence/contracts/FlowYieldVaults.cdc

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import "FungibleToken"
33
import "Burner"
44
import "ViewResolver"
5+
import "MetadataViews"
56
// DeFiActions
67
import "DeFiActions"
78
import "FlowYieldVaultsClosedBeta"
@@ -63,6 +64,53 @@ access(all) contract FlowYieldVaults {
6364
owner: Address?
6465
)
6566

67+
/* --- VIEWS --- */
68+
69+
/// YieldVaultInfo
70+
///
71+
/// Minimal view struct providing basic identification and configuration details for a YieldVault
72+
access(all) struct YieldVaultInfo {
73+
/// The YieldVault's ID (DeFiActions.UniqueIdentifier.id)
74+
access(all) let id: UInt64
75+
/// The YieldVault resource uuid
76+
access(all) let uuid: UInt64
77+
/// The type identifier of the Vault this YieldVault operates on
78+
access(all) let vaultTypeIdentifier: String
79+
/// The strategy type identifier for this YieldVault
80+
access(all) let strategyTypeIdentifier: String
81+
/// The YieldVault owner's address if available
82+
access(all) let owner: Address?
83+
84+
init(
85+
id: UInt64,
86+
uuid: UInt64,
87+
vaultTypeIdentifier: String,
88+
strategyTypeIdentifier: String,
89+
owner: Address?
90+
) {
91+
self.id = id
92+
self.uuid = uuid
93+
self.vaultTypeIdentifier = vaultTypeIdentifier
94+
self.strategyTypeIdentifier = strategyTypeIdentifier
95+
self.owner = owner
96+
}
97+
}
98+
99+
/// YieldVaultBalance
100+
///
101+
/// Minimal view struct providing the YieldVault's current available balance for the vault's denomination.
102+
access(all) struct YieldVaultBalance {
103+
/// The type identifier of the Vault this YieldVault operates on
104+
access(all) let tokenTypeIdentifier: String
105+
/// The current available balance for withdrawal
106+
access(all) let availableBalance: UFix64
107+
108+
init(tokenTypeIdentifier: String, availableBalance: UFix64) {
109+
self.tokenTypeIdentifier = tokenTypeIdentifier
110+
self.availableBalance = availableBalance
111+
}
112+
}
113+
66114
/* --- CONSTRUCTS --- */
67115

68116
/// Strategy
@@ -272,12 +320,38 @@ access(all) contract FlowYieldVaults {
272320
// Force unwrap to ensure burnCallback is called on the Strategy
273321
Burner.burn(<-_strategy!)
274322
}
275-
/// TODO: FlowYieldVaults specific views
276323
access(all) view fun getViews(): [Type] {
277-
return []
324+
return [
325+
Type<MetadataViews.Display>(),
326+
Type<YieldVaultInfo>(),
327+
Type<YieldVaultBalance>()
328+
]
278329
}
279-
/// TODO: FlowYieldVaults specific view resolution
280330
access(all) fun resolveView(_ view: Type): AnyStruct? {
331+
switch view {
332+
case Type<MetadataViews.Display>():
333+
return MetadataViews.Display(
334+
name: "Yield Vault #\(self.id())",
335+
description: "Yield vault for strategy \(self.getStrategyType()) and vault type \(self.getVaultTypeIdentifier())",
336+
// Temporary placeholder thumbnail; replace with the final hosted URL when available.
337+
thumbnail: MetadataViews.HTTPFile(
338+
url: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='1' height='1'/>"
339+
)
340+
)
341+
case Type<YieldVaultInfo>():
342+
return YieldVaultInfo(
343+
id: self.id(),
344+
uuid: self.uuid,
345+
vaultTypeIdentifier: self.vaultType.identifier,
346+
strategyTypeIdentifier: self.getStrategyType(),
347+
owner: self.owner?.address
348+
)
349+
case Type<YieldVaultBalance>():
350+
return YieldVaultBalance(
351+
tokenTypeIdentifier: self.vaultType.identifier,
352+
availableBalance: self.getYieldVaultBalance()
353+
)
354+
}
281355
return nil
282356
}
283357
/// Deposits the provided Vault to the Strategy
@@ -312,7 +386,7 @@ access(all) contract FlowYieldVaults {
312386
}
313387
/// Returns the strategy type identifier for this YieldVault
314388
access(all) view fun getStrategyType(): String {
315-
return self.strategy.getType().identifier
389+
return self._borrowStrategy().getType().identifier
316390
}
317391
/// Withdraws the requested amount from the Strategy
318392
access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @{FungibleToken.Vault} {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import "FlowYieldVaults"
2+
3+
/// Returns the YieldVaultBalance view for the yieldVault with the given ID at the provided address or nil if either the
4+
/// address does not have a YieldVaultManager stored, the YieldVault is not available, or the view cannot be resolved.
5+
///
6+
/// @param address: The address of the account to look for the YieldVault
7+
/// @param id: The ID of the YieldVault to query
8+
///
9+
access(all)
10+
fun main(address: Address, id: UInt64): FlowYieldVaults.YieldVaultBalance? {
11+
if let manager = getAccount(address)
12+
.capabilities.borrow<&FlowYieldVaults.YieldVaultManager>(FlowYieldVaults.YieldVaultManagerPublicPath)
13+
{
14+
if let yieldVault = manager.borrowYieldVault(id: id) {
15+
if !yieldVault.getViews().contains(Type<FlowYieldVaults.YieldVaultBalance>()) {
16+
return nil
17+
}
18+
return yieldVault.resolveView(Type<FlowYieldVaults.YieldVaultBalance>()) as? FlowYieldVaults.YieldVaultBalance
19+
}
20+
}
21+
return nil
22+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import "FlowYieldVaults"
2+
import "MetadataViews"
3+
4+
/// Returns the Display view for the yieldVault with the given ID at the provided address or nil if either the
5+
/// address does not have a YieldVaultManager stored, the YieldVault is not available, or the view cannot be resolved.
6+
///
7+
/// @param address: The address of the account to look for the YieldVault
8+
/// @param id: The ID of the YieldVault to query
9+
///
10+
access(all)
11+
fun main(address: Address, id: UInt64): MetadataViews.Display? {
12+
if let manager = getAccount(address)
13+
.capabilities.borrow<&FlowYieldVaults.YieldVaultManager>(FlowYieldVaults.YieldVaultManagerPublicPath)
14+
{
15+
if let yieldVault = manager.borrowYieldVault(id: id) {
16+
if !yieldVault.getViews().contains(Type<MetadataViews.Display>()) {
17+
return nil
18+
}
19+
return yieldVault.resolveView(Type<MetadataViews.Display>()) as? MetadataViews.Display
20+
}
21+
}
22+
return nil
23+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import "FlowYieldVaults"
2+
3+
/// Returns the YieldVaultInfo view for the yieldVault with the given ID at the provided address or nil if either the
4+
/// address does not have a YieldVaultManager stored, the YieldVault is not available, or the view cannot be resolved.
5+
///
6+
/// @param address: The address of the account to look for the YieldVault
7+
/// @param id: The ID of the YieldVault to query
8+
///
9+
access(all)
10+
fun main(address: Address, id: UInt64): FlowYieldVaults.YieldVaultInfo? {
11+
if let manager = getAccount(address)
12+
.capabilities.borrow<&FlowYieldVaults.YieldVaultManager>(FlowYieldVaults.YieldVaultManagerPublicPath)
13+
{
14+
if let yieldVault = manager.borrowYieldVault(id: id) {
15+
if !yieldVault.getViews().contains(Type<FlowYieldVaults.YieldVaultInfo>()) {
16+
return nil
17+
}
18+
return yieldVault.resolveView(Type<FlowYieldVaults.YieldVaultInfo>()) as? FlowYieldVaults.YieldVaultInfo
19+
}
20+
}
21+
return nil
22+
}

cadence/tests/test_helpers.cdc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import "MetadataViews"
55
import "FlowToken"
66
import "MOET"
77
import "FlowALPv0"
8+
import "FlowYieldVaults"
89

910
access(all) let serviceAccount = Test.serviceAccount()
1011

@@ -441,6 +442,27 @@ fun getYieldVaultBalance(address: Address, yieldVaultID: UInt64): UFix64? {
441442
return res.returnValue as! UFix64?
442443
}
443444

445+
access(all)
446+
fun getYieldVaultInfoView(address: Address, yieldVaultID: UInt64): FlowYieldVaults.YieldVaultInfo? {
447+
let res = _executeScript("../scripts/flow-yield-vaults/get_yield_vault_info_view.cdc", [address, yieldVaultID])
448+
Test.expect(res, Test.beSucceeded())
449+
return res.returnValue as! FlowYieldVaults.YieldVaultInfo?
450+
}
451+
452+
access(all)
453+
fun getYieldVaultBalanceView(address: Address, yieldVaultID: UInt64): FlowYieldVaults.YieldVaultBalance? {
454+
let res = _executeScript("../scripts/flow-yield-vaults/get_yield_vault_balance_view.cdc", [address, yieldVaultID])
455+
Test.expect(res, Test.beSucceeded())
456+
return res.returnValue as! FlowYieldVaults.YieldVaultBalance?
457+
}
458+
459+
access(all)
460+
fun getYieldVaultDisplayView(address: Address, yieldVaultID: UInt64): MetadataViews.Display? {
461+
let res = _executeScript("../scripts/flow-yield-vaults/get_yield_vault_display_view.cdc", [address, yieldVaultID])
462+
Test.expect(res, Test.beSucceeded())
463+
return res.returnValue as! MetadataViews.Display?
464+
}
465+
444466
access(all)
445467
fun getAutoBalancerBalance(id: UInt64): UFix64? {
446468
let res = _executeScript("../scripts/flow-yield-vaults/get_auto_balancer_balance_by_id.cdc", [id])

cadence/tests/yield_vault_lifecycle_test.cdc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,22 @@ fun testLifecycle() {
110110

111111
log("✅ YieldVault created with ID: \(yieldVaultID)")
112112

113+
// Validate minimal YieldVault views
114+
let info = getYieldVaultInfoView(address: user.address, yieldVaultID: yieldVaultID)
115+
Test.assert(info != nil, message: "Expected YieldVaultInfo view to resolve")
116+
Test.assertEqual(yieldVaultID, info!.id)
117+
Test.assertEqual(flowTokenIdentifier, info!.vaultTypeIdentifier)
118+
Test.assertEqual(strategyIdentifier, info!.strategyTypeIdentifier)
119+
120+
let balanceView = getYieldVaultBalanceView(address: user.address, yieldVaultID: yieldVaultID)
121+
Test.assert(balanceView != nil, message: "Expected YieldVaultBalance view to resolve")
122+
Test.assertEqual(flowTokenIdentifier, balanceView!.tokenTypeIdentifier)
123+
Test.assertEqual(getYieldVaultBalance(address: user.address, yieldVaultID: yieldVaultID)!, balanceView!.availableBalance)
124+
125+
let displayView = getYieldVaultDisplayView(address: user.address, yieldVaultID: yieldVaultID)
126+
Test.assert(displayView != nil, message: "Expected MetadataViews.Display view to resolve")
127+
Test.assertEqual("Yield Vault #\(yieldVaultID)", displayView!.name)
128+
113129
let addedToManagerEvents = Test.eventsOfType(Type<FlowYieldVaults.AddedToManager>())
114130
Test.assert(addedToManagerEvents.length > 0, message: "Expected at least 1 FlowYieldVaults.AddedToManager event but found none")
115131
let addedToManagerEvent = addedToManagerEvents[addedToManagerEvents.length - 1] as! FlowYieldVaults.AddedToManager

0 commit comments

Comments
 (0)