Smart Contract

FlowYieldVaultsConnectorV2

A.a092c4aab33daeda.FlowYieldVaultsConnectorV2

Valid From

143,197,179

Deployed

2w ago
Feb 10, 2026, 10:27:25 PM UTC

Dependents

3 imports
1/*
2FlowYieldVaultsConnectorV2 - Mainnet FlowYieldVaults Integration (Secured)
3
4This connector enables PrizeLinkedAccounts to deposit funds into FlowYieldVaults (yield-bearing strategies)
5and implements DeFiActions.Sink and DeFiActions.Source interfaces.
6
7Security model:
8- The YieldVaultManagerWrapper resource is stored in the deployer's account
9- A PUBLIC capability exposes only getYieldVaultBalance() for read-only queries
10- An ENTITLED capability (auth(Operate)) gates depositToYieldVault/withdrawFromYieldVault
11- The Connector struct holds the entitled capability internally, so only the contract
12  that stores the Connector (PrizeLinkedAccounts) can trigger deposit/withdraw operations
13
14FlowYieldVaults Contract: mainnet://b1d63873c3cc9f79.FlowYieldVaults
15*/
16
17import FungibleToken from 0xf233dcee88fe0abe
18import FlowYieldVaults from 0xb1d63873c3cc9f79
19import FlowYieldVaultsClosedBeta from 0xb1d63873c3cc9f79
20import DeFiActions from 0x92195d814edf9cb0
21
22access(all) contract FlowYieldVaultsConnectorV2 {
23
24    /// Entitlement required to deposit/withdraw through the YieldVaultManagerWrapper.
25    /// Only the Connector struct (living inside PrizeLinkedAccounts' Pool) holds a capability with this entitlement.
26    access(all) entitlement Operate
27
28    // Storage paths
29    access(all) let ManagerStoragePath: StoragePath
30    access(all) let ManagerPublicPath: PublicPath
31
32    // Events
33    access(all) event ConnectorCreated(managerAddress: Address, strategyType: String, vaultType: String)
34    access(all) event DepositedToYieldVault(yieldVaultID: UInt64, amount: UFix64, vaultType: String)
35    access(all) event WithdrawnFromYieldVault(yieldVaultID: UInt64, amount: UFix64, vaultType: String)
36    access(all) event YieldVaultCreated(yieldVaultID: UInt64, strategyType: String, initialAmount: UFix64)
37
38    /// YieldVaultManagerWrapper Resource
39    /// Wraps FlowYieldVaults.YieldVaultManager with beta badge authentication.
40    ///
41    /// - getYieldVaultBalance() is access(all) — callable via the public capability for read-only queries
42    /// - depositToYieldVault() and withdrawFromYieldVault() require the Operate entitlement
43    access(all) resource YieldVaultManagerWrapper {
44        access(self) let yieldVaultManagerCap: Capability<auth(FungibleToken.Withdraw) &FlowYieldVaults.YieldVaultManager>
45        access(self) let betaBadgeCap: Capability<auth(FlowYieldVaultsClosedBeta.Beta) &FlowYieldVaultsClosedBeta.BetaBadge>
46        access(self) var yieldVaultID: UInt64?
47        access(all) let vaultType: Type
48        access(all) let strategyType: Type
49
50        init(
51            yieldVaultManagerCap: Capability<auth(FungibleToken.Withdraw) &FlowYieldVaults.YieldVaultManager>,
52            betaBadgeCap: Capability<auth(FlowYieldVaultsClosedBeta.Beta) &FlowYieldVaultsClosedBeta.BetaBadge>,
53            vaultType: Type,
54            strategyType: Type
55        ) {
56            pre {
57                yieldVaultManagerCap.check(): "Invalid YieldVaultManager capability"
58                betaBadgeCap.check(): "Invalid Beta badge capability"
59            }
60
61            self.yieldVaultManagerCap = yieldVaultManagerCap
62            self.betaBadgeCap = betaBadgeCap
63            self.vaultType = vaultType
64            self.strategyType = strategyType
65            self.yieldVaultID = nil
66        }
67
68        /// Deposit tokens into the yield vault. Requires Operate entitlement.
69        access(Operate) fun depositToYieldVault(from: auth(FungibleToken.Withdraw) &{FungibleToken.Vault}) {
70            pre {
71                from.getType() == self.vaultType: "Vault type mismatch"
72                from.balance > 0.0: "Cannot deposit zero balance"
73            }
74
75            let amount = from.balance
76            let yieldVaultManager = self.yieldVaultManagerCap.borrow()
77                ?? panic("Cannot borrow YieldVaultManager")
78            let betaBadge = self.betaBadgeCap.borrow()
79                ?? panic("Cannot borrow Beta badge")
80
81            // If we don't have a YieldVault yet, create one
82            if self.yieldVaultID == nil {
83                let initialVault <- from.withdraw(amount: amount)
84
85                let newID = yieldVaultManager.createYieldVault(
86                    betaRef: betaBadge,
87                    strategyType: self.strategyType,
88                    withVault: <- initialVault
89                )
90
91                self.yieldVaultID = newID
92
93                emit YieldVaultCreated(
94                    yieldVaultID: self.yieldVaultID!,
95                    strategyType: self.strategyType.identifier,
96                    initialAmount: amount
97                )
98
99                emit DepositedToYieldVault(
100                    yieldVaultID: self.yieldVaultID!,
101                    amount: amount,
102                    vaultType: self.vaultType.identifier
103                )
104            } else {
105                let depositVault <- from.withdraw(amount: amount)
106
107                yieldVaultManager.depositToYieldVault(
108                    betaRef: betaBadge,
109                    self.yieldVaultID!,
110                    from: <- depositVault
111                )
112
113                emit DepositedToYieldVault(
114                    yieldVaultID: self.yieldVaultID!,
115                    amount: amount,
116                    vaultType: self.vaultType.identifier
117                )
118            }
119        }
120
121        /// Query the current balance in the yield vault. Publicly accessible (read-only).
122        access(all) fun getYieldVaultBalance(): UFix64 {
123            if self.yieldVaultID == nil {
124                return 0.0
125            }
126
127            let yieldVaultManager = self.yieldVaultManagerCap.borrow()
128                ?? panic("Cannot borrow YieldVaultManager")
129
130            let yieldVaultRef = yieldVaultManager.borrowYieldVault(id: self.yieldVaultID!)
131            if yieldVaultRef == nil {
132                return 0.0
133            }
134
135            return yieldVaultRef!.getYieldVaultBalance()
136        }
137
138        /// Withdraw tokens from the yield vault. Requires Operate entitlement.
139        access(Operate) fun withdrawFromYieldVault(maxAmount: UFix64): @{FungibleToken.Vault} {
140            pre {
141                self.yieldVaultID != nil: "No YieldVault initialized"
142                maxAmount > 0.0: "Cannot withdraw zero amount"
143            }
144
145            let yieldVaultManager = self.yieldVaultManagerCap.borrow()
146                ?? panic("Cannot borrow YieldVaultManager")
147
148            let available = self.getYieldVaultBalance()
149            let withdrawAmount = maxAmount < available ? maxAmount : available
150
151            assert(withdrawAmount > 0.0, message: "Insufficient balance in YieldVault")
152
153            let vault <- yieldVaultManager.withdrawFromYieldVault(self.yieldVaultID!, amount: withdrawAmount)
154
155            emit WithdrawnFromYieldVault(
156                yieldVaultID: self.yieldVaultID!,
157                amount: withdrawAmount,
158                vaultType: vault.getType().identifier
159            )
160
161            return <- vault
162        }
163    }
164
165    /// Connector Struct
166    /// Implements DeFiActions.Sink and DeFiActions.Source.
167    ///
168    /// Holds an entitled capability (auth(Operate)) to the YieldVaultManagerWrapper for deposit/withdraw,
169    /// and uses the public capability for balance queries.
170    /// Once this struct is stored inside PrizeLinkedAccounts' Pool (access(contract) field),
171    /// only PrizeLinkedAccounts can trigger deposit/withdraw operations.
172    access(all) struct Connector: DeFiActions.Sink, DeFiActions.Source {
173        access(all) let managerAddress: Address
174        access(self) let operateCap: Capability<auth(Operate) &YieldVaultManagerWrapper>
175        access(contract) var uniqueID: DeFiActions.UniqueIdentifier?
176        access(all) let vaultType: Type
177
178        init(
179            managerAddress: Address,
180            operateCap: Capability<auth(Operate) &YieldVaultManagerWrapper>,
181            vaultType: Type
182        ) {
183            pre {
184                operateCap.check(): "Invalid Operate capability for YieldVaultManagerWrapper"
185            }
186            self.managerAddress = managerAddress
187            self.operateCap = operateCap
188            self.vaultType = vaultType
189            self.uniqueID = nil
190        }
191
192        /// DeFiActions.Sink Implementation — deposits through the entitled capability
193        access(all) fun depositCapacity(from: auth(FungibleToken.Withdraw) &{FungibleToken.Vault}) {
194            let managerRef = self.operateCap.borrow()
195                ?? panic("Cannot borrow YieldVaultManagerWrapper via Operate capability")
196
197            managerRef.depositToYieldVault(from: from)
198        }
199
200        access(all) view fun getSinkType(): Type {
201            return self.vaultType
202        }
203
204        access(all) view fun minimumCapacity(): UFix64 {
205            return 0.0
206        }
207
208        /// DeFiActions.Source Implementation — balance via public path, withdraw via entitled capability
209        access(all) fun minimumAvailable(): UFix64 {
210            let managerAccount = getAccount(self.managerAddress)
211            if let managerRef = managerAccount.capabilities.borrow<&YieldVaultManagerWrapper>(
212                FlowYieldVaultsConnectorV2.ManagerPublicPath
213            ) {
214                return managerRef.getYieldVaultBalance()
215            }
216            return 0.0
217        }
218
219        access(FungibleToken.Withdraw) fun withdrawAvailable(maxAmount: UFix64): @{FungibleToken.Vault} {
220            let managerRef = self.operateCap.borrow()
221                ?? panic("Cannot borrow YieldVaultManagerWrapper via Operate capability")
222
223            return <- managerRef.withdrawFromYieldVault(maxAmount: maxAmount)
224        }
225
226        access(all) view fun getSourceType(): Type {
227            return self.vaultType
228        }
229
230        /// DeFiActions Component Info
231        access(all) fun getComponentInfo(): DeFiActions.ComponentInfo {
232            return DeFiActions.ComponentInfo(
233                type: self.getType(),
234                id: self.uniqueID?.id,
235                innerComponents: []
236            )
237        }
238
239        access(contract) view fun copyID(): DeFiActions.UniqueIdentifier? {
240            return self.uniqueID
241        }
242
243        access(contract) fun setID(_ id: DeFiActions.UniqueIdentifier?) {
244            self.uniqueID = id
245        }
246    }
247
248    /// Create a new YieldVaultManagerWrapper and store it.
249    /// Returns a Connector struct that holds an entitled capability for deposit/withdraw.
250    /// A read-only public capability is also published for balance queries.
251    access(all) fun createConnectorAndManager(
252        account: auth(Storage, Capabilities) &Account,
253        yieldVaultManagerCap: Capability<auth(FungibleToken.Withdraw) &FlowYieldVaults.YieldVaultManager>,
254        betaBadgeCap: Capability<auth(FlowYieldVaultsClosedBeta.Beta) &FlowYieldVaultsClosedBeta.BetaBadge>,
255        vaultType: Type,
256        strategyType: Type
257    ): Connector {
258        // Validate that the strategy supports the vault type
259        let supportedVaults = FlowYieldVaults.getSupportedInitializationVaults(forStrategy: strategyType)
260        assert(
261            supportedVaults[vaultType] == true,
262            message: "Strategy does not support vault type"
263        )
264
265        // Create and store the YieldVaultManagerWrapper resource
266        let manager <- create YieldVaultManagerWrapper(
267            yieldVaultManagerCap: yieldVaultManagerCap,
268            betaBadgeCap: betaBadgeCap,
269            vaultType: vaultType,
270            strategyType: strategyType
271        )
272
273        account.storage.save(<-manager, to: self.ManagerStoragePath)
274
275        // Issue entitled capability for the Connector (NOT published — only the Connector holds it)
276        let operateCap = account.capabilities.storage.issue<auth(Operate) &YieldVaultManagerWrapper>(self.ManagerStoragePath)
277
278        // Publish read-only capability for public balance queries
279        let publicCap = account.capabilities.storage.issue<&YieldVaultManagerWrapper>(self.ManagerStoragePath)
280        account.capabilities.publish(publicCap, at: self.ManagerPublicPath)
281
282        emit ConnectorCreated(
283            managerAddress: account.address,
284            strategyType: strategyType.identifier,
285            vaultType: vaultType.identifier
286        )
287
288        // Return the struct connector that holds the entitled capability
289        return Connector(
290            managerAddress: account.address,
291            operateCap: operateCap,
292            vaultType: vaultType
293        )
294    }
295
296    init() {
297        let identifier = "flowYieldVaultsManagerV2_\(self.account.address)"
298        self.ManagerStoragePath = StoragePath(identifier: identifier)!
299        self.ManagerPublicPath = PublicPath(identifier: identifier)!
300    }
301}
302