@@ -39,6 +39,85 @@ access(all) contract SwapConnectors {
3939 }
4040 }
4141
42+ /// Swap mode metadata for interpreting quote semantics in Swapper.swap().
43+ access (all ) enum SwapMode : UInt8 {
44+ access (all ) case IN
45+ access (all ) case OUT
46+ }
47+
48+ /// A quote that carries swap mode metadata and a receiver capability for exact-output residual input.
49+ access (all ) struct ModeQuote : DeFiActions .Quote {
50+ access (all ) let inType : Type
51+ access (all ) let outType : Type
52+ access (all ) let inAmount : UFix64
53+ access (all ) let outAmount : UFix64
54+ access (all ) let mode : SwapMode
55+ access (all ) let leftoverInReceiver : Capability <&{FungibleToken .Receiver }>?
56+
57+ init (
58+ inType : Type ,
59+ outType : Type ,
60+ inAmount : UFix64 ,
61+ outAmount : UFix64 ,
62+ mode : SwapMode ,
63+ leftoverInReceiver : Capability <&{FungibleToken .Receiver }>?
64+ ) {
65+ self .inType = inType
66+ self .outType = outType
67+ self .inAmount = inAmount
68+ self .outAmount = outAmount
69+ self .mode = mode
70+ self .leftoverInReceiver = leftoverInReceiver
71+ }
72+ }
73+
74+ /// Returns a ModeQuote copy of any DeFiActions.Quote.
75+ access (all ) fun asModeQuote (
76+ quote : {DeFiActions .Quote },
77+ mode : SwapMode ,
78+ leftoverInReceiver : Capability <&{FungibleToken .Receiver }>?
79+ ): ModeQuote {
80+ if let modeQuote = quote as ? ModeQuote {
81+ return ModeQuote (
82+ inType : modeQuote .inType ,
83+ outType : modeQuote .outType ,
84+ inAmount : modeQuote .inAmount ,
85+ outAmount : modeQuote .outAmount ,
86+ mode : mode ,
87+ leftoverInReceiver : leftoverInReceiver ?? modeQuote .leftoverInReceiver
88+ )
89+ }
90+ return ModeQuote (
91+ inType : quote .inType ,
92+ outType : quote .outType ,
93+ inAmount : quote .inAmount ,
94+ outAmount : quote .outAmount ,
95+ mode : mode ,
96+ leftoverInReceiver : leftoverInReceiver
97+ )
98+ }
99+
100+ /// Returns a ModeQuote configured for exact-input behavior.
101+ access (all ) fun asExactInQuote (quote : {DeFiActions .Quote }): ModeQuote {
102+ return self .asModeQuote (
103+ quote : quote ,
104+ mode : SwapMode .IN ,
105+ leftoverInReceiver : nil
106+ )
107+ }
108+
109+ /// Returns a ModeQuote configured for exact-output behavior.
110+ access (all ) fun asExactOutQuote (
111+ quote : {DeFiActions .Quote },
112+ leftoverInReceiver : Capability <&{FungibleToken .Receiver }>
113+ ): ModeQuote {
114+ return self .asModeQuote (
115+ quote : quote ,
116+ mode : SwapMode .OUT ,
117+ leftoverInReceiver : leftoverInReceiver
118+ )
119+ }
120+
42121 /// MultiSwapperQuote
43122 ///
44123 /// A MultiSwapper specific DeFiActions.Quote implementation allowing for callers to set the Swapper used in
@@ -174,13 +253,53 @@ access(all) contract SwapConnectors {
174253 /// requested and the optimal Swapper used to fulfill the swap.
175254 /// NOTE: providing a Quote does not guarantee the fulfilled swap will enforce the quote's defined outAmount
176255 access (all ) fun swap (quote : {DeFiActions .Quote }? , inVault : @{FungibleToken .Vault }): @{FungibleToken .Vault } {
256+ if quote ! = nil {
257+ if let modeQuote = quote as ? ModeQuote {
258+ if modeQuote .mode == SwapMode .OUT {
259+ let residualReceiverCap = modeQuote .leftoverInReceiver
260+ ?? panic (" Exact-out quote requires a valid leftoverInReceiver capability" )
261+ let residualReceiver = residualReceiverCap .borrow ()
262+ ?? panic (" Exact-out quote contains an invalid leftoverInReceiver capability" )
263+
264+ let estimate = self .quoteIn (forDesired : modeQuote .outAmount , reverse : false ) as ! MultiSwapperQuote
265+ let routedQuote = MultiSwapperQuote (
266+ inType : estimate .inType ,
267+ outType : estimate .outType ,
268+ inAmount : modeQuote .inAmount ,
269+ outAmount : modeQuote .outAmount ,
270+ swapperIndex : estimate .swapperIndex
271+ )
272+ let optimalSwapper = &self .swappers [routedQuote .swapperIndex ] as &{DeFiActions .Swapper }
273+ let vaults <- optimalSwapper .swapExactOut (quote : routedQuote , inVault : <- inVault )
274+
275+ let outVault <- vaults .remove (at : 0 )
276+ let leftoverInVault <- vaults .remove (at : 0 )
277+ destroy vaults
278+
279+ if leftoverInVault .balance > 0.0 {
280+ residualReceiver .deposit (from : <- leftoverInVault )
281+ } else {
282+ Burner .burn (<- leftoverInVault )
283+ }
284+
285+ return <- outVault
286+ }
287+ }
288+ }
177289 return <- self ._swap (quote : quote , from : <- inVault , reverse : false )
178290 }
179291 /// Swap for exact output using the optimal inner Swapper
180292 access (all ) fun swapExactOut (quote : {DeFiActions .Quote }, inVault : @{FungibleToken .Vault }): @[{FungibleToken .Vault }] {
181293 var multiQuote = quote as ? MultiSwapperQuote
182294 if multiQuote == nil || multiQuote ! .swapperIndex > = self .swappers .length {
183- multiQuote = self .quoteIn (forDesired : quote .outAmount , reverse : false ) as ! MultiSwapperQuote
295+ let estimate = self .quoteIn (forDesired : quote .outAmount , reverse : false ) as ! MultiSwapperQuote
296+ multiQuote = MultiSwapperQuote (
297+ inType : estimate .inType ,
298+ outType : estimate .outType ,
299+ inAmount : quote .inAmount ,
300+ outAmount : quote .outAmount ,
301+ swapperIndex : estimate .swapperIndex
302+ )
184303 }
185304 let optimalSwapper = &self .swappers [multiQuote ! .swapperIndex ] as &{DeFiActions .Swapper }
186305 return <- optimalSwapper .swapExactOut (quote : multiQuote ! , inVault : <- inVault )
0 commit comments