Smart Contract

IncrementLoopingStrategy

A.79f5b5b0f95a160b.IncrementLoopingStrategy

Valid From

128,839,139

Deployed

1w ago
Feb 20, 2026, 08:42:26 AM UTC

Dependents

0 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import FlowToken from 0x1654653399040a61
3import stFlowToken from 0xd6f80565193ad727
4import LiquidStaking from 0xd6f80565193ad727
5import LendingInterfaces from 0x2df970b6cdee5735
6import LendingComptroller from 0xf80cb737bfe7c792
7import LendingConfig from 0x2df970b6cdee5735
8
9/// Increment Fi looping strategy
10access(all) contract IncrementLoopingStrategy {
11    
12    access(all) let STFLOW_POOL_ADDRESS: Address
13    access(all) let FLOW_POOL_ADDRESS: Address
14    access(all) let BORROW_FACTOR: UFix64
15    
16    access(all) event LoopExecuted(loopNumber: UInt8, flowStaked: UFix64, stFlowReceived: UFix64, flowBorrowed: UFix64)
17    access(all) event PositionUnwound(stFlowWithdrawn: UFix64, flowRepaid: UFix64)
18    access(all) event HealthFactorWarning(message: String)
19    
20    access(self) var totalFlowStaked: UFix64
21    access(self) var totalStFlowReceived: UFix64
22    access(self) var totalFlowBorrowed: UFix64
23    access(self) var totalLoops: UInt64
24    
25    access(all) resource Strategy {
26        access(self) let userCertificate: @{LendingInterfaces.IdentityCertificate}
27        access(self) let flowVault: @FlowToken.Vault
28        access(self) let stFlowVault: @stFlowToken.Vault
29        
30        access(self) var loopCount: UInt8
31        access(self) var totalStaked: UFix64
32        access(self) var totalBorrowed: UFix64
33        
34        init() {
35            self.userCertificate <- LendingComptroller.IssueUserCertificate()
36            self.flowVault <- FlowToken.createEmptyVault(vaultType: Type<@FlowToken.Vault>()) as! @FlowToken.Vault
37            self.stFlowVault <- stFlowToken.createEmptyVault(vaultType: Type<@stFlowToken.Vault>()) as! @stFlowToken.Vault
38            
39            self.loopCount = 0
40            self.totalStaked = 0.0
41            self.totalBorrowed = 0.0
42        }
43        
44        /// Execute looping strategy with specified number of loops
45        access(all) fun executeStrategy(from: @FlowToken.Vault, numLoops: UInt8): @stFlowToken.Vault {
46            pre {
47                from.balance > 0.0: "Cannot stake zero FLOW"
48                numLoops >= 1 && numLoops <= 3: "Loops must be 1-3"
49            }
50            
51            let initialAmount = from.balance
52            
53            // Store initial FLOW
54            self.flowVault.deposit(from: <-from)
55            
56            // Execute loops
57            var currentLoop: UInt8 = 0
58            while currentLoop < numLoops {
59                let availableFlow = self.flowVault.balance
60                if availableFlow == 0.0 {
61                    break
62                }
63                
64                // Withdraw FLOW for this loop
65                let flowToStake <- self.flowVault.withdraw(amount: availableFlow) as! @FlowToken.Vault
66                
67                let borrowedAmount = self.executeSingleLoop(
68                    flowVault: <-flowToStake,
69                    loopNumber: currentLoop + 1
70                )
71                
72                currentLoop = currentLoop + 1
73                
74                if borrowedAmount == 0.0 {
75                    break
76                }
77            }
78            
79            // Final stake of any remaining borrowed FLOW
80            let remainingFlow = self.flowVault.balance
81            if remainingFlow > 0.0 {
82                let finalFlow <- self.flowVault.withdraw(amount: remainingFlow) as! @FlowToken.Vault
83                let finalStFlow <- LiquidStaking.stake(flowVault: <-finalFlow)
84                self.stFlowVault.deposit(from: <-finalStFlow)
85            }
86            
87            // Return all accumulated stFLOW
88            let totalStFlow = self.stFlowVault.balance
89            let result <- self.stFlowVault.withdraw(amount: totalStFlow) as! @stFlowToken.Vault
90            
91            return <- result
92        }
93        
94        /// Execute a single loop iteration
95        access(self) fun executeSingleLoop(flowVault: @FlowToken.Vault, loopNumber: UInt8): UFix64 {
96            let flowAmount = flowVault.balance
97            
98            // STEP 1: Stake FLOW -> stFLOW
99            let stFlowVault <- LiquidStaking.stake(flowVault: <-flowVault)
100            let stFlowReceived = stFlowVault.balance
101            
102            self.totalStaked = self.totalStaked + flowAmount
103            
104            // STEP 2: Supply stFLOW to Increment lending pool
105            let stFlowPool = getAccount(IncrementLoopingStrategy.STFLOW_POOL_ADDRESS)
106                .capabilities
107                .borrow<&{LendingInterfaces.PoolPublic}>(LendingConfig.PoolPublicPublicPath)
108                ?? panic("Cannot access stFLOW pool")
109            
110            stFlowPool.supply(
111                supplierAddr: self.userCertificate.owner!.address,
112                inUnderlyingVault: <-stFlowVault
113            )
114            
115            // STEP 3: Check borrowing capacity
116            let comptroller = getAccount(0xf80cb737bfe7c792)
117                .capabilities
118                .borrow<&{LendingInterfaces.ComptrollerPublic}>(LendingConfig.ComptrollerPublicPath)
119                ?? panic("Cannot access comptroller")
120            
121            let liquidity = comptroller.getUserCrossMarketLiquidity(
122                userAddr: self.userCertificate.owner!.address
123            )
124            
125            // liquidity returns [availableBorrow, totalBorrow, totalCollateral] as Strings
126            let availableToBorrowStr = liquidity[0] as! String
127            let availableToBorrow = LendingConfig.ScaledUInt256ToUFix64(UInt256.fromString(availableToBorrowStr)!)
128            
129            if availableToBorrow < 0.01 {
130                emit LoopExecuted(
131                    loopNumber: loopNumber,
132                    flowStaked: flowAmount,
133                    stFlowReceived: stFlowReceived,
134                    flowBorrowed: 0.0
135                )
136                return 0.0
137            }
138            
139            // STEP 4: Borrow FLOW (use borrow factor for safety)
140            let flowToBorrow = availableToBorrow * IncrementLoopingStrategy.BORROW_FACTOR
141            
142            let flowPool = getAccount(IncrementLoopingStrategy.FLOW_POOL_ADDRESS)
143                .capabilities
144                .borrow<&{LendingInterfaces.PoolPublic}>(LendingConfig.PoolPublicPublicPath)
145                ?? panic("Cannot access FLOW pool")
146            
147            let borrowedFlow <- flowPool.borrow(
148                userCertificate: &self.userCertificate as &{LendingInterfaces.IdentityCertificate},
149                borrowAmount: flowToBorrow
150            ) as! @FlowToken.Vault
151            
152            let borrowedAmount = borrowedFlow.balance
153            self.totalBorrowed = self.totalBorrowed + borrowedAmount
154            
155            // Store borrowed FLOW for next loop
156            self.flowVault.deposit(from: <-borrowedFlow)
157            
158            IncrementLoopingStrategy.totalFlowStaked = IncrementLoopingStrategy.totalFlowStaked + flowAmount
159            IncrementLoopingStrategy.totalStFlowReceived = IncrementLoopingStrategy.totalStFlowReceived + stFlowReceived
160            IncrementLoopingStrategy.totalFlowBorrowed = IncrementLoopingStrategy.totalFlowBorrowed + borrowedAmount
161            IncrementLoopingStrategy.totalLoops = IncrementLoopingStrategy.totalLoops + 1
162            
163            emit LoopExecuted(
164                loopNumber: loopNumber,
165                flowStaked: flowAmount,
166                stFlowReceived: stFlowReceived,
167                flowBorrowed: borrowedAmount
168            )
169            
170            return borrowedAmount
171        }
172        
173        /// Get health metrics
174        access(all) fun getHealthMetrics(): {String: UFix64} {
175            let comptroller = getAccount(0xf80cb737bfe7c792)
176                .capabilities
177                .borrow<&{LendingInterfaces.ComptrollerPublic}>(LendingConfig.ComptrollerPublicPath)
178                ?? panic("Cannot access comptroller")
179            
180            let liquidity = comptroller.getUserCrossMarketLiquidity(
181                userAddr: self.userCertificate.owner!.address
182            )
183            
184            let totalCollateralStr = liquidity[2] as! String
185            let totalBorrowStr = liquidity[1] as! String
186            
187            let totalCollateral = LendingConfig.ScaledUInt256ToUFix64(UInt256.fromString(totalCollateralStr)!)
188            let totalBorrow = LendingConfig.ScaledUInt256ToUFix64(UInt256.fromString(totalBorrowStr)!)
189            
190            let healthFactor = totalBorrow > 0.0 ? totalCollateral / totalBorrow : 999.0
191            let leverage = self.totalStaked > 0.0 && self.totalBorrowed > 0.0
192                ? self.totalStaked / (self.totalStaked - self.totalBorrowed)
193                : 1.0
194            
195            return {
196                "totalCollateral": totalCollateral,
197                "totalBorrow": totalBorrow,
198                "healthFactor": healthFactor,
199                "leverage": leverage,
200                "totalStaked": self.totalStaked,
201                "totalBorrowed": self.totalBorrowed
202            }
203        }
204        
205        access(all) fun harvest(): @stFlowToken.Vault {
206            return <- stFlowToken.createEmptyVault(vaultType: Type<@stFlowToken.Vault>()) as! @stFlowToken.Vault
207        }
208        
209        access(all) fun emergencyExit(): @stFlowToken.Vault {
210            emit HealthFactorWarning(message: "Emergency exit initiated - manual intervention required")
211            return <- stFlowToken.createEmptyVault(vaultType: Type<@stFlowToken.Vault>()) as! @stFlowToken.Vault
212        }
213        
214        access(all) fun getBalances(): {String: UFix64} {
215            return {
216                "flow": self.flowVault.balance,
217                "stflow": self.stFlowVault.balance,
218                "totalStaked": self.totalStaked,
219                "totalBorrowed": self.totalBorrowed,
220                "loopCount": UFix64(self.loopCount)
221            }
222        }
223    }
224    
225    access(all) fun createStrategy(): @Strategy {
226        return <- create Strategy()
227    }
228    
229    access(all) fun getMetrics(): {String: UFix64} {
230        return {
231            "totalFlowStaked": self.totalFlowStaked,
232            "totalStFlowReceived": self.totalStFlowReceived,
233            "totalFlowBorrowed": self.totalFlowBorrowed,
234            "totalLoops": UFix64(self.totalLoops),
235            "avgLeverage": self.totalFlowStaked > 0.0 && self.totalFlowBorrowed > 0.0
236                ? self.totalFlowStaked / (self.totalFlowStaked - self.totalFlowBorrowed)
237                : 1.0
238        }
239    }
240    
241    init() {
242        self.STFLOW_POOL_ADDRESS = 0x44fe3d9157770b2d
243        self.FLOW_POOL_ADDRESS = 0x7492e2f9b4acea9a
244        self.BORROW_FACTOR = 0.7
245        
246        self.totalFlowStaked = 0.0
247        self.totalStFlowReceived = 0.0
248        self.totalFlowBorrowed = 0.0
249        self.totalLoops = 0
250    }
251}