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