Smart Contract
FungibleTokenConnectors
A.0c237e1265caa7a3.FungibleTokenConnectors
1import FungibleToken from 0xf233dcee88fe0abe
2import FungibleTokenMetadataViews from 0xf233dcee88fe0abe
3
4import DeFiActionsUtils from 0x6d888f175c158410
5import DeFiActions from 0x6d888f175c158410
6
7/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
8/// THIS CONTRACT IS IN BETA AND IS NOT FINALIZED - INTERFACES MAY CHANGE AND/OR PENDING CHANGES MAY REQUIRE REDEPLOYMENT
9/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10///
11/// FungibleTokenConnectors
12///
13/// This contract defines generic DeFi Actions Sink & Source connector implementations for use with underlying Vault
14/// Capabilities. These connectors can be used alone or in conjunction with other DeFi Actions connectors to create
15/// complex DeFi workflows.
16///
17access(all) contract FungibleTokenConnectors {
18
19 /// VaultSink
20 ///
21 /// A DeFiActions connector that deposits tokens into a Vault
22 ///
23 access(all) struct VaultSink : DeFiActions.Sink {
24 /// The Vault Type accepted by the Sink
25 access(all) let depositVaultType: Type
26 /// The maximum balance of the linked Vault, checked before executing a deposit
27 access(all) let maximumBalance: UFix64
28 /// An optional identifier allowing protocols to identify stacked connector operations by defining a protocol-
29 /// specific Identifier to associated connectors on construction
30 access(contract) var uniqueID: DeFiActions.UniqueIdentifier?
31 /// An unentitled Capability on the Vault to which deposits are distributed
32 access(self) let depositVault: Capability<&{FungibleToken.Vault}>
33
34 init(
35 max: UFix64?,
36 depositVault: Capability<&{FungibleToken.Vault}>,
37 uniqueID: DeFiActions.UniqueIdentifier?
38 ) {
39 pre {
40 depositVault.check(): "Provided invalid Capability"
41 DeFiActionsUtils.definingContractIsFungibleToken(depositVault.borrow()!.getType()):
42 "The contract defining Vault \(depositVault.borrow()!.getType().identifier) does not conform to FungibleToken contract interface"
43 (max ?? UFix64.max) > 0.0:
44 "Maximum balance must be greater than 0.0 if provided"
45 }
46 self.maximumBalance = max ?? UFix64.max // assume no maximum if none provided
47 self.uniqueID = uniqueID
48 self.depositVaultType = depositVault.borrow()!.getType()
49 self.depositVault = depositVault
50 }
51
52 /// Returns a ComponentInfo struct containing information about this VaultSink and its inner DFA components
53 ///
54 /// @return a ComponentInfo struct containing information about this component and a list of ComponentInfo for
55 /// each inner component in the stack.
56 ///
57 access(all) fun getComponentInfo(): DeFiActions.ComponentInfo {
58 return DeFiActions.ComponentInfo(
59 type: self.getType(),
60 id: self.id(),
61 innerComponents: []
62 )
63 }
64 /// Returns a copy of the struct's UniqueIdentifier, used in extending a stack to identify another connector in
65 /// a DeFiActions stack. See DeFiActions.align() for more information.
66 ///
67 /// @return a copy of the struct's UniqueIdentifier
68 ///
69 access(contract) view fun copyID(): DeFiActions.UniqueIdentifier? {
70 return self.uniqueID
71 }
72 /// Sets the UniqueIdentifier of this component to the provided UniqueIdentifier, used in extending a stack to
73 /// identify another connector in a DeFiActions stack. See DeFiActions.align() for more information.
74 ///
75 /// @param id: the UniqueIdentifier to set for this component
76 ///
77 access(contract) fun setID(_ id: DeFiActions.UniqueIdentifier?) {
78 self.uniqueID = id
79 }
80 /// Returns the Vault type accepted by this Sink
81 access(all) view fun getSinkType(): Type {
82 return self.depositVaultType
83 }
84 /// Returns an estimate of how much of the associated Vault can be accepted by this Sink
85 access(all) fun minimumCapacity(): UFix64 {
86 if let vault = self.depositVault.borrow() {
87 return vault.balance < self.maximumBalance ? self.maximumBalance - vault.balance : 0.0
88 }
89 return 0.0
90 }
91 /// Deposits up to the Sink's capacity from the provided Vault
92 access(all) fun depositCapacity(from: auth(FungibleToken.Withdraw) &{FungibleToken.Vault}) {
93 let minimumCapacity = self.minimumCapacity()
94 if !self.depositVault.check() || minimumCapacity == 0.0 {
95 return
96 }
97 // deposit the lesser of the originating vault balance and minimum capacity
98 let capacity = minimumCapacity <= from.balance ? minimumCapacity : from.balance
99 self.depositVault.borrow()!.deposit(from: <-from.withdraw(amount: capacity))
100 }
101 }
102
103 /// VaultSource
104 ///
105 /// A DeFiActions connector that withdraws tokens from a Vault
106 ///
107 access(all) struct VaultSource : DeFiActions.Source {
108 /// Returns the Vault type provided by this Source
109 access(all) let withdrawVaultType: Type
110 /// The minimum balance of the linked Vault
111 access(all) let minimumBalance: UFix64
112 /// An optional identifier allowing protocols to identify stacked connector operations by defining a protocol-
113 /// specific Identifier to associated connectors on construction
114 access(contract) var uniqueID: DeFiActions.UniqueIdentifier?
115 /// An entitled Capability on the Vault from which withdrawals are sourced
116 access(self) let withdrawVault: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>
117
118 init(
119 min: UFix64?,
120 withdrawVault: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>,
121 uniqueID: DeFiActions.UniqueIdentifier?
122 ) {
123 pre {
124 withdrawVault.check(): "Provided invalid Capability"
125 DeFiActionsUtils.definingContractIsFungibleToken(withdrawVault.borrow()!.getType()):
126 "The contract defining Vault \(withdrawVault.borrow()!.getType().identifier) does not conform to FungibleToken contract interface"
127 }
128 self.minimumBalance = min ?? 0.0 // assume no minimum if none provided
129 self.withdrawVault = withdrawVault
130 self.uniqueID = uniqueID
131 self.withdrawVaultType = withdrawVault.borrow()!.getType()
132 }
133 /// Returns a ComponentInfo struct containing information about this VaultSource and its inner DFA components
134 ///
135 /// @return a ComponentInfo struct containing information about this component and a list of ComponentInfo for
136 /// each inner component in the stack.
137 ///
138 access(all) fun getComponentInfo(): DeFiActions.ComponentInfo {
139 return DeFiActions.ComponentInfo(
140 type: self.getType(),
141 id: self.id(),
142 innerComponents: []
143 )
144 }
145 /// Returns a copy of the struct's UniqueIdentifier, used in extending a stack to identify another connector in
146 /// a DeFiActions stack. See DeFiActions.align() for more information.
147 ///
148 /// @return a copy of the struct's UniqueIdentifier
149 ///
150 access(contract) view fun copyID(): DeFiActions.UniqueIdentifier? {
151 return self.uniqueID
152 }
153 /// Sets the UniqueIdentifier of this component to the provided UniqueIdentifier, used in extending a stack to
154 /// identify another connector in a DeFiActions stack. See DeFiActions.align() for more information.
155 ///
156 /// @param id: the UniqueIdentifier to set for this component
157 ///
158 access(contract) fun setID(_ id: DeFiActions.UniqueIdentifier?) {
159 self.uniqueID = id
160 }
161 /// Returns the Vault type provided by this Source
162 access(all) view fun getSourceType(): Type {
163 return self.withdrawVaultType
164 }
165 /// Returns an estimate of how much of the associated Vault can be provided by this Source
166 access(all) fun minimumAvailable(): UFix64 {
167 if let vault = self.withdrawVault.borrow() {
168 return self.minimumBalance < vault.balance ? vault.balance - self.minimumBalance : 0.0
169 }
170 return 0.0
171 }
172 /// Withdraws the lesser of maxAmount or minimumAvailable(). If none is available, an empty Vault should be
173 /// returned
174 access(FungibleToken.Withdraw) fun withdrawAvailable(maxAmount: UFix64): @{FungibleToken.Vault} {
175 let available = self.minimumAvailable()
176 if !self.withdrawVault.check() || available == 0.0 || maxAmount == 0.0 {
177 return <- DeFiActionsUtils.getEmptyVault(self.withdrawVaultType)
178 }
179 // take the lesser between the available and maximum requested amount
180 let withdrawalAmount = available <= maxAmount ? available : maxAmount
181 return <- self.withdrawVault.borrow()!.withdraw(amount: withdrawalAmount)
182 }
183 }
184
185 /// VaultSinkAndSource
186 ///
187 /// A DeFiActions connector that both deposits and withdraws tokens from a Vault
188 ///
189 access(all) struct VaultSinkAndSource : DeFiActions.Sink, DeFiActions.Source {
190 /// The minimum balance of the linked Vault
191 access(all) let minimumBalance: UFix64
192 /// The maximum balance of the linked Vault
193 access(all) let maximumBalance: UFix64
194 /// The type of Vault this connector accepts (as a Sink) and provides (as a Source)
195 access(all) let vaultType: Type
196 /// An optional identifier allowing protocols to identify stacked connector operations by defining a protocol-
197 /// specific Identifier to associated connectors on construction
198 access(contract) var uniqueID: DeFiActions.UniqueIdentifier?
199 /// An entitled Capability on the Vault from which withdrawals are sourced & deposit are routed
200 access(self) let vault: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>
201
202 init(
203 min: UFix64?,
204 max: UFix64?,
205 vault: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>,
206 uniqueID: DeFiActions.UniqueIdentifier?
207 ) {
208 pre {
209 vault.check(): "Invalid Vault Capability provided"
210 DeFiActionsUtils.definingContractIsFungibleToken(vault.borrow()!.getType()):
211 "The contract defining Vault \(vault.borrow()!.getType().identifier) does not conform to FungibleToken contract interface"
212 min ?? 0.0 < max ?? UFix64.max:
213 "Minimum balance must be less than maximum balance if either is declared"
214 }
215 self.minimumBalance = min ?? 0.0
216 self.maximumBalance = max ?? UFix64.max
217 self.vaultType = vault.borrow()!.getType()
218 self.uniqueID = uniqueID
219 self.vault = vault
220 }
221
222 /// Returns a ComponentInfo struct containing information about this VaultSinkAndSource and its inner DFA components
223 ///
224 /// @return a ComponentInfo struct containing information about this component and a list of ComponentInfo for
225 /// each inner component in the stack.
226 ///
227 access(all) fun getComponentInfo(): DeFiActions.ComponentInfo {
228 return DeFiActions.ComponentInfo(
229 type: self.getType(),
230 id: self.id(),
231 innerComponents: []
232 )
233 }
234 /// Returns a copy of the struct's UniqueIdentifier, used in extending a stack to identify another connector in
235 /// a DeFiActions stack. See DeFiActions.align() for more information.
236 ///
237 /// @return a copy of the struct's UniqueIdentifier
238 ///
239 access(contract) view fun copyID(): DeFiActions.UniqueIdentifier? {
240 return self.uniqueID
241 }
242
243 /// Sets the UniqueIdentifier of this component to the provided UniqueIdentifier, used in extending a stack to
244 /// identify another connector in a DeFiActions stack. See DeFiActions.align() for more information.
245 ///
246 /// @param id: the UniqueIdentifier to set for this component
247 ///
248 access(contract) fun setID(_ id: DeFiActions.UniqueIdentifier?) {
249 self.uniqueID = id
250 }
251 /// Returns the Vault type accepted by this Sink
252 access(all) view fun getSinkType(): Type {
253 return self.vaultType
254 }
255 /// Returns the Vault type provided by this Source
256 access(all) view fun getSourceType(): Type {
257 return self.vaultType
258 }
259 /// Returns an estimate of how much of the associated Vault can be accepted by this Sink
260 access(all) fun minimumCapacity(): UFix64 {
261 if let vault = self.vault.borrow() {
262 return vault.balance < self.maximumBalance ? self.maximumBalance - vault.balance : 0.0
263 }
264 return 0.0
265 }
266 /// Returns an estimate of how much of the associated Vault can be provided by this Source
267 access(all) fun minimumAvailable(): UFix64 {
268 if let vault = self.vault.borrow() {
269 return self.minimumBalance < vault.balance ? vault.balance - self.minimumBalance : 0.0
270 }
271 return 0.0
272 }
273 /// Deposits up to the Sink's capacity from the provided Vault
274 access(all) fun depositCapacity(from: auth(FungibleToken.Withdraw) &{FungibleToken.Vault}) {
275 let minimumCapacity = self.minimumCapacity()
276 if !self.vault.check() || minimumCapacity == 0.0 {
277 return
278 }
279 // deposit the lesser of the originating vault balance and minimum capacity
280 let capacity = minimumCapacity <= from.balance ? minimumCapacity : from.balance
281 self.vault.borrow()!.deposit(from: <-from.withdraw(amount: capacity))
282 }
283 /// Withdraws the lesser of maxAmount or minimumAvailable(). If none is available, an empty Vault should be
284 /// returned
285 access(FungibleToken.Withdraw) fun withdrawAvailable(maxAmount: UFix64): @{FungibleToken.Vault} {
286 let available = self.minimumAvailable()
287 if !self.vault.check() || available == 0.0 || maxAmount == 0.0 {
288 return <- DeFiActionsUtils.getEmptyVault(self.vaultType)
289 }
290 // take the lesser between the available and maximum requested amount
291 let withdrawalAmount = available <= maxAmount ? available : maxAmount
292 return <- self.vault.borrow()!.withdraw(amount: withdrawalAmount)
293 }
294 }
295}
296