Smart Contract

IncrementLendingStrategy

A.79f5b5b0f95a160b.IncrementLendingStrategy

Valid From

128,860,293

Deployed

1w ago
Feb 20, 2026, 08:47:50 AM UTC

Dependents

0 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import FlowToken from 0x1654653399040a61
3import stFlowToken from 0xd6f80565193ad727
4import LendingInterfaces from 0x2df970b6cdee5735
5import LendingComptroller from 0xf80cb737bfe7c792
6import LendingConfig from 0x2df970b6cdee5735
7
8/// Increment Fi lending/borrowing strategy
9/// Supply assets to earn interest, borrow against collateral
10access(all) contract IncrementLendingStrategy {
11    
12    // ====================================================================
13    // POOL ADDRESSES
14    // ====================================================================
15    access(all) let FLOW_POOL: Address       // 0x7492e2f9b4acea9a
16    access(all) let STFLOW_POOL: Address     // 0x44fe3d9157770b2d
17    
18    // ====================================================================
19    // EVENTS
20    // ====================================================================
21    access(all) event Supplied(poolAddress: Address, asset: String, amount: UFix64, supplier: Address)
22    access(all) event Borrowed(poolAddress: Address, asset: String, amount: UFix64, borrower: Address)
23    access(all) event Repaid(poolAddress: Address, asset: String, amount: UFix64, borrower: Address)
24    access(all) event Redeemed(poolAddress: Address, asset: String, amount: UFix64, supplier: Address)
25    access(all) event LiquidityChecked(availableBorrow: UFix64, totalBorrow: UFix64, totalCollateral: UFix64)
26    
27    // ====================================================================
28    // STATE
29    // ====================================================================
30    access(self) var totalSupplied: {Address: UFix64}
31    access(self) var totalBorrowed: {Address: UFix64}
32    
33    // ====================================================================
34    // STRUCTS
35    // ====================================================================
36    access(all) struct UserPosition {
37        access(all) let supplied: {Address: UFix64}
38        access(all) let borrowed: {Address: UFix64}
39        access(all) let availableBorrow: UFix64
40        access(all) let totalCollateral: UFix64
41        access(all) let healthFactor: UFix64
42        
43        init(
44            supplied: {Address: UFix64},
45            borrowed: {Address: UFix64},
46            availableBorrow: UFix64,
47            totalCollateral: UFix64,
48            healthFactor: UFix64
49        ) {
50            self.supplied = supplied
51            self.borrowed = borrowed
52            self.availableBorrow = availableBorrow
53            self.totalCollateral = totalCollateral
54            self.healthFactor = healthFactor
55        }
56    }
57    
58    // ====================================================================
59    // STRATEGY RESOURCE
60    // ====================================================================
61    access(all) resource Strategy {
62        access(self) let userCertificate: @{LendingInterfaces.IdentityCertificate}
63        access(self) let flowVault: @FlowToken.Vault
64        access(self) let stFlowVault: @stFlowToken.Vault
65        
66        access(self) var suppliedPools: {Address: UFix64}
67        access(self) var borrowedPools: {Address: UFix64}
68        
69        init() {
70            self.userCertificate <- LendingComptroller.IssueUserCertificate()
71            self.flowVault <- FlowToken.createEmptyVault(vaultType: Type<@FlowToken.Vault>()) as! @FlowToken.Vault
72            self.stFlowVault <- stFlowToken.createEmptyVault(vaultType: Type<@stFlowToken.Vault>()) as! @stFlowToken.Vault
73            
74            self.suppliedPools = {}
75            self.borrowedPools = {}
76        }
77        
78        // ====================================================================
79        // SUPPLY FUNCTIONS
80        // ====================================================================
81        
82        /// Supply FLOW to lending pool
83        access(all) fun supplyFlow(amount: UFix64) {
84            pre {
85                self.flowVault.balance >= amount: "Insufficient FLOW balance"
86                amount > 0.0: "Amount must be positive"
87            }
88            
89            let vault <- self.flowVault.withdraw(amount: amount) as! @FlowToken.Vault
90            
91            let poolPublic = getAccount(IncrementLendingStrategy.FLOW_POOL)
92                .capabilities
93                .borrow<&{LendingInterfaces.PoolPublic}>(LendingConfig.PoolPublicPublicPath)
94                ?? panic("Cannot access FLOW pool")
95            
96            poolPublic.supply(
97                supplierAddr: self.userCertificate.owner!.address,
98                inUnderlyingVault: <-vault
99            )
100            
101            let currentSupplied = self.suppliedPools[IncrementLendingStrategy.FLOW_POOL] ?? 0.0
102            self.suppliedPools[IncrementLendingStrategy.FLOW_POOL] = currentSupplied + amount
103            
104            IncrementLendingStrategy.totalSupplied[IncrementLendingStrategy.FLOW_POOL] = 
105                (IncrementLendingStrategy.totalSupplied[IncrementLendingStrategy.FLOW_POOL] ?? 0.0) + amount
106            
107            emit Supplied(
108                poolAddress: IncrementLendingStrategy.FLOW_POOL,
109                asset: "FLOW",
110                amount: amount,
111                supplier: self.userCertificate.owner!.address
112            )
113        }
114        
115        /// Supply stFLOW to lending pool
116        access(all) fun supplyStFlow(amount: UFix64) {
117            pre {
118                self.stFlowVault.balance >= amount: "Insufficient stFLOW balance"
119                amount > 0.0: "Amount must be positive"
120            }
121            
122            let vault <- self.stFlowVault.withdraw(amount: amount) as! @stFlowToken.Vault
123            
124            let poolPublic = getAccount(IncrementLendingStrategy.STFLOW_POOL)
125                .capabilities
126                .borrow<&{LendingInterfaces.PoolPublic}>(LendingConfig.PoolPublicPublicPath)
127                ?? panic("Cannot access stFLOW pool")
128            
129            poolPublic.supply(
130                supplierAddr: self.userCertificate.owner!.address,
131                inUnderlyingVault: <-vault
132            )
133            
134            let currentSupplied = self.suppliedPools[IncrementLendingStrategy.STFLOW_POOL] ?? 0.0
135            self.suppliedPools[IncrementLendingStrategy.STFLOW_POOL] = currentSupplied + amount
136            
137            IncrementLendingStrategy.totalSupplied[IncrementLendingStrategy.STFLOW_POOL] = 
138                (IncrementLendingStrategy.totalSupplied[IncrementLendingStrategy.STFLOW_POOL] ?? 0.0) + amount
139            
140            emit Supplied(
141                poolAddress: IncrementLendingStrategy.STFLOW_POOL,
142                asset: "stFLOW",
143                amount: amount,
144                supplier: self.userCertificate.owner!.address
145            )
146        }
147        
148        // ====================================================================
149        // BORROW FUNCTIONS
150        // ====================================================================
151        
152        /// Borrow FLOW from lending pool
153        access(all) fun borrowFlow(amount: UFix64): @FlowToken.Vault {
154            pre {
155                amount > 0.0: "Amount must be positive"
156            }
157            
158            let poolPublic = getAccount(IncrementLendingStrategy.FLOW_POOL)
159                .capabilities
160                .borrow<&{LendingInterfaces.PoolPublic}>(LendingConfig.PoolPublicPublicPath)
161                ?? panic("Cannot access FLOW pool")
162            
163            let borrowedVault <- poolPublic.borrow(
164                userCertificate: &self.userCertificate as &{LendingInterfaces.IdentityCertificate},
165                borrowAmount: amount
166            ) as! @FlowToken.Vault
167            
168            let currentBorrowed = self.borrowedPools[IncrementLendingStrategy.FLOW_POOL] ?? 0.0
169            self.borrowedPools[IncrementLendingStrategy.FLOW_POOL] = currentBorrowed + amount
170            
171            IncrementLendingStrategy.totalBorrowed[IncrementLendingStrategy.FLOW_POOL] = 
172                (IncrementLendingStrategy.totalBorrowed[IncrementLendingStrategy.FLOW_POOL] ?? 0.0) + amount
173            
174            emit Borrowed(
175                poolAddress: IncrementLendingStrategy.FLOW_POOL,
176                asset: "FLOW",
177                amount: amount,
178                borrower: self.userCertificate.owner!.address
179            )
180            
181            return <- borrowedVault
182        }
183        
184        /// Borrow stFLOW from lending pool
185        access(all) fun borrowStFlow(amount: UFix64): @stFlowToken.Vault {
186            pre {
187                amount > 0.0: "Amount must be positive"
188            }
189            
190            let poolPublic = getAccount(IncrementLendingStrategy.STFLOW_POOL)
191                .capabilities
192                .borrow<&{LendingInterfaces.PoolPublic}>(LendingConfig.PoolPublicPublicPath)
193                ?? panic("Cannot access stFLOW pool")
194            
195            let borrowedVault <- poolPublic.borrow(
196                userCertificate: &self.userCertificate as &{LendingInterfaces.IdentityCertificate},
197                borrowAmount: amount
198            ) as! @stFlowToken.Vault
199            
200            let currentBorrowed = self.borrowedPools[IncrementLendingStrategy.STFLOW_POOL] ?? 0.0
201            self.borrowedPools[IncrementLendingStrategy.STFLOW_POOL] = currentBorrowed + amount
202            
203            IncrementLendingStrategy.totalBorrowed[IncrementLendingStrategy.STFLOW_POOL] = 
204                (IncrementLendingStrategy.totalBorrowed[IncrementLendingStrategy.STFLOW_POOL] ?? 0.0) + amount
205            
206            emit Borrowed(
207                poolAddress: IncrementLendingStrategy.STFLOW_POOL,
208                asset: "stFLOW",
209                amount: amount,
210                borrower: self.userCertificate.owner!.address
211            )
212            
213            return <- borrowedVault
214        }
215        
216        // ====================================================================
217        // REPAY FUNCTIONS - Using repayBorrowBehalf (correct function)
218        // ====================================================================
219        
220        /// Repay FLOW loan
221        access(all) fun repayFlow(vault: @FlowToken.Vault) {
222            pre {
223                vault.balance > 0.0: "Cannot repay zero"
224            }
225            
226            let amount = vault.balance
227            
228            let poolPublic = getAccount(IncrementLendingStrategy.FLOW_POOL)
229                .capabilities
230                .borrow<&{LendingInterfaces.PoolPublic}>(LendingConfig.PoolPublicPublicPath)
231                ?? panic("Cannot access FLOW pool")
232            
233            // repayBorrow returns optional vault (excess/change)
234            let excessVault <- poolPublic.repayBorrow(
235                borrower: self.userCertificate.owner!.address,
236                repayUnderlyingVault: <-vault
237            )
238            
239            // Handle excess if any
240            if excessVault != nil {
241                self.flowVault.deposit(from: <-excessVault!)
242            } else {
243                destroy excessVault
244            }
245            
246            let currentBorrowed = self.borrowedPools[IncrementLendingStrategy.FLOW_POOL] ?? 0.0
247            self.borrowedPools[IncrementLendingStrategy.FLOW_POOL] = currentBorrowed > amount ? currentBorrowed - amount : 0.0
248            
249            emit Repaid(
250                poolAddress: IncrementLendingStrategy.FLOW_POOL,
251                asset: "FLOW",
252                amount: amount,
253                borrower: self.userCertificate.owner!.address
254            )
255        }
256        
257        /// Repay stFLOW loan
258        access(all) fun repayStFlow(vault: @stFlowToken.Vault) {
259            pre {
260                vault.balance > 0.0: "Cannot repay zero"
261            }
262            
263            let amount = vault.balance
264            
265            let poolPublic = getAccount(IncrementLendingStrategy.STFLOW_POOL)
266                .capabilities
267                .borrow<&{LendingInterfaces.PoolPublic}>(LendingConfig.PoolPublicPublicPath)
268                ?? panic("Cannot access stFLOW pool")
269            
270            // repayBorrow returns optional vault (excess/change)
271            let excessVault <- poolPublic.repayBorrow(
272                borrower: self.userCertificate.owner!.address,
273                repayUnderlyingVault: <-vault
274            )
275            
276            // Handle excess if any
277            if excessVault != nil {
278                self.stFlowVault.deposit(from: <-excessVault!)
279            } else {
280                destroy excessVault
281            }
282            
283            let currentBorrowed = self.borrowedPools[IncrementLendingStrategy.STFLOW_POOL] ?? 0.0
284            self.borrowedPools[IncrementLendingStrategy.STFLOW_POOL] = currentBorrowed > amount ? currentBorrowed - amount : 0.0
285            
286            emit Repaid(
287                poolAddress: IncrementLendingStrategy.STFLOW_POOL,
288                asset: "stFLOW",
289                amount: amount,
290                borrower: self.userCertificate.owner!.address
291            )
292        }
293        
294        // ====================================================================
295        // REDEEM FUNCTIONS - Using correct parameter name
296        // ====================================================================
297        
298        /// Redeem (withdraw) FLOW from supply
299        access(all) fun redeemFlow(amount: UFix64): @FlowToken.Vault {
300            pre {
301                amount > 0.0: "Amount must be positive"
302            }
303            
304            let poolPublic = getAccount(IncrementLendingStrategy.FLOW_POOL)
305                .capabilities
306                .borrow<&{LendingInterfaces.PoolPublic}>(LendingConfig.PoolPublicPublicPath)
307                ?? panic("Cannot access FLOW pool")
308            
309            // Use numLpTokenToRedeem parameter name
310            let redeemedVault <- poolPublic.redeem(
311                userCertificate: &self.userCertificate as &{LendingInterfaces.IdentityCertificate},
312                numLpTokenToRedeem: amount
313            ) as! @FlowToken.Vault
314            
315            let currentSupplied = self.suppliedPools[IncrementLendingStrategy.FLOW_POOL] ?? 0.0
316            self.suppliedPools[IncrementLendingStrategy.FLOW_POOL] = currentSupplied > amount ? currentSupplied - amount : 0.0
317            
318            emit Redeemed(
319                poolAddress: IncrementLendingStrategy.FLOW_POOL,
320                asset: "FLOW",
321                amount: amount,
322                supplier: self.userCertificate.owner!.address
323            )
324            
325            return <- redeemedVault
326        }
327        
328        /// Redeem (withdraw) stFLOW from supply
329        access(all) fun redeemStFlow(amount: UFix64): @stFlowToken.Vault {
330            pre {
331                amount > 0.0: "Amount must be positive"
332            }
333            
334            let poolPublic = getAccount(IncrementLendingStrategy.STFLOW_POOL)
335                .capabilities
336                .borrow<&{LendingInterfaces.PoolPublic}>(LendingConfig.PoolPublicPublicPath)
337                ?? panic("Cannot access stFLOW pool")
338            
339            // Use numLpTokenToRedeem parameter name
340            let redeemedVault <- poolPublic.redeem(
341                userCertificate: &self.userCertificate as &{LendingInterfaces.IdentityCertificate},
342                numLpTokenToRedeem: amount
343            ) as! @stFlowToken.Vault
344            
345            let currentSupplied = self.suppliedPools[IncrementLendingStrategy.STFLOW_POOL] ?? 0.0
346            self.suppliedPools[IncrementLendingStrategy.STFLOW_POOL] = currentSupplied > amount ? currentSupplied - amount : 0.0
347            
348            emit Redeemed(
349                poolAddress: IncrementLendingStrategy.STFLOW_POOL,
350                asset: "stFLOW",
351                amount: amount,
352                supplier: self.userCertificate.owner!.address
353            )
354            
355            return <- redeemedVault
356        }
357        
358        // ====================================================================
359        // LIQUIDITY & HEALTH CHECK
360        // ====================================================================
361        
362        /// Get user's borrowing capacity and position health
363        access(all) fun getLiquidityInfo(): {String: UFix64} {
364            let comptroller = getAccount(0xf80cb737bfe7c792)
365                .capabilities
366                .borrow<&{LendingInterfaces.ComptrollerPublic}>(LendingConfig.ComptrollerPublicPath)
367                ?? panic("Cannot access comptroller")
368            
369            let liquidity = comptroller.getUserCrossMarketLiquidity(
370                userAddr: self.userCertificate.owner!.address
371            )
372            
373            let availableBorrowStr = liquidity[0] as! String
374            let totalBorrowStr = liquidity[1] as! String
375            let totalCollateralStr = liquidity[2] as! String
376            
377            let availableBorrow = LendingConfig.ScaledUInt256ToUFix64(UInt256.fromString(availableBorrowStr)!)
378            let totalBorrow = LendingConfig.ScaledUInt256ToUFix64(UInt256.fromString(totalBorrowStr)!)
379            let totalCollateral = LendingConfig.ScaledUInt256ToUFix64(UInt256.fromString(totalCollateralStr)!)
380            
381            let healthFactor = totalBorrow > 0.0 ? totalCollateral / totalBorrow : 999.0
382            let borrowUtilization = totalCollateral > 0.0 ? (totalBorrow / totalCollateral) * 100.0 : 0.0
383            
384            emit LiquidityChecked(
385                availableBorrow: availableBorrow,
386                totalBorrow: totalBorrow,
387                totalCollateral: totalCollateral
388            )
389            
390            return {
391                "availableBorrow": availableBorrow,
392                "totalBorrow": totalBorrow,
393                "totalCollateral": totalCollateral,
394                "healthFactor": healthFactor,
395                "borrowUtilization": borrowUtilization
396            }
397        }
398        
399        // ====================================================================
400        // VAULT MANAGEMENT
401        // ====================================================================
402        
403        access(all) fun depositFlow(vault: @FlowToken.Vault) {
404            self.flowVault.deposit(from: <-vault)
405        }
406        
407        access(all) fun depositStFlow(vault: @stFlowToken.Vault) {
408            self.stFlowVault.deposit(from: <-vault)
409        }
410        
411        access(all) fun withdrawFlow(amount: UFix64): @FlowToken.Vault {
412            return <- self.flowVault.withdraw(amount: amount) as! @FlowToken.Vault
413        }
414        
415        access(all) fun withdrawStFlow(amount: UFix64): @stFlowToken.Vault {
416            return <- self.stFlowVault.withdraw(amount: amount) as! @stFlowToken.Vault
417        }
418        
419        // ====================================================================
420        // VIEW FUNCTIONS
421        // ====================================================================
422        
423        access(all) fun getBalances(): {String: UFix64} {
424            return {
425                "flow": self.flowVault.balance,
426                "stflow": self.stFlowVault.balance
427            }
428        }
429        
430        access(all) fun getPositions(): UserPosition {
431            let liquidityInfo = self.getLiquidityInfo()
432            
433            return UserPosition(
434                supplied: self.suppliedPools,
435                borrowed: self.borrowedPools,
436                availableBorrow: liquidityInfo["availableBorrow"]!,
437                totalCollateral: liquidityInfo["totalCollateral"]!,
438                healthFactor: liquidityInfo["healthFactor"]!
439            )
440        }
441    }
442    
443    // ====================================================================
444    // CONTRACT FUNCTIONS
445    // ====================================================================
446    
447    access(all) fun createStrategy(): @Strategy {
448        return <- create Strategy()
449    }
450    
451    access(all) fun getMetrics(): {String: AnyStruct} {
452        return {
453            "totalSuppliedByPool": self.totalSupplied,
454            "totalBorrowedByPool": self.totalBorrowed
455        }
456    }
457    
458    // ====================================================================
459    // INITIALIZATION
460    // ====================================================================
461    
462    init() {
463        self.FLOW_POOL = 0x7492e2f9b4acea9a
464        self.STFLOW_POOL = 0x44fe3d9157770b2d
465        
466        self.totalSupplied = {}
467        self.totalBorrowed = {}
468    }
469}