eco-solver 0.0.1-security → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of eco-solver might be problematic. Click here for more details.

Files changed (244) hide show
  1. package/.eslintignore +8 -0
  2. package/.eslintrc.js +24 -0
  3. package/.github/workflows/ci.yaml +38 -0
  4. package/.nvmrc +1 -0
  5. package/.prettierignore +3 -0
  6. package/.prettierrc +8 -0
  7. package/Dockerfile +11 -0
  8. package/LICENSE +21 -0
  9. package/README.md +29 -5
  10. package/config/default.ts +135 -0
  11. package/config/development.ts +95 -0
  12. package/config/preproduction.ts +17 -0
  13. package/config/production.ts +17 -0
  14. package/config/staging.ts +17 -0
  15. package/config/test.ts +7 -0
  16. package/index.js +43 -0
  17. package/jest.config.ts +14 -0
  18. package/nest-cli.json +8 -0
  19. package/package.json +117 -6
  20. package/src/api/api.module.ts +27 -0
  21. package/src/api/balance.controller.ts +41 -0
  22. package/src/api/quote.controller.ts +54 -0
  23. package/src/api/tests/balance.controller.spec.ts +113 -0
  24. package/src/api/tests/quote.controller.spec.ts +83 -0
  25. package/src/app.module.ts +74 -0
  26. package/src/balance/balance.module.ts +14 -0
  27. package/src/balance/balance.service.ts +230 -0
  28. package/src/balance/balance.ws.service.ts +104 -0
  29. package/src/balance/types.ts +16 -0
  30. package/src/bullmq/bullmq.helper.ts +41 -0
  31. package/src/bullmq/processors/eth-ws.processor.ts +47 -0
  32. package/src/bullmq/processors/inbox.processor.ts +55 -0
  33. package/src/bullmq/processors/interval.processor.ts +54 -0
  34. package/src/bullmq/processors/processor.module.ts +14 -0
  35. package/src/bullmq/processors/signer.processor.ts +41 -0
  36. package/src/bullmq/processors/solve-intent.processor.ts +73 -0
  37. package/src/bullmq/processors/tests/solve-intent.processor.spec.ts +3 -0
  38. package/src/bullmq/utils/queue.ts +22 -0
  39. package/src/chain-monitor/chain-monitor.module.ts +12 -0
  40. package/src/chain-monitor/chain-sync.service.ts +134 -0
  41. package/src/chain-monitor/tests/chain-sync.service.spec.ts +190 -0
  42. package/src/commander/.eslintrc.js +6 -0
  43. package/src/commander/balance/balance-command.module.ts +12 -0
  44. package/src/commander/balance/balance.command.ts +73 -0
  45. package/src/commander/command-main.ts +15 -0
  46. package/src/commander/commander-app.module.ts +31 -0
  47. package/src/commander/eco-config.command.ts +20 -0
  48. package/src/commander/safe/safe-command.module.ts +11 -0
  49. package/src/commander/safe/safe.command.ts +70 -0
  50. package/src/commander/transfer/client.command.ts +24 -0
  51. package/src/commander/transfer/transfer-command.module.ts +26 -0
  52. package/src/commander/transfer/transfer.command.ts +138 -0
  53. package/src/commander/utils.ts +8 -0
  54. package/src/common/chains/definitions/arbitrum.ts +12 -0
  55. package/src/common/chains/definitions/base.ts +21 -0
  56. package/src/common/chains/definitions/eco.ts +54 -0
  57. package/src/common/chains/definitions/ethereum.ts +22 -0
  58. package/src/common/chains/definitions/helix.ts +53 -0
  59. package/src/common/chains/definitions/mantle.ts +12 -0
  60. package/src/common/chains/definitions/optimism.ts +22 -0
  61. package/src/common/chains/definitions/polygon.ts +12 -0
  62. package/src/common/chains/supported.ts +26 -0
  63. package/src/common/chains/transport.ts +19 -0
  64. package/src/common/errors/eco-error.ts +155 -0
  65. package/src/common/events/constants.ts +3 -0
  66. package/src/common/events/viem.ts +22 -0
  67. package/src/common/logging/eco-log-message.ts +74 -0
  68. package/src/common/redis/constants.ts +55 -0
  69. package/src/common/redis/redis-connection-utils.ts +106 -0
  70. package/src/common/routes/constants.ts +3 -0
  71. package/src/common/utils/objects.ts +34 -0
  72. package/src/common/utils/strings.ts +49 -0
  73. package/src/common/utils/tests/objects.spec.ts +23 -0
  74. package/src/common/utils/tests/strings.spec.ts +22 -0
  75. package/src/common/viem/contracts.ts +25 -0
  76. package/src/common/viem/tests/utils.spec.ts +115 -0
  77. package/src/common/viem/utils.ts +78 -0
  78. package/src/contracts/ERC20.contract.ts +389 -0
  79. package/src/contracts/EntryPoint.V6.contract.ts +1309 -0
  80. package/src/contracts/KernelAccount.abi.ts +87 -0
  81. package/src/contracts/OwnableExecutor.abi.ts +128 -0
  82. package/src/contracts/SimpleAccount.contract.ts +524 -0
  83. package/src/contracts/inbox.ts +8 -0
  84. package/src/contracts/index.ts +9 -0
  85. package/src/contracts/intent-source.ts +55 -0
  86. package/src/contracts/interfaces/index.ts +1 -0
  87. package/src/contracts/interfaces/prover.interface.ts +22 -0
  88. package/src/contracts/prover.ts +9 -0
  89. package/src/contracts/tests/erc20.contract.spec.ts +59 -0
  90. package/src/contracts/utils.ts +31 -0
  91. package/src/decoder/decoder.interface.ts +3 -0
  92. package/src/decoder/tests/utils.spec.ts +36 -0
  93. package/src/decoder/utils.ts +24 -0
  94. package/src/decorators/cacheable.decorator.ts +48 -0
  95. package/src/eco-configs/aws-config.service.ts +75 -0
  96. package/src/eco-configs/eco-config.module.ts +44 -0
  97. package/src/eco-configs/eco-config.service.ts +220 -0
  98. package/src/eco-configs/eco-config.types.ts +278 -0
  99. package/src/eco-configs/interfaces/config-source.interface.ts +3 -0
  100. package/src/eco-configs/tests/aws-config.service.spec.ts +52 -0
  101. package/src/eco-configs/tests/eco-config.service.spec.ts +137 -0
  102. package/src/eco-configs/tests/utils.spec.ts +84 -0
  103. package/src/eco-configs/utils.ts +49 -0
  104. package/src/fee/fee.module.ts +10 -0
  105. package/src/fee/fee.service.ts +467 -0
  106. package/src/fee/tests/fee.service.spec.ts +909 -0
  107. package/src/fee/tests/utils.spec.ts +49 -0
  108. package/src/fee/types.ts +44 -0
  109. package/src/fee/utils.ts +23 -0
  110. package/src/flags/flags.module.ts +10 -0
  111. package/src/flags/flags.service.ts +112 -0
  112. package/src/flags/tests/flags.service.spec.ts +68 -0
  113. package/src/flags/utils.ts +22 -0
  114. package/src/health/constants.ts +1 -0
  115. package/src/health/health.controller.ts +23 -0
  116. package/src/health/health.module.ts +25 -0
  117. package/src/health/health.service.ts +40 -0
  118. package/src/health/indicators/balance.indicator.ts +196 -0
  119. package/src/health/indicators/eco-redis.indicator.ts +23 -0
  120. package/src/health/indicators/git-commit.indicator.ts +67 -0
  121. package/src/health/indicators/mongodb.indicator.ts +11 -0
  122. package/src/health/indicators/permission.indicator.ts +64 -0
  123. package/src/intent/create-intent.service.ts +129 -0
  124. package/src/intent/feasable-intent.service.ts +80 -0
  125. package/src/intent/fulfill-intent.service.ts +318 -0
  126. package/src/intent/intent.controller.ts +199 -0
  127. package/src/intent/intent.module.ts +49 -0
  128. package/src/intent/schemas/intent-call-data.schema.ts +16 -0
  129. package/src/intent/schemas/intent-data.schema.ts +114 -0
  130. package/src/intent/schemas/intent-source.schema.ts +33 -0
  131. package/src/intent/schemas/intent-token-amount.schema.ts +14 -0
  132. package/src/intent/schemas/reward-data.schema.ts +48 -0
  133. package/src/intent/schemas/route-data.schema.ts +52 -0
  134. package/src/intent/schemas/watch-event.schema.ts +32 -0
  135. package/src/intent/tests/create-intent.service.spec.ts +215 -0
  136. package/src/intent/tests/feasable-intent.service.spec.ts +155 -0
  137. package/src/intent/tests/fulfill-intent.service.spec.ts +564 -0
  138. package/src/intent/tests/utils-intent.service.spec.ts +308 -0
  139. package/src/intent/tests/utils.spec.ts +62 -0
  140. package/src/intent/tests/validate-intent.service.spec.ts +297 -0
  141. package/src/intent/tests/validation.service.spec.ts +337 -0
  142. package/src/intent/utils-intent.service.ts +168 -0
  143. package/src/intent/utils.ts +37 -0
  144. package/src/intent/validate-intent.service.ts +176 -0
  145. package/src/intent/validation.sevice.ts +223 -0
  146. package/src/interceptors/big-int.interceptor.ts +30 -0
  147. package/src/intervals/interval.module.ts +18 -0
  148. package/src/intervals/retry-infeasable-intents.service.ts +89 -0
  149. package/src/intervals/tests/retry-infeasable-intents.service.spec.ts +167 -0
  150. package/src/kms/errors.ts +0 -0
  151. package/src/kms/kms.module.ts +12 -0
  152. package/src/kms/kms.service.ts +65 -0
  153. package/src/kms/tests/kms.service.spec.ts +60 -0
  154. package/src/liquidity-manager/jobs/check-balances-cron.job.ts +229 -0
  155. package/src/liquidity-manager/jobs/liquidity-manager.job.ts +52 -0
  156. package/src/liquidity-manager/jobs/rebalance.job.ts +61 -0
  157. package/src/liquidity-manager/liquidity-manager.module.ts +29 -0
  158. package/src/liquidity-manager/processors/base.processor.ts +117 -0
  159. package/src/liquidity-manager/processors/eco-protocol-intents.processor.ts +34 -0
  160. package/src/liquidity-manager/processors/grouped-jobs.processor.ts +103 -0
  161. package/src/liquidity-manager/queues/liquidity-manager.queue.ts +48 -0
  162. package/src/liquidity-manager/schemas/rebalance-token.schema.ts +32 -0
  163. package/src/liquidity-manager/schemas/rebalance.schema.ts +32 -0
  164. package/src/liquidity-manager/services/liquidity-manager.service.ts +188 -0
  165. package/src/liquidity-manager/services/liquidity-provider.service.ts +25 -0
  166. package/src/liquidity-manager/services/liquidity-providers/LiFi/lifi-provider.service.spec.ts +125 -0
  167. package/src/liquidity-manager/services/liquidity-providers/LiFi/lifi-provider.service.ts +117 -0
  168. package/src/liquidity-manager/services/liquidity-providers/LiFi/utils/get-transaction-hashes.ts +16 -0
  169. package/src/liquidity-manager/tests/liquidity-manager.service.spec.ts +142 -0
  170. package/src/liquidity-manager/types/token-state.enum.ts +5 -0
  171. package/src/liquidity-manager/types/types.d.ts +52 -0
  172. package/src/liquidity-manager/utils/address.ts +5 -0
  173. package/src/liquidity-manager/utils/math.ts +9 -0
  174. package/src/liquidity-manager/utils/serialize.spec.ts +24 -0
  175. package/src/liquidity-manager/utils/serialize.ts +47 -0
  176. package/src/liquidity-manager/utils/token.ts +91 -0
  177. package/src/main.ts +63 -0
  178. package/src/nest-redlock/nest-redlock.config.ts +14 -0
  179. package/src/nest-redlock/nest-redlock.interface.ts +5 -0
  180. package/src/nest-redlock/nest-redlock.module.ts +64 -0
  181. package/src/nest-redlock/nest-redlock.service.ts +59 -0
  182. package/src/prover/proof.service.ts +184 -0
  183. package/src/prover/prover.module.ts +10 -0
  184. package/src/prover/tests/proof.service.spec.ts +154 -0
  185. package/src/quote/dto/quote.intent.data.dto.ts +35 -0
  186. package/src/quote/dto/quote.reward.data.dto.ts +67 -0
  187. package/src/quote/dto/quote.route.data.dto.ts +71 -0
  188. package/src/quote/dto/types.ts +18 -0
  189. package/src/quote/errors.ts +215 -0
  190. package/src/quote/quote.module.ts +17 -0
  191. package/src/quote/quote.service.ts +299 -0
  192. package/src/quote/schemas/quote-call.schema.ts +16 -0
  193. package/src/quote/schemas/quote-intent.schema.ts +27 -0
  194. package/src/quote/schemas/quote-reward.schema.ts +24 -0
  195. package/src/quote/schemas/quote-route.schema.ts +30 -0
  196. package/src/quote/schemas/quote-token.schema.ts +14 -0
  197. package/src/quote/tests/quote.service.spec.ts +444 -0
  198. package/src/sign/atomic-signer.service.ts +24 -0
  199. package/src/sign/atomic.nonce.service.ts +114 -0
  200. package/src/sign/kms-account/kmsToAccount.ts +73 -0
  201. package/src/sign/kms-account/signKms.ts +30 -0
  202. package/src/sign/kms-account/signKmsTransaction.ts +37 -0
  203. package/src/sign/kms-account/signKmsTypedData.ts +21 -0
  204. package/src/sign/nonce.service.ts +89 -0
  205. package/src/sign/schemas/nonce.schema.ts +36 -0
  206. package/src/sign/sign.controller.ts +52 -0
  207. package/src/sign/sign.helper.ts +23 -0
  208. package/src/sign/sign.module.ts +27 -0
  209. package/src/sign/signer-kms.service.ts +27 -0
  210. package/src/sign/signer.service.ts +26 -0
  211. package/src/solver/filters/tests/valid-smart-wallet.service.spec.ts +87 -0
  212. package/src/solver/filters/valid-smart-wallet.service.ts +58 -0
  213. package/src/solver/solver.module.ts +10 -0
  214. package/src/transaction/multichain-public-client.service.ts +15 -0
  215. package/src/transaction/smart-wallets/kernel/actions/encodeData.kernel.ts +57 -0
  216. package/src/transaction/smart-wallets/kernel/create-kernel-client-v2.account.ts +183 -0
  217. package/src/transaction/smart-wallets/kernel/create.kernel.account.ts +270 -0
  218. package/src/transaction/smart-wallets/kernel/index.ts +2 -0
  219. package/src/transaction/smart-wallets/kernel/kernel-account-client-v2.service.ts +90 -0
  220. package/src/transaction/smart-wallets/kernel/kernel-account-client.service.ts +107 -0
  221. package/src/transaction/smart-wallets/kernel/kernel-account.client.ts +105 -0
  222. package/src/transaction/smart-wallets/kernel/kernel-account.config.ts +34 -0
  223. package/src/transaction/smart-wallets/simple-account/create.simple.account.ts +19 -0
  224. package/src/transaction/smart-wallets/simple-account/index.ts +2 -0
  225. package/src/transaction/smart-wallets/simple-account/simple-account-client.service.ts +42 -0
  226. package/src/transaction/smart-wallets/simple-account/simple-account.client.ts +83 -0
  227. package/src/transaction/smart-wallets/simple-account/simple-account.config.ts +5 -0
  228. package/src/transaction/smart-wallets/smart-wallet.types.ts +38 -0
  229. package/src/transaction/smart-wallets/utils.ts +14 -0
  230. package/src/transaction/transaction.module.ts +25 -0
  231. package/src/transaction/viem_multichain_client.service.ts +100 -0
  232. package/src/transforms/viem-address.decorator.ts +14 -0
  233. package/src/utils/bigint.ts +44 -0
  234. package/src/utils/types.ts +18 -0
  235. package/src/watch/intent/tests/watch-create-intent.service.spec.ts +257 -0
  236. package/src/watch/intent/tests/watch-fulfillment.service.spec.ts +141 -0
  237. package/src/watch/intent/watch-create-intent.service.ts +106 -0
  238. package/src/watch/intent/watch-event.service.ts +133 -0
  239. package/src/watch/intent/watch-fulfillment.service.ts +115 -0
  240. package/src/watch/watch.module.ts +13 -0
  241. package/test/app.e2e-spec.ts +21 -0
  242. package/test/jest-e2e.json +9 -0
  243. package/tsconfig.build.json +4 -0
  244. package/tsconfig.json +29 -0
