Smart Contract

MorphoERC4626SwapConnectors

A.251032a66e9700ef.MorphoERC4626SwapConnectors

Valid From

142,038,879

Deployed

2w ago
Feb 13, 2026, 02:43:18 AM UTC

Dependents

0 imports
1import Burner from 0xf233dcee88fe0abe
2import FungibleToken from 0xf233dcee88fe0abe
3import EVM from 0xe467b9dd11fa00df
4import FlowEVMBridgeConfig from 0x1e4aa0b87d10b141
5import FlowEVMBridgeUtils from 0x1e4aa0b87d10b141
6import FlowToken from 0x1654653399040a61
7import DeFiActions from 0x6d888f175c158410
8import DeFiActionsUtils from 0x6d888f175c158410
9import MorphoERC4626SinkConnectors from 0x251032a66e9700ef
10import SwapConnectors from 0xe1a479f0cb911df9
11import EVMTokenConnectors from 0x1a771b21fcceadc2
12import ERC4626Utils from 0x04f5ae6bef48c1fc
13import EVMAmountUtils from 0x43c9e8bfec507db4
14
15/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
16/// THIS CONTRACT IS IN BETA AND IS NOT FINALIZED - INTERFACES MAY CHANGE AND/OR PENDING CHANGES MAY REQUIRE REDEPLOYMENT
17/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
18///
19/// MorphoERC4626SwapConnectors
20///
21/// Implements the DeFiActions.Swapper interface to swap asset tokens to 4626 shares, integrating the connector with an
22/// EVM Morpho ERC4626 Vault.
23///
24access(all) contract MorphoERC4626SwapConnectors {
25
26    /// Swapper
27    ///
28    /// An implementation of the DeFiActions.Swapper interface to swap assets to 4626 shares where the input token is
29    /// underlying asset in the 4626 vault. Both the asset & the 4626 shares must be onboarded to the VM bridge in order
30    /// for liquidity to flow between Cadence & EVM. These "swaps" are performed by depositing the input asset into the
31    /// ERC4626 vault and withdrawing the resulting shares from the ERC4626 vault.
32    ///
33    access(all) struct Swapper : DeFiActions.Swapper {
34        /// The asset type serving as the price basis in the ERC4626 vault
35        access(self) let assetType: Type
36        /// The EVM address of the asset ERC20 asset underlying the ERC4626 vault
37        access(self) let assetEVMAddress: EVM.EVMAddress
38        /// The address of the ERC4626 vault
39        access(self) let vaultEVMAddress: EVM.EVMAddress
40        /// The type of the bridged ERC4626 vault
41        access(self) let vaultType: Type
42        /// The token sink to use for the ERC4626 vault
43        access(self) let assetSink: MorphoERC4626SinkConnectors.AssetSink
44        /// The token source to use for the ERC4626 vault
45        access(self) let shareSource: EVMTokenConnectors.Source
46        /// The token sink to bridge ERC4626 shares into the COA/EVM
47        access(self) let shareSink: MorphoERC4626SinkConnectors.ShareSink
48        /// The token source to withdraw underlying assets back from the COA/EVM
49        access(self) let assetSource: EVMTokenConnectors.Source
50        /// The optional UniqueIdentifier of the ERC4626 vault
51        access(contract) var uniqueID: DeFiActions.UniqueIdentifier?
52
53        /// If true, the Swapper is configured "reversed":
54        ///   inType = vaultType (shares), outType = assetType (assets)
55        access(self) let isReversed: Bool
56
57        init(
58            vaultEVMAddress: EVM.EVMAddress,
59            coa: Capability<auth(EVM.Call, EVM.Bridge) &EVM.CadenceOwnedAccount>,
60            feeSource: {DeFiActions.Sink, DeFiActions.Source},
61            uniqueID: DeFiActions.UniqueIdentifier?,
62            isReversed: Bool
63        ) {
64            pre {
65                coa.check():
66                "Provided COA Capability is invalid - need Capability<&EVM.CadenceOwnedAccount>"
67
68                feeSource.getSourceType() == Type<@FlowToken.Vault>():
69                "Invalid feeSource - given Source must provide FlowToken Vault, but provides \(feeSource.getSourceType().identifier)"
70            }
71
72            self.uniqueID = uniqueID
73            self.isReversed = isReversed
74
75            self.vaultEVMAddress = vaultEVMAddress
76            self.vaultType = FlowEVMBridgeConfig.getTypeAssociated(with: self.vaultEVMAddress)
77                ?? panic("Provided ERC4626 Vault \(self.vaultEVMAddress.toString()) is not associated with a Cadence FungibleToken - ensure the type & ERC4626 contracts are associated via the VM bridge")
78            assert(
79                DeFiActionsUtils.definingContractIsFungibleToken(self.vaultType),
80                message: "Derived vault type \(self.vaultType.identifier) not FungibleToken type"
81            )
82
83            self.assetEVMAddress = ERC4626Utils.underlyingAssetEVMAddress(vault: self.vaultEVMAddress)
84                ?? panic("Cannot get an underlying asset EVM address from the vault")
85            self.assetType = FlowEVMBridgeConfig.getTypeAssociated(with: self.assetEVMAddress)
86                ?? panic("Underlying asset for vault \(self.vaultEVMAddress.toString()) (asset \(self.assetEVMAddress.toString())) is not associated with a Cadence FungibleToken - ensure the type & underlying asset contracts are associated via the VM bridge")
87            assert(
88                DeFiActionsUtils.definingContractIsFungibleToken(self.assetType),
89                message: "Derived asset type \(self.assetType.identifier) not FungibleToken type"
90            )
91
92            self.assetSink = MorphoERC4626SinkConnectors.AssetSink(
93                vaultEVMAddress: self.vaultEVMAddress,
94                coa: coa,
95                feeSource: feeSource,
96                uniqueID: self.uniqueID
97            )
98            self.shareSource = EVMTokenConnectors.Source(
99                min: nil,
100                withdrawVaultType: self.vaultType,
101                coa: coa,
102                feeSource: feeSource,
103                uniqueID: self.uniqueID
104            )
105
106            self.shareSink = MorphoERC4626SinkConnectors.ShareSink(
107                vaultEVMAddress: self.vaultEVMAddress,
108                coa: coa,
109                feeSource: feeSource,
110                uniqueID: self.uniqueID
111            )
112
113            self.assetSource = EVMTokenConnectors.Source(
114                min: nil,
115                withdrawVaultType: self.assetType,
116                coa: coa,
117                feeSource: feeSource,
118                uniqueID: self.uniqueID
119            )
120        }
121
122        // -------------------------
123        // Direction-aware in/out
124        // -------------------------
125
126        access(all) view fun inType(): Type {
127            return self.isReversed ? self.vaultType : self.assetType
128        }
129
130        access(all) view fun outType(): Type {
131            return self.isReversed ? self.assetType : self.vaultType
132        }
133
134        access(self) fun quoteRequiredAssetsForShares(desiredShares: UFix64): {DeFiActions.Quote} {
135            let desiredSharesEVM = FlowEVMBridgeUtils.convertCadenceAmountToERC20Amount(
136                desiredShares,
137                erc20Address: self.vaultEVMAddress
138            )
139
140            if let requiredAssetsEVM = ERC4626Utils.previewMint(vault: self.vaultEVMAddress, shares: desiredSharesEVM) {
141                let maxAssetsEVM = FlowEVMBridgeUtils.convertCadenceAmountToERC20Amount(
142                    UFix64.max,
143                    erc20Address: self.assetEVMAddress
144                )
145                let requiredAssetsEVMSafe = requiredAssetsEVM < maxAssetsEVM ? requiredAssetsEVM : maxAssetsEVM
146                let assetDecimals = FlowEVMBridgeUtils.getTokenDecimals(evmContractAddress: self.assetEVMAddress)
147                let requiredAssets = EVMAmountUtils.toCadenceIn(
148                    requiredAssetsEVMSafe,
149                    decimals: assetDecimals
150                )
151
152                return SwapConnectors.BasicQuote(
153                    inType: self.assetType,
154                    outType: self.vaultType,
155                    inAmount: requiredAssets,
156                    outAmount: desiredShares
157                )
158            }
159
160            return SwapConnectors.BasicQuote(
161                inType: self.assetType,
162                outType: self.vaultType,
163                inAmount: 0.0,
164                outAmount: 0.0
165            )
166        }
167
168        access(self) fun quoteRequiredSharesForAssets(desiredAssets: UFix64): {DeFiActions.Quote} {
169            let desiredAssetsEVM = FlowEVMBridgeUtils.convertCadenceAmountToERC20Amount(
170                desiredAssets,
171                erc20Address: self.assetEVMAddress
172            )
173
174            if let requiredSharesEVM = ERC4626Utils.previewWithdraw(vault: self.vaultEVMAddress, assets: desiredAssetsEVM) {
175                let maxSharesEVM = FlowEVMBridgeUtils.convertCadenceAmountToERC20Amount(
176                    UFix64.max,
177                    erc20Address: self.vaultEVMAddress
178                )
179                let requiredSharesEVMSafe = requiredSharesEVM < maxSharesEVM ? requiredSharesEVM : maxSharesEVM
180                let shareDecimals = FlowEVMBridgeUtils.getTokenDecimals(evmContractAddress: self.vaultEVMAddress)
181                let requiredShares = EVMAmountUtils.toCadenceIn(
182                    requiredSharesEVMSafe,
183                    decimals: shareDecimals
184                )
185
186                return SwapConnectors.BasicQuote(
187                    inType: self.vaultType,
188                    outType: self.assetType,
189                    inAmount: requiredShares,
190                    outAmount: desiredAssets
191                )
192            }
193
194            return SwapConnectors.BasicQuote(
195                inType: self.vaultType,
196                outType: self.assetType,
197                inAmount: 0.0,
198                outAmount: 0.0
199            )
200        }
201
202        // --------------------------------------------------------------------
203        // Direction model
204        //
205        // Canonical "forward" direction for this connector is:
206        //     assets (underlying ERC20) -> shares (ERC4626 vault token)
207        //
208        // The effective swap / quote direction is determined by TWO flags:
209        //
210        // 1. self.isReversed
211        //    - false: connector is configured in canonical forward mode
212        //    - true:  connector is configured reversed (shares -> assets)
213        //
214        // 2. reverse (method parameter)
215        //    - false: quote/swap in the connector's configured direction
216        //    - true:  quote/swap in the opposite direction
217        //
218        // The resulting direction is:
219        //
220        //     assetsToShares = (self.isReversed == reverse)
221        //
222        // Truth table:
223        //
224        //   isReversed | reverse | effective direction
225        //   -----------+---------+--------------------
226        //     false    |  false  | assets  -> shares
227        //     false    |  true   | shares  -> assets
228        //     true     |  false  | shares  -> assets
229        //     true     |  true   | assets  -> shares
230        //
231        // This same rule is used consistently for:
232        //   - quoteIn / quoteOut
233        //   - swap / swapBack (with different fallbacks)
234        // --------------------------------------------------------------------
235
236        /// desired OUT amount -> required IN amount
237        access(all) fun quoteIn(forDesired: UFix64, reverse: Bool): {DeFiActions.Quote} {
238            // canonical forward = assets -> shares
239            // effective assets->shares when isReversed == reverse
240            let assetsToShares = (self.isReversed == reverse)
241
242            return assetsToShares
243                ? self.quoteRequiredAssetsForShares(desiredShares: forDesired)
244                : self.quoteRequiredSharesForAssets(desiredAssets: forDesired)
245        }
246        access(self) fun quoteSharesOutForAssetsIn(providedAssets: UFix64): {DeFiActions.Quote} {
247            let providedAssetsEVM = FlowEVMBridgeUtils.convertCadenceAmountToERC20Amount(
248                providedAssets,
249                erc20Address: self.assetEVMAddress
250            )
251
252            if let sharesOutEVM = ERC4626Utils.previewDeposit(vault: self.vaultEVMAddress, assets: providedAssetsEVM) {
253                let shareDecimals = FlowEVMBridgeUtils.getTokenDecimals(evmContractAddress: self.vaultEVMAddress)
254                let sharesOut = EVMAmountUtils.toCadenceOut(
255                    sharesOutEVM,
256                    decimals: shareDecimals
257                )
258
259                return SwapConnectors.BasicQuote(
260                    inType: self.assetType,
261                    outType: self.vaultType,
262                    inAmount: providedAssets,
263                    outAmount: sharesOut
264                )
265            }
266
267            return SwapConnectors.BasicQuote(
268                inType: self.assetType,
269                outType: self.vaultType,
270                inAmount: 0.0,
271                outAmount: 0.0
272            )
273        }
274
275        access(self) fun quoteAssetsOutForSharesIn(providedShares: UFix64): {DeFiActions.Quote} {
276            let providedSharesEVM = FlowEVMBridgeUtils.convertCadenceAmountToERC20Amount(
277                providedShares,
278                erc20Address: self.vaultEVMAddress
279            )
280
281            if let assetsOutEVM = ERC4626Utils.previewRedeem(vault: self.vaultEVMAddress, shares: providedSharesEVM) {
282                let assetDecimals = FlowEVMBridgeUtils.getTokenDecimals(evmContractAddress: self.assetEVMAddress)
283                let assetsOut = EVMAmountUtils.toCadenceOut(
284                    assetsOutEVM,
285                    decimals: assetDecimals
286                )
287
288                return SwapConnectors.BasicQuote(
289                    inType: self.vaultType,
290                    outType: self.assetType,
291                    inAmount: providedShares,
292                    outAmount: assetsOut
293                )
294            }
295
296            return SwapConnectors.BasicQuote(
297                inType: self.vaultType,
298                outType: self.assetType,
299                inAmount: 0.0,
300                outAmount: 0.0
301            )
302        }
303
304        /// provided IN amount -> estimated OUT amount
305        access(all) fun quoteOut(forProvided: UFix64, reverse: Bool): {DeFiActions.Quote} {
306            // canonical forward = assets -> shares
307            // effective assets->shares when isReversed == reverse
308            let assetsToShares = (self.isReversed == reverse)
309
310            return assetsToShares
311                ? self.quoteSharesOutForAssetsIn(providedAssets: forProvided)
312                : self.quoteAssetsOutForSharesIn(providedShares: forProvided)
313        }
314
315        // -------------------------
316        // Swap internals
317        // -------------------------
318
319        /// Performs a swap taking a Vault of type inVault, outputting a resulting outVault. Implementations may choose
320        /// to swap along a pre-set path or an optimal path of a set of paths or even set of contained Swappers adapted
321        /// to use multiple Flow swap protocols.
322        access(self) fun swapAssetsToShares(
323            quote: {DeFiActions.Quote}?,
324            inVault: @{FungibleToken.Vault}
325        ): @{FungibleToken.Vault} {
326            if inVault.balance == 0.0 {
327                Burner.burn(<-inVault)
328                return <- DeFiActionsUtils.getEmptyVault(self.vaultType)
329            }
330
331            // assign or get the quote for the swap
332            let _quote = quote ?? self.quoteSharesOutForAssetsIn(providedAssets: inVault.balance)
333            let outAmount = _quote.outAmount
334
335            assert(_quote.inType == self.assetType, message: "Swap: Quote inType mismatch (expected asset)")
336            assert(_quote.outType == self.vaultType, message: "Swap: Quote outType mismatch (expected shares)")
337            assert(_quote.inAmount > 0.0, message: "Invalid quote: inAmount must be > 0")
338            assert(outAmount > 0.0, message: "Invalid quote: outAmount must be > 0")
339
340            // --- Slippage protection: don't allow spending more than quoted ---
341            let beforeInBalance = inVault.balance
342            assert(
343                beforeInBalance <= _quote.inAmount,
344                message: "Swap input (\(beforeInBalance)) exceeds quote.inAmount (\(_quote.inAmount)). Provide an updated quote or reduce inVault balance."
345            )
346
347            // Track shares available before/after to determine received shares
348            let beforeAvailable = self.shareSource.minimumAvailable()
349
350            // Deposit the inVault into the asset sink (should consume all of it)
351            self.assetSink.depositCapacity(from: &inVault as auth(FungibleToken.Withdraw) &{FungibleToken.Vault})
352
353            let remainder = inVault.balance
354            let consumedIn = beforeInBalance - remainder
355
356            // We expect full consumption in this connector's semantics.
357            // If this ever becomes "partial fill" in the future, this check + price check below
358            // ensures it still can't be worse than quoted.
359            assert(consumedIn > 0.0, message: "Asset sink did not consume any input.")
360            assert(remainder == 0.0, message: "Asset sink did not consume full input; remainder: \(remainder.toString()).")
361
362            Burner.burn(<-inVault)
363
364            // get the after available shares
365            let afterAvailable = self.shareSource.minimumAvailable()
366            assert(afterAvailable > beforeAvailable, message: "Expected ERC4626 Vault \(self.vaultEVMAddress.toString()) to have more shares after depositing")
367
368            // withdraw the available difference in shares
369            let receivedShares = afterAvailable - beforeAvailable
370
371            // --- Slippage protection: ensure minimum out ---
372            assert(
373                receivedShares >= outAmount,
374                message: "Slippage: received \(receivedShares) < quote.outAmount (\(outAmount))."
375            )
376
377            let sharesVault <- self.shareSource.withdrawAvailable(maxAmount: receivedShares)
378
379            // Extra safety: ensure the vault we’re returning matches the computed delta
380            // (withdrawAvailable could theoretically return less if liquidity changed)
381            assert(
382                sharesVault.balance >= outAmount,
383                message: "Slippage: withdrawn shares \(sharesVault.balance) < outAmount (\(outAmount))."
384            )
385
386            return <- sharesVault
387        }
388
389        access(self) fun swapSharesToAssets(
390            quote: {DeFiActions.Quote}?,
391            inVault: @{FungibleToken.Vault}
392        ): @{FungibleToken.Vault} {
393            if inVault.balance == 0.0 {
394                Burner.burn(<-inVault)
395                return <- DeFiActionsUtils.getEmptyVault(self.assetType)
396            }
397
398            // assign or get a quote from the swap
399            let _quote = quote ?? self.quoteAssetsOutForSharesIn(providedShares: inVault.balance)
400            let outAmount = _quote.outAmount
401
402            // Ensure the quote represents the inverse of this connector’s forward swap:
403            // swapback must take this connector’s outType and return its inType.
404            // These checks prevent executing a quote meant for a different connector
405            // or accidentally performing a forward swap instead of a reversal.
406            assert(_quote.inType == self.vaultType, message: "Swap: Quote inType mismatch (expected shares)")
407            assert(_quote.outType == self.assetType, message: "Swap: Quote outType mismatch (expected asset)")
408            assert(_quote.inAmount > 0.0, message: "Invalid quote: inAmount must be > 0")
409            assert(outAmount > 0.0, message: "Invalid quote: outAmount must be > 0")
410
411            // Track assets available before/after to determine received assets
412            let beforeInBalance = inVault.balance
413            assert(
414                beforeInBalance <= _quote.inAmount,
415                message: "Swap input (\(beforeInBalance)) exceeds quote.inAmount (\(_quote.inAmount)). Provide an updated quote or reduce inVault balance."
416            )
417
418            let beforeAvailable = self.assetSource.minimumAvailable()
419
420            self.shareSink.depositCapacity(from: &inVault as auth(FungibleToken.Withdraw) &{FungibleToken.Vault})
421
422            let remainder = inVault.balance
423            let consumedIn = beforeInBalance - remainder
424
425            assert(consumedIn > 0.0, message: "Share sink did not consume any input.")
426            assert(remainder == 0.0, message: "Share sink did not consume full input; remainder: \(remainder.toString()).")
427
428            Burner.burn(<-inVault)
429
430            let afterAvailable = self.assetSource.minimumAvailable()
431            assert(afterAvailable > beforeAvailable, message: "Expected more assets after depositing")
432
433            let receivedAssets = afterAvailable - beforeAvailable
434
435            // Derive the expected output from previewRedeem of the actual consumed shares rather
436            // than the quote's outAmount. The quote may have been generated via quoteIn (using
437            // previewWithdraw which rounds up shares), then passed through MultiSwapper which
438            // preserves the desired outAmount. Since redeem rounds down assets (vault-favorable),
439            // the actual output can be less than the quoted outAmount. Using previewRedeem of the
440            // consumed shares gives the correct floor for the slippage check.
441            let consumedSharesEVM = FlowEVMBridgeUtils.convertCadenceAmountToERC20Amount(
442                consumedIn,
443                erc20Address: self.vaultEVMAddress
444            )
445            var expectedOut = outAmount
446            if let previewOutEVM = ERC4626Utils.previewRedeem(vault: self.vaultEVMAddress, shares: consumedSharesEVM) {
447                let assetDecimals = FlowEVMBridgeUtils.getTokenDecimals(evmContractAddress: self.assetEVMAddress)
448                let previewOut = EVMAmountUtils.toCadenceOut(
449                    previewOutEVM,
450                    decimals: assetDecimals
451                )
452                if previewOut < expectedOut {
453                    expectedOut = previewOut
454                }
455            }
456
457            assert(receivedAssets >= expectedOut, message: "Slippage: received < quote.outAmount")
458
459            let assetsVault <- self.assetSource.withdrawAvailable(maxAmount: receivedAssets)
460            assert(assetsVault.balance >= expectedOut, message: "Slippage: withdrawn assets < outAmount")
461
462            return <- assetsVault
463        }
464
465        // -------------------------
466        // Direction-aware swap entrypoints
467        // -------------------------
468
469        access(self) fun quoteIndicatesAssetsToShares(_ q: {DeFiActions.Quote}): Bool {
470            return q.inType == self.assetType && q.outType == self.vaultType
471        }
472
473        access(self) fun quoteIndicatesSharesToAssets(_ q: {DeFiActions.Quote}): Bool {
474            return q.inType == self.vaultType && q.outType == self.assetType
475        }
476
477        access(self) fun decideAssetsToShares(
478            quote: {DeFiActions.Quote}?,
479            fallbackAssetsToShares: Bool
480        ): Bool {
481            if quote == nil {
482                return fallbackAssetsToShares
483            }
484            assert(
485                self.quoteIndicatesAssetsToShares(quote!) || self.quoteIndicatesSharesToAssets(quote!),
486                message: "Quote types not recognized for this connector"
487            )
488            return self.quoteIndicatesAssetsToShares(quote!)
489        }
490
491        access(self) fun assertInputVaultType(
492            _ vault: &{FungibleToken.Vault},
493            assetsToShares: Bool,
494            context: String
495        ) {
496            let expectedType = assetsToShares ? self.assetType : self.vaultType
497            assert(
498                vault.getType() == expectedType,
499                message: "\(context): input vault type mismatch. Expected \(expectedType.identifier), got \(vault.getType().identifier)"
500            )
501        }
502
503        access(all) fun swap(
504            quote: {DeFiActions.Quote}?,
505            inVault: @{FungibleToken.Vault}
506        ): @{FungibleToken.Vault} {
507            // Decide direction:
508            // - if quote provided, trust its type pair
509            // - else fall back to configured direction (isReversed)
510            let assetsToShares = self.decideAssetsToShares(quote: quote, fallbackAssetsToShares: !self.isReversed)
511
512            self.assertInputVaultType(
513                &inVault as &{FungibleToken.Vault},
514                assetsToShares: assetsToShares,
515                context: "Swap"
516            )
517
518            if assetsToShares {
519                return <- self.swapAssetsToShares(quote: quote, inVault: <-inVault)
520            }
521            return <- self.swapSharesToAssets(quote: quote, inVault: <-inVault)
522        }
523
524        /// Performs a swap taking a Vault of type outVault, outputting a resulting inVault. Implementations may choose
525        /// to swap along a pre-set path or an optimal path of a set of paths or even set of contained Swappers adapted
526        /// to use multiple Flow swap protocols.
527        access(all) fun swapBack(
528            quote: {DeFiActions.Quote}?,
529            residual: @{FungibleToken.Vault}
530        ): @{FungibleToken.Vault} {
531            // Decide direction:
532            // - if quote provided, trust its type pair
533            // - else fall back to configured direction (isReversed)
534            let assetsToShares = self.decideAssetsToShares(quote: quote, fallbackAssetsToShares: self.isReversed)
535
536            self.assertInputVaultType(
537                &residual as &{FungibleToken.Vault},
538                assetsToShares: assetsToShares,
539                context: "SwapBack"
540            )
541
542            if assetsToShares {
543                return <- self.swapAssetsToShares(quote: quote, inVault: <-residual)
544            }
545            return <- self.swapSharesToAssets(quote: quote, inVault: <-residual)
546        }
547
548        /// Returns a ComponentInfo struct containing information about this component and a list of ComponentInfo for
549        /// each inner component in the stack.
550        access(all) fun getComponentInfo(): DeFiActions.ComponentInfo {
551            return DeFiActions.ComponentInfo(
552                type: self.getType(),
553                id: self.id(),
554                innerComponents: [
555                    self.assetSink.getComponentInfo(),
556                    self.shareSource.getComponentInfo(),
557                    self.shareSink.getComponentInfo(),
558                    self.assetSource.getComponentInfo()
559                ]
560            )
561        }
562        /// Returns a copy of the struct's UniqueIdentifier, used in extending a stack to identify another connector in
563        /// a DeFiActions stack. See DeFiActions.align() for more information.
564        ///
565        /// @return a copy of the struct's UniqueIdentifier
566        ///
567        access(contract) view fun copyID(): DeFiActions.UniqueIdentifier? {
568            return self.uniqueID
569        }
570        /// Sets the UniqueIdentifier of this component to the provided UniqueIdentifier, used in extending a stack to
571        /// identify another connector in a DeFiActions stack. See DeFiActions.align() for more information.
572        ///
573        /// @param id: the UniqueIdentifier to set for this component
574        ///
575        access(contract) fun setID(_ id: DeFiActions.UniqueIdentifier?) {
576            self.uniqueID = id
577        }
578    }
579}
580