Smart Contract

MultiTokenQuoteTypes

A.136a10c590912ef8.MultiTokenQuoteTypes

Valid From

132,566,917

Deployed

6d ago
Feb 21, 2026, 05:26:12 PM UTC

Dependents

8 imports
1/// Contract to hold types and events for multi-token swap quote transactions
2/// This is needed because structs and events cannot be declared at the top level of transaction files
3
4access(all) contract MultiTokenQuoteTypes {
5  
6  access(all) struct SwapQuote {
7    access(all) let fromToken: String
8    access(all) let toToken: String
9    access(all) let protocol: String // "IncrementFi" or "Uniswap"
10    access(all) let inputAmount: UFix64
11    access(all) let outputAmount: UFix64
12    access(all) let rate: UFix64 // outputAmount / inputAmount
13    access(all) let pairExists: Bool
14    access(all) let error: String? // Error message if quote failed
15
16    init(fromToken: String, toToken: String, protocol: String, inputAmount: UFix64, outputAmount: UFix64, pairExists: Bool, error: String?) {
17      self.fromToken = fromToken
18      self.toToken = toToken
19      self.protocol = protocol
20      self.inputAmount = inputAmount
21      self.outputAmount = outputAmount
22      self.rate = inputAmount > 0.0 ? outputAmount / inputAmount : 0.0
23      self.pairExists = pairExists
24      self.error = error
25    }
26  }
27
28  access(all) struct PairComparison {
29    access(all) let fromToken: String
30    access(all) let toToken: String
31    access(all) let incrementFiQuote: SwapQuote
32    access(all) let uniswapQuote: SwapQuote
33    access(all) let bestProtocol: String? // Which protocol offers better rate
34    access(all) let rateDifference: UFix64 // Absolute difference in rates
35    access(all) let rateDifferencePercent: UFix64 // Percentage difference
36
37    init(incrementFiQuote: SwapQuote, uniswapQuote: SwapQuote) {
38      self.fromToken = incrementFiQuote.fromToken
39      self.toToken = incrementFiQuote.toToken
40      self.incrementFiQuote = incrementFiQuote
41      self.uniswapQuote = uniswapQuote
42      
43      // Determine best protocol
44      if incrementFiQuote.pairExists && uniswapQuote.pairExists {
45        if incrementFiQuote.outputAmount > uniswapQuote.outputAmount {
46          self.bestProtocol = "IncrementFi"
47        } else if uniswapQuote.outputAmount > incrementFiQuote.outputAmount {
48          self.bestProtocol = "Uniswap"
49        } else {
50          self.bestProtocol = "Equal"
51        }
52      } else if incrementFiQuote.pairExists {
53        self.bestProtocol = "IncrementFi"
54      } else if uniswapQuote.pairExists {
55        self.bestProtocol = "Uniswap"
56      } else {
57        self.bestProtocol = nil
58      }
59      
60      // Calculate rate difference
61      if incrementFiQuote.pairExists && uniswapQuote.pairExists {
62        self.rateDifference = incrementFiQuote.rate > uniswapQuote.rate 
63          ? incrementFiQuote.rate - uniswapQuote.rate 
64          : uniswapQuote.rate - incrementFiQuote.rate
65        
66        let lowerRate = incrementFiQuote.rate < uniswapQuote.rate ? incrementFiQuote.rate : uniswapQuote.rate
67        self.rateDifferencePercent = lowerRate > 0.0 ? (self.rateDifference / lowerRate) * 100.0 : 0.0
68      } else {
69        self.rateDifference = 0.0
70        self.rateDifferencePercent = 0.0
71      }
72    }
73  }
74
75  access(all) struct MultiTokenQuoteResult {
76    access(all) let comparisons: [PairComparison]
77    access(all) let totalPairsTested: Int
78    access(all) let bothProtocolsAvailable: Int
79    access(all) let onlyIncrementFi: Int
80    access(all) let onlyUniswap: Int
81    access(all) let neitherAvailable: Int
82    access(all) let arbitrageOpportunities: Int // Pairs with >1% rate difference
83
84    init(comparisons: [PairComparison]) {
85      self.comparisons = comparisons
86      self.totalPairsTested = comparisons.length
87      
88      var bothAvailable = 0
89      var onlyIncrement = 0
90      var onlyUni = 0
91      var neither = 0
92      var arbOps = 0
93      
94      for comparison in comparisons {
95        if comparison.incrementFiQuote.pairExists && comparison.uniswapQuote.pairExists {
96          bothAvailable = bothAvailable + 1
97          if comparison.rateDifferencePercent > 1.0 {
98            arbOps = arbOps + 1
99          }
100        } else if comparison.incrementFiQuote.pairExists {
101          onlyIncrement = onlyIncrement + 1
102        } else if comparison.uniswapQuote.pairExists {
103          onlyUni = onlyUni + 1
104        } else {
105          neither = neither + 1
106        }
107      }
108      
109      self.bothProtocolsAvailable = bothAvailable
110      self.onlyIncrementFi = onlyIncrement
111      self.onlyUniswap = onlyUni
112      self.neitherAvailable = neither
113      self.arbitrageOpportunities = arbOps
114    }
115  }
116
117  // Events for logging results
118  access(all) event QuoteResult(
119    fromToken: String,
120    toToken: String,
121    incrementFiOutput: UFix64,
122    uniswapOutput: UFix64,
123    bestProtocol: String?,
124    rateDifferencePercent: UFix64,
125    incrementFiExists: Bool,
126    uniswapExists: Bool
127  )
128
129  access(all) event SummaryResult(
130    totalPairsTested: Int,
131    bothProtocolsAvailable: Int,
132    onlyIncrementFi: Int,
133    onlyUniswap: Int,
134    neitherAvailable: Int,
135    arbitrageOpportunities: Int
136  )
137
138  // Cross-chain route structures
139  access(all) struct CrossChainRoute {
140    access(all) let routeName: String
141    access(all) let startToken: String
142    access(all) let steps: [String] // Description of each step
143    access(all) let amounts: [UFix64] // Amount at each step
144    access(all) let bridgeFees: [UFix64] // Bridge fees for each bridge operation
145    access(all) let swapFees: [UFix64] // Swap fees for each swap
146    access(all) let finalAmount: UFix64
147    access(all) let profit: UFix64
148    access(all) let profitPercentage: UFix64
149    access(all) let isProfitable: Bool
150    access(all) let totalFees: UFix64
151
152    init(
153      routeName: String,
154      startToken: String,
155      steps: [String],
156      amounts: [UFix64],
157      bridgeFees: [UFix64],
158      swapFees: [UFix64]
159    ) {
160      self.routeName = routeName
161      self.startToken = startToken
162      self.steps = steps
163      self.amounts = amounts
164      self.bridgeFees = bridgeFees
165      self.swapFees = swapFees
166      self.finalAmount = amounts[amounts.length - 1]
167      
168      var totalFeesCalc = 0.0
169      for fee in bridgeFees {
170        totalFeesCalc = totalFeesCalc + fee
171      }
172      for fee in swapFees {
173        totalFeesCalc = totalFeesCalc + fee
174      }
175      self.totalFees = totalFeesCalc
176      
177      let initialAmount = amounts[0]
178      if self.finalAmount >= initialAmount {
179        self.profit = self.finalAmount - initialAmount - self.totalFees
180        self.isProfitable = self.profit > 0.0
181      } else {
182        self.profit = 0.0
183        self.isProfitable = false
184      }
185      
186      self.profitPercentage = initialAmount > 0.0 ? (self.profit / initialAmount) * 100.0 : 0.0
187    }
188  }
189
190  // Event for cross-chain route results
191  access(all) event CrossChainRouteResult(
192    routeName: String,
193    startToken: String,
194    steps: [String],
195    amounts: [UFix64],
196    bridgeFees: [UFix64],
197    swapFees: [UFix64],
198    finalAmount: UFix64,
199    profit: UFix64,
200    profitPercentage: UFix64,
201    isProfitable: Bool,
202    totalFees: UFix64
203  )
204
205  // Helper functions to emit events (called from transactions)
206  // These act as "log" functions that emit events for data capture
207  // Similar to how scripts return data, but using events since transactions can't return data
208  
209  /// Emits a QuoteResult event for a pair comparison
210  /// This is like returning one element from an array in a script
211  access(all) fun emitQuoteResult(
212    fromToken: String,
213    toToken: String,
214    incrementFiOutput: UFix64,
215    uniswapOutput: UFix64,
216    bestProtocol: String?,
217    rateDifferencePercent: UFix64,
218    incrementFiExists: Bool,
219    uniswapExists: Bool
220  ) {
221    emit QuoteResult(
222      fromToken: fromToken,
223      toToken: toToken,
224      incrementFiOutput: incrementFiOutput,
225      uniswapOutput: uniswapOutput,
226      bestProtocol: bestProtocol,
227      rateDifferencePercent: rateDifferencePercent,
228      incrementFiExists: incrementFiExists,
229      uniswapExists: uniswapExists
230    )
231  }
232
233  /// Emits a SummaryResult event with overall statistics
234  /// This is like returning the final summary struct in a script
235  access(all) fun emitSummaryResult(
236    totalPairsTested: Int,
237    bothProtocolsAvailable: Int,
238    onlyIncrementFi: Int,
239    onlyUniswap: Int,
240    neitherAvailable: Int,
241    arbitrageOpportunities: Int
242  ) {
243    emit SummaryResult(
244      totalPairsTested: totalPairsTested,
245      bothProtocolsAvailable: bothProtocolsAvailable,
246      onlyIncrementFi: onlyIncrementFi,
247      onlyUniswap: onlyUniswap,
248      neitherAvailable: neitherAvailable,
249      arbitrageOpportunities: arbitrageOpportunities
250    )
251  }
252
253  /// Emits a CrossChainRouteResult event for a cross-chain arbitrage route
254  access(all) fun emitCrossChainRouteResult(
255    routeName: String,
256    startToken: String,
257    steps: [String],
258    amounts: [UFix64],
259    bridgeFees: [UFix64],
260    swapFees: [UFix64],
261    finalAmount: UFix64,
262    profit: UFix64,
263    profitPercentage: UFix64,
264    isProfitable: Bool,
265    totalFees: UFix64
266  ) {
267    emit CrossChainRouteResult(
268      routeName: routeName,
269      startToken: startToken,
270      steps: steps,
271      amounts: amounts,
272      bridgeFees: bridgeFees,
273      swapFees: swapFees,
274      finalAmount: finalAmount,
275      profit: profit,
276      profitPercentage: profitPercentage,
277      isProfitable: isProfitable,
278      totalFees: totalFees
279    )
280  }
281
282  init() {
283    // Contract initialization - no state needed
284  }
285}
286
287