@@ -0,0 +1,467 @@
1
+ import { BalanceService, TokenFetchAnalysis } from '@/balance/balance.service'
2
+ import { EcoLogMessage } from '@/common/logging/eco-log-message'
3
+ import { getERC20Selector, isERC20Target } from '@/contracts'
4
+ import { EcoConfigService } from '@/eco-configs/eco-config.service'
5
+ import {
6
+ FeeAlgorithmConfig,
7
+ FeeConfigType,
8
+ WhitelistFeeRecord,
9
+ } from '@/eco-configs/eco-config.types'
10
+ import { CalculateTokensType, NormalizedToken } from '@/fee/types'
11
+ import { normalizeBalance } from '@/fee/utils'
12
+ import { getTransactionTargetData } from '@/intent/utils'
13
+ import { QuoteIntentDataInterface } from '@/quote/dto/quote.intent.data.dto'
14
+ import { QuoteError } from '@/quote/errors'
15
+ import { Mathb } from '@/utils/bigint'
16
+ import { Injectable, Logger, OnModuleInit } from '@nestjs/common'
17
+ import { getAddress, Hex } from 'viem'
18
+ import * as _ from 'lodash'
19
+ import { QuoteRouteDataInterface } from '@/quote/dto/quote.route.data.dto'
20
+
21
+ /**
22
+ * The base decimal number for erc20 tokens.
23
+ */
24
+ export const BASE_DECIMALS: number = 6
25
+
26
+ @Injectable()
27
+ export class FeeService implements OnModuleInit {
28
+ private logger = new Logger(FeeService.name)
29
+ private defaultFee: FeeConfigType
30
+ private whitelist: WhitelistFeeRecord
31
+
32
+ constructor(
33
+ private readonly balanceService: BalanceService,
34
+ private readonly ecoConfigService: EcoConfigService,
35
+ ) {}
36
+
37
+ onModuleInit() {
38
+ this.defaultFee = this.ecoConfigService.getIntentConfigs().defaultFee
39
+ this.whitelist = this.ecoConfigService.getWhitelist()
40
+ }
41
+
42
+ /**
43
+ * Returns the fee for a transaction, if the intent is provided
44
+ * then it returns a special fee for that intent if there is one,
45
+ * otherwise it returns the default fee
46
+ *
47
+ * @param intent the optional intent for the fee
48
+ * @param defaultFeeArg the default fee to use if the intent is not provided,
49
+ * usually from the solvers own config
50
+ * @returns
51
+ */
52
+ getFeeConfig(args?: {
53
+ intent?: QuoteIntentDataInterface
54
+ defaultFeeArg?: FeeConfigType
55
+ }): FeeConfigType {
56
+ const { intent, defaultFeeArg } = args || {}
57
+ let feeConfig = defaultFeeArg || this.defaultFee
58
+ if (intent) {
59
+ const destDefaultFee = this.getAskRouteDestinationSolver(intent.route).fee
60
+ feeConfig = defaultFeeArg || destDefaultFee
61
+ const specialFee = this.whitelist[intent.reward.creator]
62
+ if (specialFee) {
63
+ const chainFee = specialFee[Number(intent.route.source)]
64
+ // return a fee that is a merge of the default fee, the special fee and the chain fee
65
+ // merges left to right with the rightmost object taking precedence. In this
66
+ // case that is the user and chain specific fee
67
+ feeConfig = _.merge({}, feeConfig, specialFee.default, chainFee)
68
+ }
69
+ }
70
+ return feeConfig
71
+ }
72
+
73
+ /**
74
+ * Gets the ask for the quote
75
+ *
76
+ * @param totalFulfill the total amount to fulfill, assumes base6
77
+ * @param route the route of the quote intent
78
+ * @returns a bigint representing the ask
79
+ */
80
+ getAsk(totalFulfill: bigint, intent: QuoteIntentDataInterface) {
81
+ const route = intent.route
82
+ //hardcode the destination to eth mainnet/sepolia if its part of the route
83
+ const solver = this.getAskRouteDestinationSolver(route)
84
+
85
+ let fee = 0n
86
+ const feeConfig = this.getFeeConfig({ intent, defaultFeeArg: solver.fee })
87
+ switch (feeConfig.algorithm) {
88
+ // the default
89
+ // 0.02 cents + $0.015 per 100$
90
+ // 20_000n + (totalFulfill / 100_000_000n) * 15_000n
91
+ case 'linear':
92
+ const { tranche } = feeConfig.constants as FeeAlgorithmConfig<'linear'>
93
+ fee =
94
+ BigInt(feeConfig.constants.baseFee) +
95
+ (totalFulfill / BigInt(tranche.unitSize)) * BigInt(tranche.unitFee)
96
+ break
97
+ default:
98
+ throw QuoteError.InvalidSolverAlgorithm(route.destination, solver.fee.algorithm)
99
+ }
100
+ return fee + totalFulfill
101
+ }
102
+
103
+ /**
104
+ * Checks if the route is feasible for the quote intent:
105
+ * 1) the solver can fulfill the transaction
106
+ * 2) the route is profitable for the solver, ie the rewards cover the ask
107
+ * @param quote the quote
108
+ * @returns the error is undefined, error is defined if its not feasible
109
+ */
110
+ async isRouteFeasible(quote: QuoteIntentDataInterface): Promise<{ error?: Error }> {
111
+ if (quote.route.calls.length != 1) {
112
+ //todo support multiple calls after testing
113
+ return { error: QuoteError.MultiFulfillRoute() }
114
+ }
115
+ const { totalFillNormalized, error } = await this.getTotalFill(quote)
116
+ if (!!error) {
117
+ return { error }
118
+ }
119
+ const { totalRewardsNormalized, error: error1 } = await this.getTotalRewards(quote)
120
+ if (!!error1) {
121
+ return { error: error1 }
122
+ }
123
+ const ask = this.getAsk(totalFillNormalized, quote)
124
+ return {
125
+ error:
126
+ totalRewardsNormalized >= ask
127
+ ? undefined
128
+ : QuoteError.RouteIsInfeasable(ask, totalRewardsNormalized),
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Calculates the total normalized fill for the quote intent
134
+ *
135
+ * @param quote the quote intent
136
+ * @returns
137
+ */
138
+ async getTotalFill(
139
+ quote: QuoteIntentDataInterface,
140
+ ): Promise<{ totalFillNormalized: bigint; error?: Error }> {
141
+ const { calls, error } = await this.getCallsNormalized(quote)
142
+ if (error) {
143
+ return { totalFillNormalized: 0n, error }
144
+ }
145
+ return { totalFillNormalized: calls.reduce((acc, call) => acc + call.balance, 0n) }
146
+ }
147
+
148
+ /**
149
+ * Calculates the total normalized and acceoted rewards for the quote intent
150
+ * @param quote the quote intent
151
+ * @returns
152
+ */
153
+ async getTotalRewards(
154
+ quote: QuoteIntentDataInterface,
155
+ ): Promise<{ totalRewardsNormalized: bigint; error?: Error }> {
156
+ const { rewards, error } = await this.getRewardsNormalized(quote)
157
+ if (error) {
158
+ return { totalRewardsNormalized: 0n, error }
159
+ }
160
+ return { totalRewardsNormalized: rewards.reduce((acc, reward) => acc + reward.balance, 0n) }
161
+ }
162
+
163
+ /**
164
+ * Gets the solver tokens for the source chain and orders them in
165
+ * a normalized delta descending order. delta = (balance - minBalance) * decimals
166
+ * @param route the route
167
+ * @returns
168
+ */
169
+ async calculateTokens(quote: QuoteIntentDataInterface): Promise<{
170
+ calculated?: CalculateTokensType
171
+ error?: Error
172
+ }> {
173
+ const route = quote.route
174
+ const srcChainID = route.source
175
+ const destChainID = route.destination
176
+
177
+ const source = this.ecoConfigService
178
+ .getIntentSources()
179
+ .find((intent) => BigInt(intent.chainID) == srcChainID)!
180
+ const solver = this.ecoConfigService.getSolver(destChainID)!
181
+
182
+ if (!source || !solver) {
183
+ let error: Error | undefined
184
+ if (!source) {
185
+ error = QuoteError.NoIntentSourceForSource(srcChainID)
186
+ } else if (!solver) {
187
+ error = QuoteError.NoSolverForDestination(destChainID)
188
+ }
189
+ if (error) {
190
+ this.logger.error(
191
+ EcoLogMessage.fromDefault({
192
+ message: error.message,
193
+ properties: {
194
+ error,
195
+ source,
196
+ solver,
197
+ },
198
+ }),
199
+ )
200
+ return { error }
201
+ }
202
+ }
203
+
204
+ //Get the tokens the solver accepts on the source chain
205
+ const balance = await this.balanceService.fetchTokenData(Number(srcChainID))
206
+ if (!balance) {
207
+ throw QuoteError.FetchingCallTokensFailed(quote.route.source)
208
+ }
209
+ const deficitDescending = balance
210
+ .filter((tokenAnalysis) => source.tokens.includes(tokenAnalysis.token.address))
211
+ .map((token) => {
212
+ return {
213
+ ...token,
214
+ //calculates, converts and normalizes the delta
215
+ delta: this.calculateDelta(token),
216
+ }
217
+ })
218
+ //Sort tokens with leading deficits than: inrange/surplus reordered in accending order
219
+ .sort((a, b) => -1 * Mathb.compare(a.delta.balance, b.delta.balance))
220
+
221
+ //ge/calculate the rewards for the quote intent
222
+ const { rewards, error: errorRewards } = await this.getRewardsNormalized(quote)
223
+ const { calls, error: errorCalls } = await this.getCallsNormalized(quote)
224
+ if (errorCalls || errorRewards) {
225
+ return { error: errorCalls || errorRewards }
226
+ }
227
+ return {
228
+ calculated: {
229
+ solver,
230
+ rewards,
231
+ calls,
232
+ deficitDescending, //token liquidity with deficit first descending
233
+ },
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Fetches the rewardes for the quote intent, grabs their info from the erc20 contracts and then converts
239
+ * and normalizes their values
240
+ * @param quote the quote intent
241
+ */
242
+ async getRewardsNormalized(
243
+ quote: QuoteIntentDataInterface,
244
+ ): Promise<{ rewards: NormalizedToken[]; error?: Error }> {
245
+ const srcChainID = quote.route.source
246
+ const source = this.ecoConfigService
247
+ .getIntentSources()
248
+ .find((intent) => BigInt(intent.chainID) == srcChainID)
249
+ if (!source) {
250
+ return { rewards: [], error: QuoteError.NoIntentSourceForSource(srcChainID) }
251
+ }
252
+ const acceptedTokens = quote.reward.tokens
253
+ .filter((reward) => source.tokens.includes(reward.token))
254
+ .map((reward) => reward.token)
255
+ const erc20Rewards = await this.balanceService.fetchTokenBalances(
256
+ Number(srcChainID),
257
+ acceptedTokens,
258
+ )
259
+ if (Object.keys(erc20Rewards).length === 0) {
260
+ return { rewards: [], error: QuoteError.FetchingRewardTokensFailed(BigInt(srcChainID)) }
261
+ }
262
+
263
+ return {
264
+ rewards: Object.values(erc20Rewards).map((tb) => {
265
+ const token = quote.reward.tokens.find((reward) => getAddress(reward.token) === tb.address)
266
+ return this.convertNormalize(token!.amount, {
267
+ chainID: srcChainID,
268
+ address: tb.address,
269
+ decimals: tb.decimals,
270
+ })
271
+ }),
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Fetches the call tokens for the quote intent, grabs their info from the erc20 contracts and then converts
277
+ * to a standard reserve value for comparisons
278
+ *
279
+ * Throws if there is not enought liquidity for the call
280
+ *
281
+ * @param quote the quote intent
282
+ * @param solver the solver for the quote intent
283
+ * @returns
284
+ */
285
+ async getCallsNormalized(quote: QuoteIntentDataInterface): Promise<{
286
+ calls: NormalizedToken[]
287
+ error: Error | undefined
288
+ }> {
289
+ const solver = this.ecoConfigService.getSolver(quote.route.destination)
290
+ if (!solver) {
291
+ return { calls: [], error: QuoteError.NoSolverForDestination(quote.route.destination) }
292
+ }
293
+ const callERC20Balances = await this.balanceService.fetchTokenBalances(
294
+ solver.chainID,
295
+ quote.route.calls.map((call) => call.target),
296
+ )
297
+
298
+ if (Object.keys(callERC20Balances).length === 0) {
299
+ return { calls: [], error: QuoteError.FetchingCallTokensFailed(BigInt(solver.chainID)) }
300
+ }
301
+ const erc20Balances = Object.values(callERC20Balances).reduce(
302
+ (acc, tokenBalance) => {
303
+ const config = solver.targets[tokenBalance.address]
304
+ acc[tokenBalance.address] = {
305
+ token: tokenBalance,
306
+ config: {
307
+ ...config,
308
+ chainId: solver.chainID,
309
+ address: tokenBalance.address,
310
+ type: 'erc20',
311
+ },
312
+ chainId: solver.chainID,
313
+ }
314
+ return acc
315
+ },
316
+ {} as Record<Hex, TokenFetchAnalysis>,
317
+ )
318
+
319
+ let error: Error | undefined
320
+
321
+ let calls: NormalizedToken[] = []
322
+ try {
323
+ calls = quote.route.calls.map((call) => {
324
+ const ttd = getTransactionTargetData(solver, call)
325
+ if (!isERC20Target(ttd, getERC20Selector('transfer'))) {
326
+ const err = QuoteError.NonERC20TargetInCalls()
327
+ this.logger.error(
328
+ EcoLogMessage.fromDefault({
329
+ message: err.message,
330
+ properties: {
331
+ error: err,
332
+ call,
333
+ ttd,
334
+ },
335
+ }),
336
+ )
337
+ throw err
338
+ }
339
+ const callTarget = erc20Balances[call.target]
340
+ if (!callTarget) {
341
+ throw QuoteError.FailedToFetchTarget(BigInt(solver.chainID), call.target)
342
+ }
343
+
344
+ const transferAmount = ttd!.decodedFunctionData.args![1] as bigint
345
+ const normMinBalance = this.getNormalizedMinBalance(callTarget)
346
+ if (transferAmount > callTarget.token.balance - normMinBalance) {
347
+ const err = QuoteError.SolverLacksLiquidity(
348
+ solver.chainID,
349
+ call.target,
350
+ transferAmount,
351
+ callTarget.token.balance,
352
+ normMinBalance,
353
+ )
354
+ this.logger.error(
355
+ EcoLogMessage.fromDefault({
356
+ message: QuoteError.SolverLacksLiquidity.name,
357
+ properties: {
358
+ error: err,
359
+ quote,
360
+ callTarget,
361
+ },
362
+ }),
363
+ )
364
+ throw err
365
+ }
366
+ return this.convertNormalize(transferAmount, {
367
+ chainID: BigInt(solver.chainID),
368
+ address: call.target,
369
+ decimals: callTarget.token.decimals,
370
+ })
371
+ })
372
+ } catch (e) {
373
+ error = e
374
+ }
375
+
376
+ return { calls, error }
377
+ }
378
+
379
+ /**
380
+ * Calculates the delta for the token as defined as the balance - minBalance
381
+ * @param token the token to us
382
+ * @returns
383
+ */
384
+ calculateDelta(token: TokenFetchAnalysis) {
385
+ const minBalance = this.getNormalizedMinBalance(token)
386
+ const delta = token.token.balance - minBalance
387
+ return this.convertNormalize(delta, {
388
+ chainID: BigInt(token.chainId),
389
+ address: token.config.address,
390
+ decimals: token.token.decimals,
391
+ })
392
+ }
393
+
394
+ /**
395
+ * Returns the normalized min balance for the token. Assumes that the minBalance is
396
+ * set with a decimal of 0, ie in normal dollar units
397
+ * @param tokenAnalysis the token to use
398
+ * @returns
399
+ */
400
+ getNormalizedMinBalance(tokenAnalysis: TokenFetchAnalysis) {
401
+ return normalizeBalance(
402
+ { balance: BigInt(tokenAnalysis.config.minBalance), decimal: 0 },
403
+ tokenAnalysis.token.decimals,
404
+ ).balance
405
+ }
406
+
407
+ /**
408
+ * Converts and normalizes the token to a standard reserve value for comparisons
409
+ * @param value the value to convert
410
+ * @param token the token to us
411
+ * @returns
412
+ */
413
+ convertNormalize(
414
+ value: bigint,
415
+ token: { chainID: bigint; address: Hex; decimals: number },
416
+ ): NormalizedToken {
417
+ const original = value
418
+ const newDecimals = BASE_DECIMALS
419
+ //todo some conversion, assuming here 1-1
420
+ return {
421
+ ...token,
422
+ balance: normalizeBalance({ balance: original, decimal: token.decimals }, newDecimals)
423
+ .balance,
424
+ decimals: newDecimals,
425
+ }
426
+ }
427
+
428
+ /**
429
+ * Deconverts and denormalizes the token form a standard reserve value for comparisons
430
+ * @param value the value to deconvert
431
+ * @param token the token to deconvert
432
+ * @returns
433
+ */
434
+ deconvertNormalize(value: bigint, token: { chainID: bigint; address: Hex; decimals: number }) {
435
+ const original = value
436
+ //todo some conversion, assuming here 1-1
437
+ return {
438
+ ...token,
439
+ balance: normalizeBalance({ balance: original, decimal: BASE_DECIMALS }, token.decimals)
440
+ .balance,
441
+ }
442
+ }
443
+
444
+ /**
445
+ * Returbs the default route destination solver, unless its a ethereum L1 (mainnet or sepolia).
446
+ * In which case it returns that one instead
447
+ *
448
+ * @param route The route of the quote intent
449
+ * @returns
450
+ */
451
+ getAskRouteDestinationSolver(route: QuoteRouteDataInterface) {
452
+ //hardcode the destination to eth mainnet/sepolia if its part of the route
453
+ const destination =
454
+ route.destination === 1n || route.source === 1n
455
+ ? 1n
456
+ : route.destination === 11155111n || route.source === 11155111n
457
+ ? 11155111n
458
+ : route.destination
459
+
460
+ const solver = this.ecoConfigService.getSolver(destination)
461
+ if (!solver) {
462
+ //we shouldn't get here after validations are run so throw
463
+ throw QuoteError.NoSolverForDestination(destination)
464
+ }
465
+ return solver
466
+ }
467
+ }