@@ -15,12 +15,12 @@ import "FlowVaultsAutoBalancers"
1515// tokens
1616import " YieldToken"
1717import " MOET"
18- // amm integration - TODO: Uncomment when bridge integration is ready
19- // import "UniswapV3SwapConnectors"
20- // vm bridge - TODO: Uncomment when bridge integration is ready
21- // import "FlowEVMBridgeConfig"
22- // import "FlowEVMBridgeUtils"
23- // import "FlowEVMBridge"
18+ // amm integration
19+ import " UniswapV3SwapConnectors"
20+ // vm bridge
21+ import " FlowEVMBridgeConfig"
22+ import " FlowEVMBridgeUtils"
23+ import " FlowEVMBridge"
2424// mocks
2525import " MockOracle"
2626import " MockSwapper"
@@ -168,36 +168,211 @@ access(all) contract FlowVaultsStrategies {
168168 outVault : yieldTokenType ,
169169 uniqueID : uniqueID
170170 )
171- // TODO: Update to use UniswapV3SwapConnectors when bridge integration is ready
172- // let stableToYieldSwapper = UniswapV3SwapConnectors.Swapper(
173- // factoryAddress: FlowVaultsStrategies.univ3FactoryEVMAddress,
174- // routerAddress: FlowVaultsStrategies.univ3RouterEVMAddress,
175- // quoterAddress: FlowVaultsStrategies.univ3QuoterEVMAddress,
176- // tokenPath: [moetTokenEVMAddress, FlowVaultsStrategies.yieldTokenEVMAddress],
177- // feePath: [3000],
178- // inVault: moetTokenType,
179- // outVault: yieldTokenType,
180- // coaCapability: FlowVaultsStrategies._getCOACapability(),
181- // uniqueID: uniqueID
182- // )
183171 // YieldToken -> Stable
184172 let yieldToStableSwapper = MockSwapper .Swapper (
185173 inVault : yieldTokenType ,
186174 outVault : moetTokenType ,
187175 uniqueID : uniqueID
188176 )
189- // TODO: Update to use UniswapV3SwapConnectors when bridge integration is ready
190- // let yieldToStableSwapper = UniswapV3SwapConnectors.Swapper(
191- // factoryAddress: FlowVaultsStrategies.univ3FactoryEVMAddress,
192- // routerAddress: FlowVaultsStrategies.univ3RouterEVMAddress,
193- // quoterAddress: FlowVaultsStrategies.univ3QuoterEVMAddress,
194- // tokenPath: [FlowVaultsStrategies.yieldTokenEVMAddress, moetTokenEVMAddress],
195- // feePath: [3000],
196- // inVault: yieldTokenType,
197- // outVault: moetTokenType,
198- // coaCapability: FlowVaultsStrategies._getCOACapability(),
199- // uniqueID: uniqueID
200- // )
177+
178+ // init SwapSink directing swapped funds to AutoBalancer
179+ //
180+ // Swaps provided Stable to YieldToken & deposits to the AutoBalancer
181+ let abaSwapSink = SwapConnectors .SwapSink (swapper : stableToYieldSwapper , sink : abaSink , uniqueID : uniqueID )
182+ // Swaps YieldToken & provides swapped Stable, sourcing YieldToken from the AutoBalancer
183+ let abaSwapSource = SwapConnectors .SwapSource (swapper : yieldToStableSwapper , source : abaSource , uniqueID : uniqueID )
184+
185+ // open a FlowALP position
186+ let poolCap = FlowVaultsStrategies .account .storage .load <Capability <auth (FlowALP.EParticipant , FlowALP.EPosition ) &FlowALP.Pool >>(
187+ from : FlowALP .PoolCapStoragePath
188+ ) ?? panic (" Missing pool capability" )
189+
190+ let poolRef = poolCap .borrow () ?? panic (" Invalid Pool Cap" )
191+
192+ let pid = poolRef .createPosition (
193+ funds : <- withFunds ,
194+ issuanceSink : abaSwapSink ,
195+ repaymentSource : abaSwapSource ,
196+ pushToDrawDownSink : true
197+ )
198+ let position = FlowALP .Position (id : pid , pool : poolCap )
199+ FlowVaultsStrategies .account .storage .save (poolCap , to : FlowALP .PoolCapStoragePath )
200+
201+ // get Sink & Source connectors relating to the new Position
202+ let positionSink = position .createSinkWithOptions (type : collateralType , pushToDrawDownSink : true )
203+ let positionSource = position .createSourceWithOptions (type : collateralType , pullFromTopUpSource : true ) // TODO: may need to be false
204+
205+ // init YieldToken -> FLOW Swapper
206+ let yieldToFlowSwapper = MockSwapper .Swapper (
207+ inVault : yieldTokenType ,
208+ outVault : collateralType ,
209+ uniqueID : uniqueID
210+ )
211+ // allows for YieldToken to be deposited to the Position
212+ let positionSwapSink = SwapConnectors .SwapSink (swapper : yieldToFlowSwapper , sink : positionSink , uniqueID : uniqueID )
213+
214+ // set the AutoBalancer's rebalance Sink which it will use to deposit overflown value,
215+ // recollateralizing the position
216+ autoBalancer .setSink (positionSwapSink , updateSinkID : true )
217+
218+ return <- create TracerStrategy (
219+ id : DeFiActions .createUniqueIdentifier (),
220+ collateralType : collateralType ,
221+ position : position
222+ )
223+ }
224+ }
225+
226+ /// This strategy uses mUSDC vaults
227+ access (all ) resource mUSDCStrategy : FlowVaults .Strategy , DeFiActions .IdentifiableResource {
228+ /// An optional identifier allowing protocols to identify stacked connector operations by defining a protocol-
229+ /// specific Identifier to associated connectors on construction
230+ access (contract ) var uniqueID : DeFiActions .UniqueIdentifier ?
231+ access (self ) let position : FlowALP .Position
232+ access (self ) var sink : {DeFiActions .Sink }
233+ access (self ) var source : {DeFiActions .Source }
234+
235+ init (id : DeFiActions .UniqueIdentifier , collateralType : Type , position : FlowALP .Position ) {
236+ self .uniqueID = id
237+ self .position = position
238+ self .sink = position .createSink (type : collateralType )
239+ self .source = position .createSourceWithOptions (type : collateralType , pullFromTopUpSource : true )
240+ }
241+
242+ // Inherited from FlowVaults.Strategy default implementation
243+ // access(all) view fun isSupportedCollateralType(_ type: Type): Bool
244+
245+ access (all ) view fun getSupportedCollateralTypes (): {Type : Bool } {
246+ return { self .sink .getSinkType (): true }
247+ }
248+ /// Returns the amount available for withdrawal via the inner Source
249+ access (all ) fun availableBalance (ofToken : Type ): UFix64 {
250+ return ofToken == self .source .getSourceType () ? self .source .minimumAvailable () : 0.0
251+ }
252+ /// Deposits up to the inner Sink's capacity from the provided authorized Vault reference
253+ access (all ) fun deposit (from : auth (FungibleToken.Withdraw ) &{FungibleToken .Vault }) {
254+ self .sink .depositCapacity (from : from )
255+ }
256+ /// Withdraws up to the max amount, returning the withdrawn Vault. If the requested token type is unsupported,
257+ /// an empty Vault is returned.
258+ access (FungibleToken.Withdraw ) fun withdraw (maxAmount : UFix64 , ofToken : Type ): @{FungibleToken .Vault } {
259+ if ofToken ! = self .source .getSourceType () {
260+ return <- DeFiActionsUtils .getEmptyVault (ofToken )
261+ }
262+ return <- self .source .withdrawAvailable (maxAmount : maxAmount )
263+ }
264+ /// Executed when a Strategy is burned, cleaning up the Strategy's stored AutoBalancer
265+ access (contract ) fun burnCallback () {
266+ FlowVaultsAutoBalancers ._cleanupAutoBalancer (id : self .id ()! )
267+ }
268+ access (all ) fun getComponentInfo (): DeFiActions .ComponentInfo {
269+ return DeFiActions .ComponentInfo (
270+ type : self .getType (),
271+ id : self .id (),
272+ innerComponents : []
273+ )
274+ }
275+ access (contract ) view fun copyID (): DeFiActions .UniqueIdentifier ? {
276+ return self .uniqueID
277+ }
278+ access (contract ) fun setID (_ id : DeFiActions .UniqueIdentifier ? ) {
279+ self .uniqueID = id
280+ }
281+ }
282+
283+ /// This StrategyComposer builds a mUSDCStrategy
284+ access (all ) resource mUSDCStrategyComposer : FlowVaults .StrategyComposer {
285+ /// Returns the Types of Strategies composed by this StrategyComposer
286+ access (all ) view fun getComposedStrategyTypes (): {Type : Bool } {
287+ return { Type <@mUSDCStrategy >(): true }
288+ }
289+
290+ /// Returns the Vault types which can be used to initialize a given Strategy
291+ access (all ) view fun getSupportedInitializationVaults (forStrategy : Type ): {Type : Bool } {
292+ return { Type <@FlowToken.Vault >(): true }
293+ }
294+
295+ /// Returns the Vault types which can be deposited to a given Strategy instance if it was initialized with the
296+ /// provided Vault type
297+ access (all ) view fun getSupportedInstanceVaults (forStrategy : Type , initializedWith : Type ): {Type : Bool } {
298+ return { Type <@FlowToken.Vault >(): true }
299+ }
300+
301+ /// Composes a Strategy of the given type with the provided funds
302+ access (all ) fun createStrategy (
303+ _ type : Type ,
304+ uniqueID : DeFiActions .UniqueIdentifier ,
305+ withFunds : @{FungibleToken .Vault }
306+ ): @{FlowVaults .Strategy } {
307+ // this PriceOracle is mocked and will be shared by all components used in the TracerStrategy
308+ // TODO: add ERC4626 price oracle
309+ let oracle = MockOracle .PriceOracle ()
310+
311+ // assign EVM token addresses & types
312+
313+ let moetTokenType : Type = Type <@MOET.Vault >()
314+ let moetTokenEVMAddress = FlowEVMBridgeConfig .getEVMAddressAssociated (with : moetTokenType )
315+ ?? panic (" MOET not registered in bridge" )
316+
317+ let yieldTokenType = FlowEVMBridgeConfig .getTypeAssociated (with : FlowVaultsStrategies .yieldTokenEVMAddress )
318+ ?? panic (" YieldToken associated with EVM address \( FlowVaultsStrategies .yieldTokenEVMAddress .toString ()) not found in VM Bridge config" )
319+ // assign collateral & flow token types
320+ let collateralType = withFunds .getType ()
321+
322+ // configure and AutoBalancer for this stack
323+ let autoBalancer = FlowVaultsAutoBalancers ._initNewAutoBalancer (
324+ oracle : oracle , // used to determine value of deposits & when to rebalance
325+ vaultType : yieldTokenType , // the type of Vault held by the AutoBalancer
326+ lowerThreshold : 0.95 , // set AutoBalancer to pull from rebalanceSource when balance is 5% below value of deposits
327+ upperThreshold : 1.05 , // set AutoBalancer to push to rebalanceSink when balance is 5% below value of deposits
328+ rebalanceSink : nil , // nil on init - will be set once a PositionSink is available
329+ rebalanceSource : nil , // nil on init - not set for TracerStrategy
330+ uniqueID : uniqueID // identifies AutoBalancer as part of this Strategy
331+ )
332+ // enables deposits of YieldToken to the AutoBalancer
333+ let abaSink = autoBalancer .createBalancerSink () ?? panic (" Could not retrieve Sink from AutoBalancer with id \( uniqueID .id ) " )
334+ // enables withdrawals of YieldToken from the AutoBalancer
335+ let abaSource = autoBalancer .createBalancerSource () ?? panic (" Could not retrieve Sink from AutoBalancer with id \( uniqueID .id ) " )
336+
337+ // init Stable <> YIELD swappers
338+ //
339+ // Stable -> YieldToken
340+ // TODO: Update to use UniswapV3SwapConnectors
341+ // let stableToYieldSwapper = MockSwapper.Swapper(
342+ // inVault: moetTokenType,
343+ // outVault: yieldTokenType,
344+ // uniqueID: uniqueID
345+ // )
346+ // TODO: consider how we're going to pass the user's COA capability to the Swapper
347+ let stableToYieldSwapper = UniswapV3SwapConnectors .Swapper (
348+ factoryAddress : FlowVaultsStrategies .univ3FactoryEVMAddress ,
349+ routerAddress : FlowVaultsStrategies .univ3RouterEVMAddress ,
350+ quoterAddress : FlowVaultsStrategies .univ3QuoterEVMAddress ,
351+ tokenPath : [moetTokenEVMAddress , FlowVaultsStrategies .yieldTokenEVMAddress ],
352+ feePath : [3000 ],
353+ inVault : moetTokenType ,
354+ outVault : yieldTokenType ,
355+ coaCapability : FlowVaultsStrategies ._getCOACapability (),
356+ uniqueID : uniqueID
357+ )
358+ // YieldToken -> Stable
359+ // TODO: Update to use UniswapV3SwapConnectors
360+ // let yieldToStableSwapper = MockSwapper.Swapper(
361+ // inVault: yieldTokenType,
362+ // outVault: moetTokenType,
363+ // uniqueID: uniqueID
364+ // )
365+ let yieldToStableSwapper = UniswapV3SwapConnectors .Swapper (
366+ factoryAddress : FlowVaultsStrategies .univ3FactoryEVMAddress ,
367+ routerAddress : FlowVaultsStrategies .univ3RouterEVMAddress ,
368+ quoterAddress : FlowVaultsStrategies .univ3QuoterEVMAddress ,
369+ tokenPath : [FlowVaultsStrategies .yieldTokenEVMAddress , moetTokenEVMAddress ],
370+ feePath : [3000 ],
371+ inVault : yieldTokenType ,
372+ outVault : moetTokenType ,
373+ coaCapability : FlowVaultsStrategies ._getCOACapability (),
374+ uniqueID : uniqueID
375+ )
201376
202377 // init SwapSink directing swapped funds to AutoBalancer
203378 //
@@ -273,24 +448,22 @@ access(all) contract FlowVaultsStrategies {
273448 return coaCap
274449 }
275450
276- init () {
277- // TODO: Initialize these when UniswapV3 integration is ready
278- // For now using MockSwapper so these are not needed
279- self .univ3FactoryEVMAddress = EVM .EVMAddress (bytes : [0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ])
280- self .univ3RouterEVMAddress = EVM .EVMAddress (bytes : [0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ])
281- self .univ3QuoterEVMAddress = EVM .EVMAddress (bytes : [0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ])
282- self .yieldTokenEVMAddress = EVM .EVMAddress (bytes : [0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ])
451+ init (factoryAddress : String , routerAddress : String , quoterAddress : String , yieldTokenAddress : String ) {
452+ self .univ3FactoryEVMAddress = EVM .addressFromString (factoryAddress )
453+ self .univ3RouterEVMAddress = EVM .addressFromString (routerAddress )
454+ self .univ3QuoterEVMAddress = EVM .addressFromString (quoterAddress )
455+ self .yieldTokenEVMAddress = EVM .addressFromString (yieldTokenAddress )
283456
284457 self .IssuerStoragePath = StoragePath (identifier : " FlowVaultsStrategyComposerIssuer_\( self .account .address ) " )!
285458
286459 self .account .storage .save (<- create StrategyComposerIssuer (), to : self .IssuerStoragePath )
287460
288- // TODO: COA creation will be needed when UniswapV3 integration is ready
289- // For now using MockSwapper so COA is not needed
290- // if self.account.storage.type(at: /storage/evm) == nil {
291- // self.account.storage.save(<-EVM.createCadenceOwnedAccount(), to: /storage/evm)
292- // let cap = self.account.capabilities.storage.issue<&EVM.CadenceOwnedAccount>(/storage/evm)
293- // self.account.capabilities.publish(cap, at: /public/evm)
294- // }
461+ // TODO: this is temporary until we have a better way to pass user's COAs to inner connectors
462+ // create a COA in this account
463+ if self .account .storage .type (at : /storage/evm ) == nil {
464+ self .account .storage .save (<- EVM .createCadenceOwnedAccount (), to : /storage/evm )
465+ let cap = self .account .capabilities .storage .issue <&EVM.CadenceOwnedAccount >(/storage/evm )
466+ self .account .capabilities .publish (cap , at : /public/evm )
467+ }
295468 }
296469}
0 commit comments