Smart Contract
IncrementFiFlashloanConnectors
A.e844c7cf7430a77c.IncrementFiFlashloanConnectors
1import FungibleToken from 0xf233dcee88fe0abe
2import DeFiActions from 0x6d888f175c158410
3import SwapInterfaces from 0xb78ef7afa52ff906
4import SwapConfig from 0xb78ef7afa52ff906
5import SwapFactory from 0xb063c16cac85dbd1
6
7/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
8/// THIS CONTRACT IS IN BETA AND IS NOT FINALIZED - INTERFACES MAY CHANGE AND/OR PENDING CHANGES MAY REQUIRE REDEPLOYMENT
9/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10///
11/// IncrementFiFlashloanConnectors
12///
13/// DeFiActions adapter implementations fitting IncrementFi protocols to the DeFiActions Flasher interface.
14///
15access(all) contract IncrementFiFlashloanConnectors {
16
17 /// Canonical path in which to store the generic FlashLoanExecutor resource
18 access(all) let ExecutorStoragePath: StoragePath
19
20 /* --- CONSTRUCTS --- */
21
22 /// Flasher
23 ///
24 /// A DeFiActions connector that performs flash loans using IncrementFi's SwapPair contract
25 ///
26 access(all) struct Flasher : DeFiActions.Flasher {
27 /// The address of the SwapPair contract to use for flash loans
28 access(all) let pairAddress: Address
29 /// The type of token to borrow
30 access(all) let type: Type
31 /// Capability to the FlashLoanExecutor resource
32 access(self) let executor: Capability<&{SwapInterfaces.FlashLoanExecutor}>
33 /// An optional identifier allowing protocols to identify stacked connector operations by defining a protocol-
34 /// specific Identifier to associated connectors on construction
35 access(contract) var uniqueID: DeFiActions.UniqueIdentifier?
36
37 init(
38 pairAddress: Address,
39 type: Type,
40 executor: Capability<&{SwapInterfaces.FlashLoanExecutor}>,
41 uniqueID: DeFiActions.UniqueIdentifier?
42 ) {
43 pre {
44 executor.check(): "Invalid SwapInterfaces.FlashLoanExecutor capability provided"
45 }
46 let pair = getAccount(pairAddress).capabilities.borrow<&{SwapInterfaces.PairPublic}>(SwapConfig.PairPublicPath)
47 ?? panic("Could not reference SwapPair public capability at address \(pairAddress)")
48 let pairInfo = pair.getPairInfoStruct()
49 assert(pairInfo.token0Key == type.identifier || pairInfo.token1Key == type.identifier,
50 message: "Provided type is not supported by the SwapPair at address \(pairAddress) - "
51 .concat("valid types for this SwapPair are \(pairInfo.token0Key) and \(pairInfo.token1Key)"))
52 self.pairAddress = pairAddress
53 self.type = type
54 self.executor = executor
55 self.uniqueID = uniqueID
56 }
57
58 /// Returns a ComponentInfo struct containing information about this Flasher and its inner DFA components
59 ///
60 /// @return a ComponentInfo struct containing information about this component and a list of ComponentInfo for
61 /// each inner component in the stack.
62 ///
63 access(all) fun getComponentInfo(): DeFiActions.ComponentInfo {
64 return DeFiActions.ComponentInfo(
65 type: self.getType(),
66 id: self.id(),
67 innerComponents: []
68 )
69 }
70 /// Returns a copy of the struct's UniqueIdentifier, used in extending a stack to identify another connector in
71 /// a DeFiActions stack. See DeFiActions.align() for more information.
72 ///
73 /// @return a copy of the struct's UniqueIdentifier
74 ///
75 access(contract) view fun copyID(): DeFiActions.UniqueIdentifier? {
76 return self.uniqueID
77 }
78 /// Sets the UniqueIdentifier of this component to the provided UniqueIdentifier, used in extending a stack to
79 /// identify another connector in a DeFiActions stack. See DeFiActions.align() for more information.
80 ///
81 /// @param id: the UniqueIdentifier to set for this component
82 ///
83 access(contract) fun setID(_ id: DeFiActions.UniqueIdentifier?) {
84 self.uniqueID = id
85 }
86 /// Returns the asset type this Flasher can issue as a flash loan
87 ///
88 /// @return the type of token this Flasher can issue as a flash loan
89 ///
90 access(all) view fun borrowType(): Type {
91 return self.type
92 }
93 /// Returns the estimated fee for a flash loan of the specified amount
94 ///
95 /// @param loanAmount: The amount of tokens to borrow
96 /// @return the estimated fee for a flash loan of the specified amount
97 ///
98 access(all) fun calculateFee(loanAmount: UFix64): UFix64 {
99 return UFix64(SwapFactory.getFlashloanRateBps()) * loanAmount / 10000.0
100 }
101 /// Performs a flash loan of the specified amount. The callback function is passed the fee amount, a Vault
102 /// containing the loan, and the data. The callback function should return a Vault containing the loan + fee.
103 ///
104 /// @param amount: The amount of tokens to borrow
105 /// @param data: Optional data to pass to the callback function
106 /// @param callback: The callback function to use for the flash loan
107 ///
108 access(all) fun flashLoan(
109 amount: UFix64,
110 data: AnyStruct?,
111 callback: fun(UFix64, @{FungibleToken.Vault}, AnyStruct?): @{FungibleToken.Vault} // fee, loan, data
112 ) {
113 // get the SwapPair public capability on which to perform the flash loan
114 let pair = getAccount(self.pairAddress).capabilities.borrow<&{SwapInterfaces.PairPublic}>(
115 SwapConfig.PairPublicPath
116 ) ?? panic("Could not reference SwapPair public capability at address \(self.pairAddress)")
117
118 // cast data to expected params type and add fee and callback to params for the callback function
119 let params = data as! {String: AnyStruct}? ?? {}
120 params["fee"] = self.calculateFee(loanAmount: amount)
121 params["callback"] = callback
122
123 // perform the flash loan
124 pair.flashloan(
125 executor: self._borrowExecutor(),
126 requestedTokenVaultType: self.type,
127 requestedAmount: amount,
128 params: params
129 )
130 }
131 /// Returns a reference to the FlashLoanExecutor capability
132 ///
133 /// @return a reference to the FlashLoanExecutor capability
134 ///
135 access(self) fun _borrowExecutor(): &{SwapInterfaces.FlashLoanExecutor} {
136 return self.executor.borrow() ?? panic("Could not borrow FlashLoanExecutor capability")
137 }
138 }
139
140 /// FlashLoanExecutor
141 ///
142 /// A resource that implements the SwapInterfaces.FlashLoanExecutor interface and is used to execute flash loans.
143 /// This resource is required to in order to execute flash loans on IncrementFi's SwapPair contract, but is made
144 /// generically to be used in conjunction with the Flasher DeFiActions connector defined in this contract. Instead
145 /// of statically declaring the logic within the Executor's executeAndRepay() function, the callback function is
146 /// passed as a parameter to the executeAndRepay() function via the params object indexed by "callback".
147 ///
148 access(all) resource Executor : SwapInterfaces.FlashLoanExecutor {
149 /// Performs a flash loan of the specified amount. The Flasher.flashLoan() callback function should be found in
150 /// the params object passed to this function under the key "callback". The callback function is passed the fee
151 /// amount, a Vault containing the loan, and the data. The callback function should return a Vault containing
152 /// the loan + fee.
153 access(all) fun executeAndRepay(loanedToken: @{FungibleToken.Vault}, params: {String: AnyStruct}): @{FungibleToken.Vault} {
154 // cast params to expected types and execute the callback
155 let fee = params.remove(key: "fee") as? UFix64 ?? panic("No fee provided in params to executeAndRepay")
156 let callback = params.remove(key: "callback") as? fun(UFix64, @{FungibleToken.Vault}, AnyStruct?): @{FungibleToken.Vault}
157 ?? panic("No callback function provided in params to executeAndRepay")
158
159 // execute the callback logic
160 let repaidToken <- callback(fee, <-loanedToken, params)
161
162 // return the repaid token
163 return <- repaidToken
164 }
165 }
166
167 /* --- PUBLIC FUNCTIONS --- */
168
169 /// Creates a new FlashLoanExecutor resource
170 ///
171 /// @return a new FlashLoanExecutor resource
172 ///
173 access(all) fun createExecutor(): @Executor {
174 return <- create Executor()
175 }
176
177 init() {
178 self.ExecutorStoragePath = StoragePath(identifier: "IncrementFiFlashloanConnectorsExecutor_\(self.account.address)")!
179 }
180}
181