Smart Contract
IncrementLoopingStrategy
A.79f5b5b0f95a160b.IncrementLoopingStrategy
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}