Smart Contract
FlowVaultsConnector
A.6a3c3dad7074bca5.FlowVaultsConnector
1/*
2FlowVaultsConnector - Mainnet Flow Vaults Integration
3
4This connector enables PrizeSavings to deposit funds into FlowVaults (Tides)
5and implements DeFiActions.Sink and DeFiActions.Source interfaces.
6
7FlowVaults Contract: mainnet://b1d63873c3cc9f79.FlowVaults
8*/
9
10import FungibleToken from 0xf233dcee88fe0abe
11import FlowVaults from 0xb1d63873c3cc9f79
12import FlowVaultsClosedBeta from 0xb1d63873c3cc9f79
13import DeFiActions from 0x92195d814edf9cb0
14
15access(all) contract FlowVaultsConnector {
16
17 // Storage paths
18 access(all) let ManagerStoragePath: StoragePath
19 access(all) let ManagerPublicPath: PublicPath
20
21 // Events
22 access(all) event ConnectorCreated(managerAddress: Address, strategyType: String, vaultType: String)
23 access(all) event DepositedToTide(tideID: UInt64, amount: UFix64, vaultType: String)
24 access(all) event WithdrawnFromTide(tideID: UInt64, amount: UFix64, vaultType: String)
25 access(all) event TideCreated(tideID: UInt64, strategyType: String, initialAmount: UFix64)
26
27 /// TideManagerWrapper Resource
28 /// Wraps FlowVaults.TideManager with beta badge authentication
29 access(all) resource TideManagerWrapper {
30 access(self) let tideManagerCap: Capability<auth(FungibleToken.Withdraw) &FlowVaults.TideManager>
31 access(self) let betaBadgeCap: Capability<auth(FlowVaultsClosedBeta.Beta) &FlowVaultsClosedBeta.BetaBadge>
32 access(self) var tideID: UInt64?
33 access(all) let vaultType: Type
34 access(all) let strategyType: Type
35
36 init(
37 tideManagerCap: Capability<auth(FungibleToken.Withdraw) &FlowVaults.TideManager>,
38 betaBadgeCap: Capability<auth(FlowVaultsClosedBeta.Beta) &FlowVaultsClosedBeta.BetaBadge>,
39 vaultType: Type,
40 strategyType: Type
41 ) {
42 pre {
43 tideManagerCap.check(): "Invalid TideManager capability"
44 betaBadgeCap.check(): "Invalid Beta badge capability"
45 }
46
47 self.tideManagerCap = tideManagerCap
48 self.betaBadgeCap = betaBadgeCap
49 self.vaultType = vaultType
50 self.strategyType = strategyType
51 self.tideID = nil
52 }
53
54 access(all) fun depositToTide(from: auth(FungibleToken.Withdraw) &{FungibleToken.Vault}) {
55 pre {
56 from.getType() == self.vaultType: "Vault type mismatch"
57 from.balance > 0.0: "Cannot deposit zero balance"
58 }
59
60 let amount = from.balance
61 let tideManager = self.tideManagerCap.borrow()
62 ?? panic("Cannot borrow TideManager")
63 let betaBadge = self.betaBadgeCap.borrow()
64 ?? panic("Cannot borrow Beta badge")
65
66 // If we don't have a Tide yet, create one
67 if self.tideID == nil {
68 let initialVault <- from.withdraw(amount: amount)
69
70 tideManager.createTide(
71 betaRef: betaBadge,
72 strategyType: self.strategyType,
73 withVault: <- initialVault
74 )
75
76 let tideIDs = tideManager.getIDs()
77 assert(tideIDs.length > 0, message: "Failed to create Tide")
78 self.tideID = tideIDs[tideIDs.length - 1]
79
80 emit TideCreated(
81 tideID: self.tideID!,
82 strategyType: self.strategyType.identifier,
83 initialAmount: amount
84 )
85
86 emit DepositedToTide(
87 tideID: self.tideID!,
88 amount: amount,
89 vaultType: self.vaultType.identifier
90 )
91 } else {
92 let depositVault <- from.withdraw(amount: amount)
93
94 tideManager.depositToTide(
95 betaRef: betaBadge,
96 self.tideID!,
97 from: <- depositVault
98 )
99
100 emit DepositedToTide(
101 tideID: self.tideID!,
102 amount: amount,
103 vaultType: self.vaultType.identifier
104 )
105 }
106 }
107
108 access(all) fun getTideBalance(): UFix64 {
109 if self.tideID == nil {
110 return 0.0
111 }
112
113 let tideManager = self.tideManagerCap.borrow()
114 ?? panic("Cannot borrow TideManager")
115
116 let tideRef = tideManager.borrowTide(id: self.tideID!)
117 if tideRef == nil {
118 return 0.0
119 }
120
121 return tideRef!.getTideBalance()
122 }
123
124 access(all) fun withdrawFromTide(maxAmount: UFix64): @{FungibleToken.Vault} {
125 pre {
126 self.tideID != nil: "No Tide initialized"
127 maxAmount > 0.0: "Cannot withdraw zero amount"
128 }
129
130 let tideManager = self.tideManagerCap.borrow()
131 ?? panic("Cannot borrow TideManager")
132
133 let available = self.getTideBalance()
134 let withdrawAmount = maxAmount < available ? maxAmount : available
135
136 assert(withdrawAmount > 0.0, message: "Insufficient balance in Tide")
137
138 let vault <- tideManager.withdrawFromTide(self.tideID!, amount: withdrawAmount)
139
140 emit WithdrawnFromTide(
141 tideID: self.tideID!,
142 amount: withdrawAmount,
143 vaultType: vault.getType().identifier
144 )
145
146 return <- vault
147 }
148 }
149
150 /// Connector Struct
151 /// Implements DeFiActions.Sink and DeFiActions.Source
152 /// References a stored TideManagerWrapper resource
153 access(all) struct Connector: DeFiActions.Sink, DeFiActions.Source {
154 access(all) let managerAddress: Address
155 access(contract) var uniqueID: DeFiActions.UniqueIdentifier?
156 access(all) let vaultType: Type
157
158 init(managerAddress: Address, vaultType: Type) {
159 self.managerAddress = managerAddress
160 self.vaultType = vaultType
161 self.uniqueID = nil
162 }
163
164 /// DeFiActions.Sink Implementation
165 access(all) fun depositCapacity(from: auth(FungibleToken.Withdraw) &{FungibleToken.Vault}) {
166 let managerAccount = getAccount(self.managerAddress)
167 let managerRef = managerAccount.capabilities.borrow<&TideManagerWrapper>(
168 FlowVaultsConnector.ManagerPublicPath
169 ) ?? panic("Cannot borrow TideManagerWrapper from address")
170
171 managerRef.depositToTide(from: from)
172 }
173
174 access(all) view fun getSinkType(): Type {
175 return self.vaultType
176 }
177
178 access(all) view fun minimumCapacity(): UFix64 {
179 return 0.0
180 }
181
182 /// DeFiActions.Source Implementation
183 access(all) fun minimumAvailable(): UFix64 {
184 let managerAccount = getAccount(self.managerAddress)
185 if let managerRef = managerAccount.capabilities.borrow<&TideManagerWrapper>(
186 FlowVaultsConnector.ManagerPublicPath
187 ) {
188 return managerRef.getTideBalance()
189 }
190 return 0.0
191 }
192
193 access(FungibleToken.Withdraw) fun withdrawAvailable(maxAmount: UFix64): @{FungibleToken.Vault} {
194 let managerAccount = getAccount(self.managerAddress)
195 let managerRef = managerAccount.capabilities.borrow<&TideManagerWrapper>(
196 FlowVaultsConnector.ManagerPublicPath
197 ) ?? panic("Cannot borrow TideManagerWrapper from address")
198
199 return <- managerRef.withdrawFromTide(maxAmount: maxAmount)
200 }
201
202 access(all) view fun getSourceType(): Type {
203 return self.vaultType
204 }
205
206 /// DeFiActions Component Info
207 access(all) fun getComponentInfo(): DeFiActions.ComponentInfo {
208 return DeFiActions.ComponentInfo(
209 type: self.getType(),
210 id: self.uniqueID?.id,
211 innerComponents: []
212 )
213 }
214
215 access(contract) view fun copyID(): DeFiActions.UniqueIdentifier? {
216 return self.uniqueID
217 }
218
219 access(contract) fun setID(_ id: DeFiActions.UniqueIdentifier?) {
220 self.uniqueID = id
221 }
222 }
223
224 /// Create a new TideManagerWrapper and store it
225 /// Returns a Connector struct that references it
226 access(all) fun createConnectorAndManager(
227 account: auth(Storage, Capabilities) &Account,
228 tideManagerCap: Capability<auth(FungibleToken.Withdraw) &FlowVaults.TideManager>,
229 betaBadgeCap: Capability<auth(FlowVaultsClosedBeta.Beta) &FlowVaultsClosedBeta.BetaBadge>,
230 vaultType: Type,
231 strategyType: Type
232 ): Connector {
233 // Validate that the strategy supports the vault type
234 let supportedVaults = FlowVaults.getSupportedInitializationVaults(forStrategy: strategyType)
235 assert(
236 supportedVaults[vaultType] == true,
237 message: "Strategy does not support vault type"
238 )
239
240 // Create and store the TideManagerWrapper resource
241 let manager <- create TideManagerWrapper(
242 tideManagerCap: tideManagerCap,
243 betaBadgeCap: betaBadgeCap,
244 vaultType: vaultType,
245 strategyType: strategyType
246 )
247
248 account.storage.save(<-manager, to: self.ManagerStoragePath)
249
250 // Create public capability for the manager
251 let managerCap = account.capabilities.storage.issue<&TideManagerWrapper>(self.ManagerStoragePath)
252 account.capabilities.publish(managerCap, at: self.ManagerPublicPath)
253
254 emit ConnectorCreated(
255 managerAddress: account.address,
256 strategyType: strategyType.identifier,
257 vaultType: vaultType.identifier
258 )
259
260 // Return the struct connector that references this manager
261 return Connector(
262 managerAddress: account.address,
263 vaultType: vaultType
264 )
265 }
266
267 init() {
268 let identifier = "flowVaultsTideManager_\(self.account.address)"
269 self.ManagerStoragePath = StoragePath(identifier: identifier)!
270 self.ManagerPublicPath = PublicPath(identifier: identifier)!
271 }
272}
273