Smart Contract
FlowVaultsAutoBalancers
A.b1d63873c3cc9f79.FlowVaultsAutoBalancers
1// standards
2import Burner from 0xf233dcee88fe0abe
3import FungibleToken from 0xf233dcee88fe0abe
4// DeFiActions
5import DeFiActions from 0x6d888f175c158410
6import FlowTransactionScheduler from 0xe467b9dd11fa00df
7
8/// FlowVaultsAutoBalancers
9///
10/// This contract deals with the storage, retrieval and cleanup of DeFiActions AutoBalancers as they are used in
11/// FlowVaults defined Strategies.
12///
13/// AutoBalancers are stored in contract account storage at paths derived by their related DeFiActions.UniqueIdentifier.id
14/// which identifies all DeFiActions components in the stack related to their composite Strategy.
15///
16/// When a Tide and necessarily the related Strategy is closed & burned, the related AutoBalancer and its Capabilities
17/// are destroyed and deleted
18///
19access(all) contract FlowVaultsAutoBalancers {
20
21 /// The path prefix used for StoragePath & PublicPath derivations
22 access(all) let pathPrefix: String
23
24 /* --- PUBLIC METHODS --- */
25
26 /// Returns the path (StoragePath or PublicPath) at which an AutoBalancer is stored with the associated
27 /// UniqueIdentifier.id.
28 access(all) view fun deriveAutoBalancerPath(id: UInt64, storage: Bool): Path {
29 return storage ? StoragePath(identifier: "\(self.pathPrefix)\(id)")! : PublicPath(identifier: "\(self.pathPrefix)\(id)")!
30 }
31
32 /// Returns an unauthorized reference to an AutoBalancer with the given UniqueIdentifier.id value. If none is
33 /// configured, `nil` will be returned.
34 access(all) fun borrowAutoBalancer(id: UInt64): &DeFiActions.AutoBalancer? {
35 let publicPath = self.deriveAutoBalancerPath(id: id, storage: false) as! PublicPath
36 return self.account.capabilities.borrow<&DeFiActions.AutoBalancer>(publicPath)
37 }
38
39 /* --- INTERNAL METHODS --- */
40
41 /// Configures a new AutoBalancer in storage, configures its public Capability, and sets its inner authorized
42 /// Capability. If an AutoBalancer is stored with an associated UniqueID value, the operation reverts.
43 access(account) fun _initNewAutoBalancer(
44 oracle: {DeFiActions.PriceOracle},
45 vaultType: Type,
46 lowerThreshold: UFix64,
47 upperThreshold: UFix64,
48 rebalanceSink: {DeFiActions.Sink}?,
49 rebalanceSource: {DeFiActions.Source}?,
50 uniqueID: DeFiActions.UniqueIdentifier
51 ): auth(DeFiActions.Auto, DeFiActions.Set, DeFiActions.Get, FungibleToken.Withdraw) &DeFiActions.AutoBalancer {
52
53 // derive paths & prevent collision
54 let storagePath = self.deriveAutoBalancerPath(id: uniqueID.id, storage: true) as! StoragePath
55 let publicPath = self.deriveAutoBalancerPath(id: uniqueID.id, storage: false) as! PublicPath
56 var storedType = self.account.storage.type(at: storagePath)
57 var publishedCap = self.account.capabilities.exists(publicPath)
58 assert(storedType == nil,
59 message: "Storage collision when creating AutoBalancer for UniqueIdentifier.id \(uniqueID.id) at path \(storagePath)")
60 assert(!publishedCap,
61 message: "Published Capability collision found when publishing AutoBalancer for UniqueIdentifier.id \(uniqueID.id) at path \(publicPath)")
62
63 // create & save AutoBalancer
64 let autoBalancer <- DeFiActions.createAutoBalancer(
65 oracle: oracle,
66 vaultType: vaultType,
67 lowerThreshold: lowerThreshold,
68 upperThreshold: upperThreshold,
69 rebalanceSink: rebalanceSink,
70 rebalanceSource: rebalanceSource,
71 recurringConfig: nil,
72 uniqueID: uniqueID
73 )
74 self.account.storage.save(<-autoBalancer, to: storagePath)
75 let autoBalancerRef = self._borrowAutoBalancer(uniqueID.id)
76
77 // issue & publish public capability
78 let publicCap = self.account.capabilities.storage.issue<&DeFiActions.AutoBalancer>(storagePath)
79 self.account.capabilities.publish(publicCap, at: publicPath)
80
81 // issue private capability & set within AutoBalancer
82 let authorizedCap = self.account.capabilities.storage.issue<auth(FungibleToken.Withdraw, FlowTransactionScheduler.Execute) &DeFiActions.AutoBalancer>(storagePath)
83 autoBalancerRef.setSelfCapability(authorizedCap)
84
85 // ensure proper configuration before closing
86 storedType = self.account.storage.type(at: storagePath)
87 publishedCap = self.account.capabilities.exists(publicPath)
88 assert(storedType == Type<@DeFiActions.AutoBalancer>(),
89 message: "Error when configuring AutoBalancer for UniqueIdentifier.id \(uniqueID.id) at path \(storagePath)")
90 assert(publishedCap,
91 message: "Error when publishing AutoBalancer Capability for UniqueIdentifier.id \(uniqueID.id) at path \(publicPath)")
92 return autoBalancerRef
93 }
94
95 /// Returns an authorized reference on the AutoBalancer with the associated UniqueIdentifier.id. If none is found,
96 /// the operation reverts.
97 access(account)
98 fun _borrowAutoBalancer(_ id: UInt64): auth(DeFiActions.Auto, DeFiActions.Set, DeFiActions.Get, FungibleToken.Withdraw) &DeFiActions.AutoBalancer {
99 let storagePath = self.deriveAutoBalancerPath(id: id, storage: true) as! StoragePath
100 return self.account.storage.borrow<auth(DeFiActions.Auto, DeFiActions.Set, DeFiActions.Get, FungibleToken.Withdraw) &DeFiActions.AutoBalancer>(
101 from: storagePath
102 ) ?? panic("Could not borrow reference to AutoBalancer with UniqueIdentifier.id \(id) from StoragePath \(storagePath)")
103 }
104
105 /// Called by strategies defined in the FlowVaults account which leverage account-hosted AutoBalancers when a
106 /// Strategy is burned
107 access(account) fun _cleanupAutoBalancer(id: UInt64) {
108 let storagePath = self.deriveAutoBalancerPath(id: id, storage: true) as! StoragePath
109 let publicPath = self.deriveAutoBalancerPath(id: id, storage: false) as! PublicPath
110 // unpublish the public AutoBalancer Capability
111 self.account.capabilities.unpublish(publicPath)
112 // delete any CapabilityControllers targetting the AutoBalancer
113 self.account.capabilities.storage.forEachController(forPath: storagePath, fun(_ controller: &StorageCapabilityController): Bool {
114 controller.delete()
115 return true
116 })
117 // load & burn the AutoBalancer
118 let autoBalancer <-self.account.storage.load<@DeFiActions.AutoBalancer>(from: storagePath)
119 Burner.burn(<-autoBalancer)
120 }
121
122 init() {
123 self.pathPrefix = "FlowVaultsAutoBalancer_"
124 }
125}
126