Smart Contract

DeFiActions

A.92195d814edf9cb0.DeFiActions

Valid From

122,643,819

Deployed

2w ago
Feb 11, 2026, 06:45:38 PM UTC

Dependents

38 imports
1import Burner from 0xf233dcee88fe0abe
2import ViewResolver from 0x1d7e57aa55817448
3import FungibleToken from 0xf233dcee88fe0abe
4
5import DeFiActionsUtils from 0x92195d814edf9cb0
6import DeFiActionsMathUtils from 0x92195d814edf9cb0
7
8/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
9/// THIS CONTRACT IS IN BETA AND IS NOT FINALIZED - INTERFACES MAY CHANGE AND/OR PENDING CHANGES MAY REQUIRE REDEPLOYMENT
10/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
11///
12/// [BETA] DeFiActions
13///
14/// DeFiActions is a library of small DeFi components that act as glue to connect typical DeFi primitives (dexes, lending
15/// pools, farms) into individual aggregations.
16///
17/// The core component of DeFiActions is the “Connector”; a conduit between the more complex pieces of the DeFi puzzle.
18/// Connectors aren't to do anything especially complex, but make it simple and straightforward to connect the
19/// traditional DeFi pieces together into new, custom aggregations.
20///
21/// Connectors should be thought of analogously with the small text processing tools of Unix that are mostly meant to be
22/// connected with pipe operations instead of being operated individually. All Connectors are either a “Source” or
23/// “Sink”.
24///
25access(all) contract DeFiActions {
26
27    /* --- FIELDS --- */
28
29    /// The current ID assigned to UniqueIdentifiers as they are initialized
30    /// It is incremented by 1 every time a UniqueIdentifier is created so each ID is only ever used once
31    access(all) var currentID: UInt64
32    /// The AuthenticationToken Capability required to create a UniqueIdentifier
33    access(self) let authTokenCap: Capability<auth(Identify) &AuthenticationToken>
34    /// The StoragePath for the AuthenticationToken resource
35    access(self) let AuthTokenStoragePath: StoragePath
36
37    /* --- INTERFACE-LEVEL EVENTS --- */
38
39    /// Emitted when value is deposited to a Sink
40    access(all) event Deposited(
41        type: String,
42        amount: UFix64,
43        fromUUID: UInt64,
44        uniqueID: UInt64?,
45        sinkType: String
46    )
47    /// Emitted when value is withdrawn from a Source
48    access(all) event Withdrawn(
49        type: String,
50        amount: UFix64,
51        withdrawnUUID: UInt64,
52        uniqueID: UInt64?,
53        sourceType: String
54    )
55    /// Emitted when a Swapper executes a Swap
56    access(all) event Swapped(
57        inVault: String,
58        outVault: String,
59        inAmount: UFix64,
60        outAmount: UFix64,
61        inUUID: UInt64,
62        outUUID: UInt64,
63        uniqueID: UInt64?,
64        swapperType: String
65    )
66    /// Emitted when a Flasher executes a flash loan
67    access(all) event Flashed(
68        requestedAmount: UFix64,
69        borrowType: String,
70        uniqueID: UInt64?,
71        flasherType: String
72    )
73    /// Emitted when an IdentifiableResource's UniqueIdentifier is aligned with another DFA component
74    access(all) event UpdatedID(
75        oldID: UInt64?,
76        newID: UInt64?,
77        component: String,
78        uuid: UInt64?
79    )
80    /// Emitted when an AutoBalancer is created
81    access(all) event CreatedAutoBalancer(
82        lowerThreshold: UFix64,
83        upperThreshold: UFix64,
84        vaultType: String,
85        vaultUUID: UInt64,
86        uuid: UInt64,
87        uniqueID: UInt64?
88    )
89    /// Emitted when AutoBalancer.rebalance() is called
90    access(all) event Rebalanced(
91        amount: UFix64,
92        value: UFix64,
93        unitOfAccount: String,
94        isSurplus: Bool,
95        vaultType: String,
96        vaultUUID: UInt64,
97        balancerUUID: UInt64,
98        address: Address?,
99        uuid: UInt64,
100        uniqueID: UInt64?
101    )
102
103    /* --- CONSTRUCTS --- */
104
105    access(all) entitlement Identify
106
107    /// AuthenticationToken
108    ///
109    /// A resource intended to ensure UniqueIdentifiers are only created by the DeFiActions contract
110    ///
111    access(all) resource AuthenticationToken {}
112
113    /// UniqueIdentifier
114    ///
115    /// This construct enables protocols to trace stack operations via DeFiActions interface-level events, identifying
116    /// them by UniqueIdentifier IDs. IdentifiableResource Implementations should ensure that access to them is
117    /// encapsulated by the structures they are used to identify.
118    ///
119    access(all) struct UniqueIdentifier {
120        /// The ID value of this UniqueIdentifier
121        access(all) let id: UInt64
122        /// The AuthenticationToken Capability required to create this UniqueIdentifier. Since this is a struct which
123        /// can be created in any context, this authorized Capability ensures that the UniqueIdentifier can only be
124        /// created by the DeFiActions contract, thus preventing forged UniqueIdentifiers from being created.
125        access(self) let authCap: Capability<auth(Identify) &AuthenticationToken>
126
127        access(contract) view init(_ id: UInt64, _ authCap: Capability<auth(Identify) &AuthenticationToken>) {
128            pre {
129                authCap.check(): "Invalid AuthenticationToken Capability provided"
130            }
131            self.id = id
132            self.authCap = authCap
133        }
134    }
135
136    /// ComponentInfo
137    ///
138    /// A struct containing minimal information about a DeFiActions component and its inner components
139    ///
140    access(all) struct ComponentInfo {
141        /// The type of the component
142        access(all) let type: Type
143        /// The UniqueIdentifier.id of the component
144        access(all) let id: UInt64?
145        /// The inner component types of the serving component
146        access(all) let innerComponents: [ComponentInfo]
147        init(
148            type: Type,
149            id: UInt64?,
150            innerComponents: [ComponentInfo]
151        ) {
152            self.type = type
153            self.id = id
154            self.innerComponents = innerComponents
155        }
156    }
157
158    /// Extend entitlement allowing for the authorized copying of UniqueIdentifiers from existing components
159    access(all) entitlement Extend
160
161    /// IdentifiableResource
162    ///
163    /// A resource interface containing a UniqueIdentifier and convenience getters about it
164    ///
165    access(all) struct interface IdentifiableStruct {
166        /// An optional identifier allowing protocols to identify stacked connector operations by defining a protocol-
167        /// specific Identifier to associated connectors on construction
168        access(contract) var uniqueID: UniqueIdentifier?
169        /// Convenience method returning the inner UniqueIdentifier's id or `nil` if none is set.
170        ///
171        /// NOTE: This interface method may be spoofed if the function is overridden, so callers should not rely on it
172        /// for critical identification unless the implementation itself is known and trusted
173        access(all) view fun id(): UInt64? {
174            return self.uniqueID?.id
175        }
176        /// Returns a ComponentInfo struct containing information about this component and a list of ComponentInfo for
177        /// each inner component in the stack.
178        access(all) fun getComponentInfo(): ComponentInfo
179        /// Returns a copy of the struct's UniqueIdentifier, used in extending a stack to identify another connector in
180        /// a DeFiActions stack. See DeFiActions.align() for more information.
181        access(contract) view fun copyID(): UniqueIdentifier? {
182            post {
183                result?.id == self.uniqueID?.id:
184                "UniqueIdentifier of \(self.getType().identifier) was not successfully copied"
185            }
186        }
187        /// Sets the UniqueIdentifier of this component to the provided UniqueIdentifier, used in extending a stack to
188        /// identify another connector in a DeFiActions stack. See DeFiActions.align() for more information.
189        access(contract) fun setID(_ id: UniqueIdentifier?) {
190            post {
191                self.uniqueID?.id == id?.id:
192                "UniqueIdentifier of \(self.getType().identifier) was not successfully set"
193                DeFiActions.emitUpdatedID(
194                    oldID: before(self.uniqueID?.id),
195                    newID: self.uniqueID?.id,
196                    component: self.getType().identifier,
197                    uuid: nil // no UUID for structs
198                ): "Unknown error emitting DeFiActions.UpdatedID from IdentifiableStruct \(self.getType().identifier) with ID ".concat(self.id()?.toString() ?? "UNASSIGNED")
199            }
200        }
201    }
202
203    /// IdentifiableResource
204    ///
205    /// A resource interface containing a UniqueIdentifier and convenience getters about it
206    ///
207    access(all) resource interface IdentifiableResource {
208        /// An optional identifier allowing protocols to identify stacked connector operations by defining a protocol-
209        /// specific Identifier to associated connectors on construction
210        access(contract) var uniqueID: UniqueIdentifier?
211        /// Convenience method returning the inner UniqueIdentifier's id or `nil` if none is set.
212        ///
213        /// NOTE: This interface method may be spoofed if the function is overridden, so callers should not rely on it
214        /// for critical identification unless the implementation itself is known and trusted
215        access(all) view fun id(): UInt64? {
216            return self.uniqueID?.id
217        }
218        /// Returns a ComponentInfo struct containing information about this component and a list of ComponentInfo for
219        /// each inner component in the stack.
220        access(all) fun getComponentInfo(): ComponentInfo
221        /// Returns a copy of the struct's UniqueIdentifier, used in extending a stack to identify another connector in
222        /// a DeFiActions stack. See DeFiActions.align() for more information.
223        access(contract) view fun copyID(): UniqueIdentifier? {
224            post {
225                result?.id == self.uniqueID?.id:
226                "UniqueIdentifier of \(self.getType().identifier) was not successfully copied"
227            }
228        }
229        /// Sets the UniqueIdentifier of this component to the provided UniqueIdentifier, used in extending a stack to
230        /// identify another connector in a DeFiActions stack. See DeFiActions.align() for more information.
231        access(contract) fun setID(_ id: UniqueIdentifier?) {
232            post {
233                self.uniqueID?.id == id?.id:
234                "UniqueIdentifier of \(self.getType().identifier) was not successfully set"
235                DeFiActions.emitUpdatedID(
236                    oldID: before(self.uniqueID?.id),
237                    newID: self.uniqueID?.id,
238                    component: self.getType().identifier,
239                    uuid: self.uuid
240                ): "Unknown error emitting DeFiActions.UpdatedID from IdentifiableStruct \(self.getType().identifier) with ID ".concat(self.id()?.toString() ?? "UNASSIGNED")
241            }
242        }
243    }
244
245    /// Sink
246    ///
247    /// A Sink Connector (or just “Sink”) is analogous to the Fungible Token Receiver interface that accepts deposits of
248    /// funds. It differs from the standard Receiver interface in that it is a struct interface (instead of resource
249    /// interface) and allows for the graceful handling of Sinks that have a limited capacity on the amount they can
250    /// accept for deposit. Implementations should therefore avoid the possibility of reversion with graceful fallback
251    /// on unexpected conditions, executing no-ops instead of reverting.
252    ///
253    access(all) struct interface Sink : IdentifiableStruct {
254        /// Returns the Vault type accepted by this Sink
255        access(all) view fun getSinkType(): Type
256        /// Returns an estimate of how much can be withdrawn from the depositing Vault for this Sink to reach capacity
257        access(all) fun minimumCapacity(): UFix64
258        /// Deposits up to the Sink's capacity from the provided Vault
259        access(all) fun depositCapacity(from: auth(FungibleToken.Withdraw) &{FungibleToken.Vault}) {
260            pre {
261                from.getType() == self.getSinkType():
262                "Invalid vault provided for deposit - \(from.getType().identifier) is not \(self.getSinkType().identifier)"
263            }
264            post {
265                DeFiActions.emitDeposited(
266                    type: from.getType().identifier,
267                    beforeBalance: before(from.balance),
268                    afterBalance: from.balance,
269                    fromUUID: from.uuid,
270                    uniqueID: self.uniqueID?.id,
271                    sinkType: self.getType().identifier
272                ): "Unknown error emitting DeFiActions.Withdrawn from Sink \(self.getType().identifier) with ID ".concat(self.id()?.toString() ?? "UNASSIGNED")
273            }
274        }
275    }
276
277    /// Source
278    ///
279    /// A Source Connector (or just “Source”) is analogous to the Fungible Token Provider interface that provides funds
280    /// on demand. It differs from the standard Provider interface in that it is a struct interface (instead of resource
281    /// interface) and allows for graceful handling of the case that the Source might not know exactly the total amount
282    /// of funds available to be withdrawn. Implementations should therefore avoid the possibility of reversion with
283    /// graceful fallback on unexpected conditions, executing no-ops or returning an empty Vault instead of reverting.
284    ///
285    access(all) struct interface Source : IdentifiableStruct {
286        /// Returns the Vault type provided by this Source
287        access(all) view fun getSourceType(): Type
288        /// Returns an estimate of how much of the associated Vault Type can be provided by this Source
289        access(all) fun minimumAvailable(): UFix64
290        /// Withdraws the lesser of maxAmount or minimumAvailable(). If none is available, an empty Vault should be
291        /// returned
292        access(FungibleToken.Withdraw) fun withdrawAvailable(maxAmount: UFix64): @{FungibleToken.Vault} {
293            post {
294                result.getType() == self.getSourceType():
295                "Invalid vault provided for withdraw - \(result.getType().identifier) is not \(self.getSourceType().identifier)"
296                DeFiActions.emitWithdrawn(
297                    type: result.getType().identifier,
298                    amount: result.balance,
299                    withdrawnUUID: result.uuid,
300                    uniqueID: self.uniqueID?.id ?? nil,
301                    sourceType: self.getType().identifier
302                ): "Unknown error emitting DeFiActions.Withdrawn from Source \(self.getType().identifier) with ID ".concat(self.id()?.toString() ?? "UNASSIGNED")
303            }
304        }
305    }
306
307    /// Quote
308    ///
309    /// An interface for an estimate to be returned by a Swapper when asking for a swap estimate. This may be helpful
310    /// for passing additional parameters to a Swapper relevant to the use case. Implementations may choose to add
311    /// fields relevant to their Swapper implementation and downcast in swap() and/or swapBack() scope.
312    ///
313    access(all) struct interface Quote {
314        /// The quoted pre-swap Vault type
315        access(all) let inType: Type
316        /// The quoted post-swap Vault type
317        access(all) let outType: Type
318        /// The quoted amount of pre-swap currency
319        access(all) let inAmount: UFix64
320        /// The quoted amount of post-swap currency for the defined inAmount
321        access(all) let outAmount: UFix64
322    }
323
324    /// Swapper
325    ///
326    /// A basic interface for a struct that swaps between tokens. Implementations may choose to adapt this interface
327    /// to fit any given swap protocol or set of protocols.
328    ///
329    access(all) struct interface Swapper : IdentifiableStruct {
330        /// The type of Vault this Swapper accepts when performing a swap
331        access(all) view fun inType(): Type
332        /// The type of Vault this Swapper provides when performing a swap
333        access(all) view fun outType(): Type
334        /// The estimated amount required to provide a Vault with the desired output balance
335        access(all) fun quoteIn(forDesired: UFix64, reverse: Bool): {Quote} // fun quoteIn/Out
336        /// The estimated amount delivered out for a provided input balance
337        access(all) fun quoteOut(forProvided: UFix64, reverse: Bool): {Quote}
338        /// Performs a swap taking a Vault of type inVault, outputting a resulting outVault. Implementations may choose
339        /// to swap along a pre-set path or an optimal path of a set of paths or even set of contained Swappers adapted
340        /// to use multiple Flow swap protocols.
341        access(all) fun swap(quote: {Quote}?, inVault: @{FungibleToken.Vault}): @{FungibleToken.Vault} {
342            pre {
343                inVault.getType() == self.inType():
344                "Invalid vault provided for swap - \(inVault.getType().identifier) is not \(self.inType().identifier)"
345                (quote?.inType ?? inVault.getType()) == inVault.getType():
346                "Quote.inType type \(quote!.inType.identifier) does not match the provided inVault \(inVault.getType().identifier)"
347            }
348            post {
349                emit Swapped(
350                    inVault: before(inVault.getType().identifier),
351                    outVault: result.getType().identifier,
352                    inAmount: before(inVault.balance),
353                    outAmount: result.balance,
354                    inUUID: before(inVault.uuid),
355                    outUUID: result.uuid,
356                    uniqueID: self.uniqueID?.id ?? nil,
357                    swapperType: self.getType().identifier
358                )
359            }
360        }
361        /// Performs a swap taking a Vault of type outVault, outputting a resulting inVault. Implementations may choose
362        /// to swap along a pre-set path or an optimal path of a set of paths or even set of contained Swappers adapted
363        /// to use multiple Flow swap protocols.
364        // TODO: Impl detail - accept quote that was just used by swap() but reverse the direction assuming swap() was just called
365        access(all) fun swapBack(quote: {Quote}?, residual: @{FungibleToken.Vault}): @{FungibleToken.Vault} {
366            pre {
367                residual.getType() == self.outType():
368                "Invalid vault provided for swapBack - \(residual.getType().identifier) is not \(self.outType().identifier)"
369            }
370            post {
371                emit Swapped(
372                    inVault: before(residual.getType().identifier),
373                    outVault: result.getType().identifier,
374                    inAmount: before(residual.balance),
375                    outAmount: result.balance,
376                    inUUID: before(residual.uuid),
377                    outUUID: result.uuid,
378                    uniqueID: self.uniqueID?.id ?? nil,
379                    swapperType: self.getType().identifier
380                )
381            }
382        }
383    }
384
385    /// PriceOracle
386    ///
387    /// An interface for a price oracle adapter. Implementations should adapt this interface to various price feed
388    /// oracles deployed on Flow
389    ///
390    access(all) struct interface PriceOracle : IdentifiableStruct {
391        /// Returns the asset type serving as the price basis - e.g. USD in FLOW/USD
392        access(all) view fun unitOfAccount(): Type
393        /// Returns the latest price data for a given asset denominated in unitOfAccount() if available, otherwise `nil`
394        /// should be returned. Callers should note that although an optional is supported, implementations may choose
395        /// to revert.
396        access(all) fun price(ofToken: Type): UFix64?
397    }
398
399    /// Flasher
400    ///
401    /// An interface for a flash loan adapter. Implementations should adapt this interface to various flash loan
402    /// protocols deployed on Flow
403    ///
404    access(all) struct interface Flasher : IdentifiableStruct {
405        /// Returns the asset type this Flasher can issue as a flash loan
406        access(all) view fun borrowType(): Type
407        /// Returns the estimated fee for a flash loan of the specified amount
408        access(all) fun calculateFee(loanAmount: UFix64): UFix64
409        /// Performs a flash loan of the specified amount. The callback function is passed the fee amount, a Vault
410        /// containing the loan, and the data. The callback function should return a Vault containing the loan + fee.
411        access(all) fun flashLoan(
412            amount: UFix64,
413            data: AnyStruct?,
414            callback: fun(UFix64, @{FungibleToken.Vault}, AnyStruct?): @{FungibleToken.Vault} // fee, loan, data
415        ) {
416            post {
417                emit Flashed(
418                    requestedAmount: amount,
419                    borrowType: self.borrowType().identifier,
420                    uniqueID: self.uniqueID?.id ?? nil,
421                    flasherType: self.getType().identifier
422                )
423            }
424        }
425    }
426
427    /*******************************************************************************************************************
428        NOTICE: The AutoBalancer will extend the FlowCallbackScheduler.CallbackHandler interface which is not yet
429        finalized. To avoid the need for re-deploying with that interface and related fields managing ScheduleCallback
430        structs, the AutoBalancer and its connectors are omitted from the DeFiActions contract on Testnet & Mainnet
431        until the FlowCallbackScheduler contract is available.
432     *******************************************************************************************************************/
433
434    // /// AutoBalancerSink
435    // ///
436    // /// A DeFiActions Sink enabling the deposit of funds to an underlying AutoBalancer resource. As written, this Source
437    // /// may be used with externally defined AutoBalancer implementations
438    // ///
439    // access(all) struct AutoBalancerSink : Sink {
440    //     /// The Type this Sink accepts
441    //     access(self) let type: Type
442    //     /// An authorized Capability on the underlying AutoBalancer where funds are deposited
443    //     access(self) let autoBalancer: Capability<&AutoBalancer>
444    //     /// An optional identifier allowing protocols to identify stacked connector operations by defining a protocol-
445    //     /// specific Identifier to associated connectors on construction
446    //     access(contract) var uniqueID: UniqueIdentifier?
447
448    //     init(autoBalancer: Capability<&AutoBalancer>, uniqueID: UniqueIdentifier?) {
449    //         pre {
450    //             autoBalancer.check():
451    //             "Invalid AutoBalancer Capability Provided"
452    //         }
453    //         self.type = autoBalancer.borrow()!.vaultType()
454    //         self.autoBalancer = autoBalancer
455    //         self.uniqueID = uniqueID
456    //     }
457
458    //     /// Returns the Vault type accepted by this Sink
459    //     access(all) view fun getSinkType(): Type {
460    //         return self.type
461    //     }
462    //     /// Returns an estimate of how much can be withdrawn from the depositing Vault for this Sink to reach capacity
463    //     /// can currently only be UFix64.max or 0.0
464    //     access(all) fun minimumCapacity(): UFix64 {
465    //         return self.autoBalancer.check() ? UFix64.max : 0.0
466    //     }
467    //     /// Deposits up to the Sink's capacity from the provided Vault
468    //     access(all) fun depositCapacity(from: auth(FungibleToken.Withdraw) &{FungibleToken.Vault}) {
469    //         if let ab = self.autoBalancer.borrow() {
470    //             ab.deposit(from: <-from.withdraw(amount: from.balance))
471    //         }
472    //         return
473    //     }
474    //     /// Returns a ComponentInfo struct containing information about this component and a list of ComponentInfo for
475    //     /// each inner component in the stack.
476    //     access(all) fun getComponentInfo(): ComponentInfo {
477    //         return ComponentInfo(
478    //             type: self.getType(),
479    //             id: self.id(),
480    //             innerComponents: []
481    //         )
482    //     }
483    //     /// Returns a copy of the struct's UniqueIdentifier, used in extending a stack to identify another connector in
484    //     /// a DeFiActions stack. See DeFiActions.align() for more information.
485    //     access(contract) view fun copyID(): UniqueIdentifier? {
486    //         return self.uniqueID
487    //     }
488    //     /// Sets the UniqueIdentifier of this component to the provided UniqueIdentifier, used in extending a stack to
489    //     /// identify another connector in a DeFiActions stack. See DeFiActions.align() for more information.
490    //     access(contract) fun setID(_ id: UniqueIdentifier?) {
491    //         self.uniqueID = id
492    //     }
493    // }
494
495    // /// AutoBalancerSource
496    // ///
497    // /// A DeFiActions Source targeting an underlying AutoBalancer resource. As written, this Source may be used with
498    // /// externally defined AutoBalancer implementations
499    // ///
500    // access(all) struct AutoBalancerSource : Source {
501    //     /// The Type this Source provides
502    //     access(self) let type: Type
503    //     /// An authorized Capability on the underlying AutoBalancer where funds are sourced
504    //     access(self) let autoBalancer: Capability<auth(FungibleToken.Withdraw) &AutoBalancer>
505    //     /// An optional identifier allowing protocols to identify stacked connector operations by defining a protocol-
506    //     /// specific Identifier to associated connectors on construction
507    //     access(contract) var uniqueID: UniqueIdentifier?
508
509    //     init(autoBalancer: Capability<auth(FungibleToken.Withdraw) &AutoBalancer>, uniqueID: UniqueIdentifier?) {
510    //         pre {
511    //             autoBalancer.check():
512    //             "Invalid AutoBalancer Capability Provided"
513    //         }
514    //         self.type = autoBalancer.borrow()!.vaultType()
515    //         self.autoBalancer = autoBalancer
516    //         self.uniqueID = uniqueID
517    //     }
518
519    //     /// Returns the Vault type provided by this Source
520    //     access(all) view fun getSourceType(): Type {
521    //         return self.type
522    //     }
523    //     /// Returns an estimate of how much of the associated Vault Type can be provided by this Source
524    //     access(all) fun minimumAvailable(): UFix64 {
525    //         if let ab = self.autoBalancer.borrow() {
526    //             return ab.vaultBalance()
527    //         }
528    //         return 0.0
529    //     }
530    //     /// Withdraws the lesser of maxAmount or minimumAvailable(). If none is available, an empty Vault should be
531    //     /// returned
532    //     access(FungibleToken.Withdraw) fun withdrawAvailable(maxAmount: UFix64): @{FungibleToken.Vault} {
533    //         if let ab = self.autoBalancer.borrow() {
534    //             return <-ab.withdraw(
535    //                 amount: maxAmount <= ab.vaultBalance() ? maxAmount : ab.vaultBalance()
536    //             )
537    //         }
538    //         return <- DeFiActionsUtils.getEmptyVault(self.type)
539    //     }
540    //     /// Returns a ComponentInfo struct containing information about this component and a list of ComponentInfo for
541    //     /// each inner component in the stack.
542    //     access(all) fun getComponentInfo(): ComponentInfo {
543    //         return ComponentInfo(
544    //             type: self.getType(),
545    //             id: self.id(),
546    //             innerComponents: []
547    //         )
548    //     }
549    //     /// Returns a copy of the struct's UniqueIdentifier, used in extending a stack to identify another connector in
550    //     /// a DeFiActions stack. See DeFiActions.align() for more information.
551    //     access(contract) view fun copyID(): UniqueIdentifier? {
552    //         return self.uniqueID
553    //     }
554    //     /// Sets the UniqueIdentifier of this component to the provided UniqueIdentifier, used in extending a stack to
555    //     /// identify another connector in a DeFiActions stack. See DeFiActions.align() for more information.
556    //     access(contract) fun setID(_ id: UniqueIdentifier?) {
557    //         self.uniqueID = id
558    //     }
559    // }
560
561    // /// Entitlement used by the AutoBalancer to set inner Sink and Source
562    // access(all) entitlement Auto
563    // access(all) entitlement Set
564    // access(all) entitlement Get
565
566    // /// AutoBalancer
567    // ///
568    // /// A resource designed to enable permissionless rebalancing of value around a wrapped Vault. An
569    // /// AutoBalancer can be a critical component of DeFiActions stacks by allowing for strategies to compound, repay
570    // /// loans or direct accumulated value to other sub-systems and/or user Vaults.
571    // ///
572    // access(all) resource AutoBalancer : IdentifiableResource, FungibleToken.Receiver, FungibleToken.Provider, ViewResolver.Resolver, Burner.Burnable {
573    //     /// The value in deposits & withdrawals over time denominated in oracle.unitOfAccount()
574    //     access(self) var _valueOfDeposits: UFix64
575    //     /// The percentage low and high thresholds defining when a rebalance executes
576    //     /// Index 0 is low, index 1 is high
577    //     access(self) var _rebalanceRange: [UFix64; 2]
578    //     /// Oracle used to track the baseValue for deposits & withdrawals over time
579    //     access(self) let _oracle: {PriceOracle}
580    //     /// The inner Vault's Type captured for the ResourceDestroyed event
581    //     access(self) let _vaultType: Type
582    //     /// Vault used to deposit & withdraw from made optional only so the Vault can be burned via Burner.burn() if the
583    //     /// AutoBalancer is burned and the Vault's burnCallback() can be called in the process
584    //     access(self) var _vault: @{FungibleToken.Vault}?
585    //     /// An optional Sink used to deposit excess funds from the inner Vault once the converted value exceeds the
586    //     /// rebalance range. This Sink may be used to compound yield into a position or direct excess value to an
587    //     /// external Vault
588    //     access(self) var _rebalanceSink: {Sink}?
589    //     /// An optional Source used to deposit excess funds to the inner Vault once the converted value is below the
590    //     /// rebalance range
591    //     access(self) var _rebalanceSource: {Source}?
592    //     /// Capability on this AutoBalancer instance
593    //     access(self) var _selfCap: Capability<auth(FungibleToken.Withdraw) &AutoBalancer>?
594    //     /// An optional UniqueIdentifier tying this AutoBalancer to a given stack
595    //     access(contract) var uniqueID: UniqueIdentifier?
596
597    //     /// Emitted when the AutoBalancer is destroyed
598    //     access(all) event ResourceDestroyed(
599    //         uuid: UInt64 = self.uuid,
600    //         vaultType: String = self._vaultType.identifier,
601    //         balance: UFix64? = self._vault?.balance,
602    //         uniqueID: UInt64? = self.uniqueID?.id
603    //     )
604
605    //     init(
606    //         lower: UFix64,
607    //         upper: UFix64,
608    //         oracle: {PriceOracle},
609    //         vaultType: Type,
610    //         outSink: {Sink}?,
611    //         inSource: {Source}?,
612    //         uniqueID: UniqueIdentifier?
613    //     ) {
614    //         pre {
615    //             lower < upper && 0.01 <= lower && lower < 1.0 && 1.0 < upper && upper < 2.0:
616    //             "Invalid rebalanceRange [lower, upper]: [\(lower), \(upper)] - thresholds must be set such that 0.01 <= lower < 1.0 and 1.0 < upper < 2.0 relative to value of deposits"
617    //             DeFiActionsUtils.definingContractIsFungibleToken(vaultType):
618    //             "The contract defining Vault \(vaultType.identifier) does not conform to FungibleToken contract interface"
619    //         }
620    //         assert(oracle.price(ofToken: vaultType) != nil,
621    //             message: "Provided Oracle \(oracle.getType().identifier) could not provide a price for vault \(vaultType.identifier)")
622    //         self._valueOfDeposits = 0.0
623    //         self._rebalanceRange = [lower, upper]
624    //         self._oracle = oracle
625    //         self._vault <- DeFiActionsUtils.getEmptyVault(vaultType)
626    //         self._vaultType = vaultType
627    //         self._rebalanceSink = outSink
628    //         self._rebalanceSource = inSource
629    //         self._selfCap = nil
630    //         self.uniqueID = uniqueID
631
632    //         emit CreatedAutoBalancer(
633    //             lowerThreshold: lower,
634    //             upperThreshold: upper,
635    //             vaultType: vaultType.identifier,
636    //             vaultUUID: self._borrowVault().uuid,
637    //             uuid: self.uuid,
638    //             uniqueID: self.id()
639    //         )
640    //     }
641
642    //     /* Core AutoBalancer Functionality */
643
644    //     /// Returns the balance of the inner Vault
645    //     ///
646    //     /// @return the current balance of the inner Vault
647    //     ///
648    //     access(all) view fun vaultBalance(): UFix64 {
649    //         return self._borrowVault().balance
650    //     }
651    //     /// Returns the Type of the inner Vault
652    //     ///
653    //     /// @return the Type of the inner Vault
654    //     ///
655    //     access(all) view fun vaultType(): Type {
656    //         return self._borrowVault().getType()
657    //     }
658    //     /// Returns the low and high rebalance thresholds as a fixed length UFix64 containing [low, high]
659    //     ///
660    //     /// @return a sorted fixed-length array containing the relative lower and upper thresholds conditioning
661    //     ///     rebalance execution
662    //     ///
663    //     access(all) view fun rebalanceThresholds(): [UFix64; 2] {
664    //         return self._rebalanceRange
665    //     }
666    //     /// Returns the value of all accounted deposits/withdraws as they have occurred denominated in unitOfAccount.
667    //     /// The returned value is the value as tracked historically, not necessarily the current value of the inner
668    //     /// Vault's balance.
669    //     ///
670    //     /// @return the historical value of deposits
671    //     ///
672    //     access(all) view fun valueOfDeposits(): UFix64 {
673    //         return self._valueOfDeposits
674    //     }
675    //     /// Returns the token Type serving as the price basis of this AutoBalancer
676    //     ///
677    //     /// @return the price denomination of value of the underlying vault as returned from the inner PriceOracle
678    //     ///
679    //     access(all) view fun unitOfAccount(): Type {
680    //         return self._oracle.unitOfAccount()
681    //     }
682    //     /// Returns the current value of the inner Vault's balance. If a price is not available from the AutoBalancer's
683    //     /// PriceOracle, `nil` is returned
684    //     ///
685    //     /// @return the current value of the inner's Vault's balance denominated in unitOfAccount() if a price is
686    //     ///     available, `nil` otherwise
687    //     ///
688    //     access(all) fun currentValue(): UFix64? {
689    //         if let price = self._oracle.price(ofToken: self.vaultType()) {
690    //             return price * self._borrowVault().balance
691    //         }
692    //         return nil
693    //     }
694    //     /// Returns a ComponentInfo struct containing information about this AutoBalancer and its inner DFA components
695    //     ///
696    //     /// @return a ComponentInfo struct containing information about this component and a list of ComponentInfo for
697    //     ///     each inner component in the stack.
698    //     ///
699    //     access(all) fun getComponentInfo(): ComponentInfo {
700    //         // get the inner components
701    //         let oracle = self._borrowOracle()
702    //         let inner: [ComponentInfo] = [oracle.getComponentInfo()]
703
704    //         // get the info for the optional inner components if they exist
705    //         let maybeSink = self._borrowSink()
706    //         let maybeSource = self._borrowSource()
707    //         if let sink = maybeSink {
708    //             inner.append(sink.getComponentInfo())
709    //         }
710    //         if let source = maybeSource {
711    //             inner.append(source.getComponentInfo())
712    //         }
713
714    //         // create the ComponentInfo for the AutoBalancer and insert it at the beginning of the list
715    //         return ComponentInfo(
716    //             type: self.getType(),
717    //             id: self.id(),
718    //             innerComponents: inner
719    //         )
720    //     }
721    //     /// Convenience method issuing a Sink allowing for deposits to this AutoBalancer. If the AutoBalancer's
722    //     /// Capability on itself is not set or is invalid, `nil` is returned.
723    //     ///
724    //     /// @return a Sink routing deposits to this AutoBalancer
725    //     ///
726    //     access(all) fun createBalancerSink(): {Sink}? {
727    //         if self._selfCap == nil || !self._selfCap!.check() {
728    //             return nil
729    //         }
730    //         return AutoBalancerSink(autoBalancer: self._selfCap!, uniqueID: self.uniqueID)
731    //     }
732    //     /// Convenience method issuing a Source enabling withdrawals from this AutoBalancer. If the AutoBalancer's
733    //     /// Capability on itself is not set or is invalid, `nil` is returned.
734    //     ///
735    //     /// @return a Source routing withdrawals from this AutoBalancer
736    //     ///
737    //     access(Get) fun createBalancerSource(): {Source}? {
738    //         if self._selfCap == nil || !self._selfCap!.check() {
739    //             return nil
740    //         }
741    //         return AutoBalancerSource(autoBalancer: self._selfCap!, uniqueID: self.uniqueID)
742    //     }
743    //     /// A setter enabling an AutoBalancer to set a Sink to which overflow value should be deposited
744    //     ///
745    //     /// @param sink: The optional Sink DeFiActions connector from which funds are sourced when this AutoBalancer
746    //     ///     current value rises above the upper threshold relative to its valueOfDeposits(). If `nil`, overflown
747    //     ///     value will not rebalance
748    //     ///
749    //     access(Set) fun setSink(_ sink: {Sink}?, updateSinkID: Bool) {
750    //         if sink != nil && updateSinkID {
751    //             let toUpdate = &sink! as auth(Extend) &{IdentifiableStruct}
752    //             let toAlign = &self as auth(Identify) &{IdentifiableResource}
753    //             DeFiActions.alignID(toUpdate: toUpdate, with: toAlign)
754    //         }
755    //         self._rebalanceSink = sink
756    //     }
757    //     /// A setter enabling an AutoBalancer to set a Source from which underflow value should be withdrawn
758    //     ///
759    //     /// @param source: The optional Source DeFiActions connector from which funds are sourced when this AutoBalancer
760    //     ///     current value falls below the lower threshold relative to its valueOfDeposits(). If `nil`, underflown
761    //     ///     value will not rebalance
762    //     ///
763    //     access(Set) fun setSource(_ source: {Source}?, updateSourceID: Bool) {
764    //         if source != nil && updateSourceID {
765    //             let toUpdate = &source! as auth(Extend) &{IdentifiableStruct}
766    //             let toAlign = &self as auth(Identify) &{IdentifiableResource}
767    //             DeFiActions.alignID(toUpdate: toUpdate, with: toAlign)
768    //         }
769    //         self._rebalanceSource = source
770    //     }
771    //     /// Enables the setting of a Capability on the AutoBalancer for the distribution of Sinks & Sources targeting
772    //     /// the AutoBalancer instance. Due to the mechanisms of Capabilities, this must be done after the AutoBalancer
773    //     /// has been saved to account storage and an authorized Capability has been issued.
774    //     access(Set) fun setSelfCapability(_ cap: Capability<auth(FungibleToken.Withdraw) &AutoBalancer>) {
775    //         pre {
776    //             self._selfCap == nil || self._selfCap!.check() != true:
777    //             "Internal AutoBalancer Capability has been set and is still valid - cannot be re-assigned"
778    //             cap.check(): "Invalid AutoBalancer Capability provided"
779    //             self.getType() == cap.borrow()!.getType() && self.uuid == cap.borrow()!.uuid:
780    //             "Provided Capability does not target this AutoBalancer of type \(self.getType().identifier) with UUID \(self.uuid) - "
781    //                 .concat("provided Capability for AutoBalancer of type \(cap.borrow()!.getType().identifier) with UUID \(cap.borrow()!.uuid)")
782    //         }
783    //         self._selfCap = cap
784    //     }
785    //     /// Sets the rebalance range of this AutoBalancer
786    //     ///
787    //     /// @param range: a sorted array containing lower and upper thresholds that condition rebalance execution. The
788    //     ///     thresholds must be values such that 0.01 <= range[0] < 1.0 && 1.0 < range[1] < 2.0
789    //     ///
790    //     access(Set) fun setRebalanceRange(_ range: [UFix64; 2]) {
791    //         pre {
792    //             range[0] < range[1] && 0.01 <= range[0] && range[0] < 1.0 && 1.0 < range[1] && range[1] < 2.0:
793    //             "Invalid rebalanceRange [lower, upper]: [\(range[0]), \(range[1])] - thresholds must be set such that 0.01 <= range[0] < 1.0 and 1.0 < range[1] < 2.0 relative to value of deposits"
794    //         }
795    //         self._rebalanceRange = range
796    //     }
797    //     /// Returns a copy of the struct's UniqueIdentifier, used in extending a stack to identify another connector in
798    //     /// a DeFiActions stack. See DeFiActions.align() for more information.
799    //     access(contract) view fun copyID(): UniqueIdentifier? {
800    //         return self.uniqueID
801    //     }
802    //     /// Sets the UniqueIdentifier of this component to the provided UniqueIdentifier, used in extending a stack to
803    //     /// identify another connector in a DeFiActions stack. See DeFiActions.align() for more information.
804    //     access(contract) fun setID(_ id: UniqueIdentifier?) {
805    //         self.uniqueID = id
806    //     }
807    //     /// Allows for external parties to call on the AutoBalancer and execute a rebalance according to it's rebalance
808    //     /// parameters. This method must be called by external party regularly in order for rebalancing to occur.
809    //     ///
810    //     /// @param force: if false, rebalance will occur only when beyond upper or lower thresholds; if true, rebalance
811    //     ///     will execute as long as a price is available via the oracle and the current value is non-zero
812    //     ///
813    //     access(Auto) fun rebalance(force: Bool) {
814    //         let currentPrice = self._oracle.price(ofToken: self._vaultType)
815    //         if currentPrice == nil {
816    //             return // no price available -> do nothing
817    //         }
818    //         let currentValue = self.currentValue()!
819    //         // calculate the difference between the current value and the historical value of deposits
820    //         var valueDiff: UFix64 = currentValue < self._valueOfDeposits ? self._valueOfDeposits - currentValue : currentValue - self._valueOfDeposits
821    //         // if deficit detected, choose lower threshold, otherwise choose upper threshold
822    //         let isDeficit = currentValue < self._valueOfDeposits
823    //         let threshold = isDeficit ? (1.0 - self._rebalanceRange[0]) : (self._rebalanceRange[1] - 1.0)
824
825    //         if currentPrice == 0.0 || valueDiff == 0.0 || ((valueDiff / self._valueOfDeposits) < threshold && !force) {
826    //             // division by zero, no difference, or difference does not exceed rebalance ratio & not forced -> no-op
827    //             return
828    //         }
829
830    //         let vault = self._borrowVault()
831    //         //var amount = valueDiff / currentPrice!
832    //         var amount = DeFiActionsMathUtils.divUFix64WithRounding(valueDiff, currentPrice!)
833    //         var executed = false
834    //         let maybeRebalanceSource = &self._rebalanceSource as auth(FungibleToken.Withdraw) &{Source}?
835    //         let maybeRebalanceSink = &self._rebalanceSink as &{Sink}?
836    //         if isDeficit && maybeRebalanceSource != nil {
837    //             // rebalance back up to baseline sourcing funds from _rebalanceSource
838    //             vault.deposit(from:  <- maybeRebalanceSource!.withdrawAvailable(maxAmount: amount))
839    //             executed = true
840    //         } else if !isDeficit && maybeRebalanceSink != nil {
841    //             // rebalance back down to baseline depositing excess to _rebalanceSink
842    //             if amount > vault.balance {
843    //                 amount = vault.balance // protect underflow
844    //             }
845    //             let surplus <- vault.withdraw(amount: amount)
846    //             maybeRebalanceSink!.depositCapacity(from: &surplus as auth(FungibleToken.Withdraw) &{FungibleToken.Vault})
847    //             executed = true
848    //             if surplus.balance == 0.0 {
849    //                 Burner.burn(<-surplus) // could destroy
850    //             } else {
851    //                 amount = amount - surplus.balance // update the rebalanced amount
852    //                 valueDiff = valueDiff - (surplus.balance * currentPrice!) // update the value difference
853    //                 vault.deposit(from: <-surplus) // deposit any excess not taken by the Sink
854    //             }
855    //         }
856    //         // emit event only if rebalance was executed
857    //         if executed {
858    //             emit Rebalanced(
859    //                 amount: amount,
860    //                 value: valueDiff,
861    //                 unitOfAccount: self.unitOfAccount().identifier,
862    //                 isSurplus: !isDeficit,
863    //                 vaultType: self.vaultType().identifier,
864    //                 vaultUUID: self._borrowVault().uuid,
865    //                 balancerUUID: self.uuid,
866    //                 address: self.owner?.address,
867    //                 uuid: self.uuid,
868    //                 uniqueID: self.id()
869    //             )
870    //         }
871    //     }
872
873    //     /* ViewResolver.Resolver conformance */
874
875    //     /// Passthrough to inner Vault's view Types
876    //     access(all) view fun getViews(): [Type] {
877    //         return self._borrowVault().getViews()
878    //     }
879    //     /// Passthrough to inner Vault's view resolution
880    //     access(all) fun resolveView(_ view: Type): AnyStruct? {
881    //         return self._borrowVault().resolveView(view)
882    //     }
883
884    //     /* FungibleToken.Receiver & .Provider conformance */
885
886    //     /// Only the nested Vault type is supported by this AutoBalancer for deposits & withdrawal for the sake of
887    //     /// single asset accounting
888    //     access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
889    //         return { self.vaultType(): true }
890    //     }
891    //     /// True if the provided Type is the nested Vault Type, false otherwise
892    //     access(all) view fun isSupportedVaultType(type: Type): Bool {
893    //         return self.getSupportedVaultTypes()[type] == true
894    //     }
895    //     /// Passthrough to the inner Vault's isAvailableToWithdraw() method
896    //     access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {
897    //         return self._borrowVault().isAvailableToWithdraw(amount: amount)
898    //     }
899    //     /// Deposits the provided Vault to the nested Vault if it is of the same Type, reverting otherwise. In the
900    //     /// process, the current value of the deposited amount (denominated in unitOfAccount) increments the
901    //     /// AutoBalancer's baseValue. If a price is not available via the internal PriceOracle, an average price is
902    //     /// calculated base on the inner vault balance & valueOfDeposits and valueOfDeposits is incremented by the
903    //     /// value of the deposited vault on the basis of that average
904    //     access(all) fun deposit(from: @{FungibleToken.Vault}) {
905    //         pre {
906    //             from.getType() == self.vaultType():
907    //             "Invalid Vault type \(from.getType().identifier) deposited - this AutoBalancer only accepts \(self.vaultType().identifier)"
908    //         }
909    //         // if no price available use an average price based on historical value of deposits and inner vault balance
910    //         let price = self._oracle.price(ofToken: from.getType()) ?? (self.valueOfDeposits() / self.vaultBalance())
911    //         self._valueOfDeposits = self._valueOfDeposits + (from.balance * price)
912    //         self._borrowVault().deposit(from: <-from)
913    //     }
914    //     /// Returns the requested amount of the nested Vault type, reducing the baseValue by the current value
915    //     /// (denominated in unitOfAccount) of the token amount. The AutoBalancer's valueOfDeposits is decremented
916    //     /// in proportion to the amount withdrawn relative to the inner Vault's balance
917    //     access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @{FungibleToken.Vault} {
918    //         pre {
919    //             amount <= self.vaultBalance(): "Withdraw amount \(amount) exceeds current vault balance \(self.vaultBalance())"
920    //         }
921    //         if amount == 0.0 {
922    //             return <- self._borrowVault().createEmptyVault()
923    //         }
924    //         // adjust historical value of deposits proportionate to the amount withdrawn & return withdrawn vault
925    //         // self._valueOfDeposits = (1.0 - amount / self.vaultBalance()) * self._valueOfDeposits
926    //         let proportion: UFix64 = 1.0 - DeFiActionsMathUtils.divUFix64WithRounding(amount, self.vaultBalance())
927    //         let newValue = self._valueOfDeposits * proportion
928    //         self._valueOfDeposits = newValue
929    //         return <- self._borrowVault().withdraw(amount: amount)
930    //     }
931
932    //     /* Burnable.Burner conformance */
933
934    //     /// Executed in Burner.burn(). Passes along the inner vault to be burned, executing the inner Vault's
935    //     /// burnCallback() logic
936    //     access(contract) fun burnCallback() {
937    //         let vault <- self._vault <- nil
938    //         Burner.burn(<-vault) // executes the inner Vault's burnCallback()
939    //     }
940
941    //     /* Internal */
942
943    //     /// Returns a reference to the inner Vault
944    //     access(self) view fun _borrowVault(): auth(FungibleToken.Withdraw) &{FungibleToken.Vault} {
945    //         return (&self._vault)!
946    //     }
947    //     /// Returns a reference to the inner Vault
948    //     access(self) view fun _borrowOracle(): &{PriceOracle} {
949    //         return &self._oracle
950    //     }
951    //     /// Returns a reference to the inner Vault
952    //     access(self) view fun _borrowSink(): &{Sink}? {
953    //         return &self._rebalanceSink
954    //     }
955    //     /// Returns a reference to the inner Source
956    //     access(self) view fun _borrowSource(): auth(FungibleToken.Withdraw) &{Source}? {
957    //         return &self._rebalanceSource as auth(FungibleToken.Withdraw) &{Source}?
958    //     }
959    // }
960
961    // /* --- PUBLIC METHODS --- */
962
963    // /// Returns an AutoBalancer wrapping the provided Vault.
964    // ///
965    // /// @param oracle: The oracle used to query deposited & withdrawn value and to determine if a rebalance should execute
966    // /// @param vault: The Vault wrapped by the AutoBalancer
967    // /// @param rebalanceRange: The percentage range from the AutoBalancer's base value at which a rebalance is executed
968    // /// @param outSink: An optional DeFiActions Sink to which excess value is directed when rebalancing
969    // /// @param inSource: An optional DeFiActions Source from which value is withdrawn to the inner vault when rebalancing
970    // /// @param uniqueID: An optional DeFiActions UniqueIdentifier used for identifying rebalance events
971    // ///
972    // access(all) fun createAutoBalancer(
973    //     oracle: {PriceOracle},
974    //     vaultType: Type,
975    //     lowerThreshold: UFix64,
976    //     upperThreshold: UFix64,
977    //     rebalanceSink: {Sink}?,
978    //     rebalanceSource: {Source}?,
979    //     uniqueID: UniqueIdentifier?
980    // ): @AutoBalancer {
981    //     let ab <- create AutoBalancer(
982    //         lower: lowerThreshold,
983    //         upper: upperThreshold,
984    //         oracle: oracle,
985    //         vaultType: vaultType,
986    //         outSink: rebalanceSink,
987    //         inSource: rebalanceSource,
988    //         uniqueID: uniqueID
989    //     )
990    //     return <- ab
991    // }
992
993    /// Creates a new UniqueIdentifier used for identifying action stacks
994    ///
995    /// @return a new UniqueIdentifier
996    ///
997    access(all) fun createUniqueIdentifier(): UniqueIdentifier {
998        let id = UniqueIdentifier(self.currentID, self.authTokenCap)
999        self.currentID = self.currentID + 1
1000        return id
1001    }
1002
1003    /// Aligns the UniqueIdentifier of the provided component with the provided component, setting the UniqueIdentifier of
1004    /// the provided component to the UniqueIdentifier of the provided component. Parameters are AnyStruct to allow for
1005    /// alignment of both IdentifiableStruct and IdentifiableResource. However, note that the provided component must
1006    /// be an auth(Extend) &{IdentifiableStruct} or auth(Extend) &{IdentifiableResource} to be aligned.
1007    ///
1008    /// @param toUpdate: The component to update the UniqueIdentifier of. Must be an auth(Extend) &{IdentifiableStruct}
1009    ///     or auth(Extend) &{IdentifiableResource}
1010    /// @param with: The component to align the UniqueIdentifier of the provided component with. Must be an
1011    ///     auth(Identify) &{IdentifiableStruct} or auth(Identify) &{IdentifiableResource}
1012    ///
1013    access(all) fun alignID(toUpdate: AnyStruct, with: AnyStruct) {
1014        let maybeISToUpdate = toUpdate as? auth(Extend) &{IdentifiableStruct}
1015        let maybeIRToUpdate = toUpdate as? auth(Extend) &{IdentifiableResource}
1016        let maybeISWith = with as? auth(Identify) &{IdentifiableStruct}
1017        let maybeIRWith = with as? auth(Identify) &{IdentifiableResource}
1018
1019        if maybeISToUpdate != nil && maybeISWith != nil {
1020            maybeISToUpdate!.setID(maybeISWith!.copyID())
1021        } else if maybeISToUpdate != nil && maybeIRWith != nil {
1022            maybeISToUpdate!.setID(maybeIRWith!.copyID())
1023        } else if maybeIRToUpdate != nil && maybeISWith != nil {
1024            maybeIRToUpdate!.setID(maybeISWith!.copyID())
1025        } else if maybeIRToUpdate != nil && maybeIRWith != nil {
1026            maybeIRToUpdate!.setID(maybeIRWith!.copyID())
1027        }
1028        return
1029    }
1030
1031    /* --- INTERNAL CONDITIONAL EVENT EMITTERS --- */
1032
1033    /// Emits Deposited event if a change in balance is detected
1034    access(self) view fun emitDeposited(
1035        type: String,
1036        beforeBalance: UFix64,
1037        afterBalance: UFix64,
1038        fromUUID: UInt64,
1039        uniqueID: UInt64?,
1040        sinkType: String
1041    ): Bool {
1042        if beforeBalance == afterBalance {
1043            return true
1044        }
1045        emit Deposited(
1046            type: type,
1047            amount: beforeBalance > afterBalance ? beforeBalance - afterBalance : afterBalance - beforeBalance,
1048            fromUUID: fromUUID,
1049            uniqueID: uniqueID,
1050            sinkType: sinkType
1051        )
1052        return true
1053    }
1054
1055    /// Emits Withdrawn event if a change in balance is detected
1056    access(self) view fun emitWithdrawn(
1057        type: String,
1058        amount: UFix64,
1059        withdrawnUUID: UInt64,
1060        uniqueID: UInt64?,
1061        sourceType: String
1062    ): Bool {
1063        if amount == 0.0 {
1064            return true
1065        }
1066        emit Withdrawn(
1067            type: type,
1068            amount: amount,
1069            withdrawnUUID: withdrawnUUID,
1070            uniqueID: uniqueID,
1071            sourceType: sourceType
1072        )
1073        return true
1074    }
1075
1076    /// Emits Aligned event if a change in UniqueIdentifier is detected
1077    access(self) view fun emitUpdatedID(
1078        oldID: UInt64?,
1079        newID: UInt64?,
1080        component: String,
1081        uuid: UInt64?
1082    ): Bool {
1083        if oldID == newID {
1084            return true
1085        }
1086        emit UpdatedID(
1087            oldID: oldID,
1088            newID: newID,
1089            component: component,
1090            uuid: uuid
1091        )
1092        return true
1093    }
1094
1095    init() {
1096        self.currentID = 0
1097        self.AuthTokenStoragePath = /storage/authToken
1098
1099        self.account.storage.save(<-create AuthenticationToken(), to: self.AuthTokenStoragePath)
1100        self.authTokenCap = self.account.capabilities.storage.issue<auth(Identify) &AuthenticationToken>(self.AuthTokenStoragePath)
1101
1102        assert(self.authTokenCap.check(), message: "Failed to issue AuthenticationToken Capability")
1103    }
1104}
1105