eco-solver 0.0.1-security → 1.5.1

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 +115 -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,71 @@
1
+ import { CallDataInterface } from '@/contracts'
2
+ import { QuoteRewardTokensDTO } from '@/quote/dto/quote.reward.data.dto'
3
+ import { ViemAddressTransform } from '@/transforms/viem-address.decorator'
4
+ import { RouteType } from '@eco-foundation/routes-ts'
5
+ import { ApiProperty } from '@nestjs/swagger'
6
+ import { Transform, Type } from 'class-transformer'
7
+ import { ArrayNotEmpty, IsArray, IsNotEmpty, ValidateNested } from 'class-validator'
8
+ import { Hex } from 'viem'
9
+
10
+ /**
11
+ * The DTO for the route data that the sender wants to make.
12
+ * Similar to {@link RouteType} except that it does not contain the salt field.
13
+ * @param source denotes the source chain id of the route
14
+ * @param destination denotes the destination chain id of the route
15
+ * @param inbox denotes the inbox address
16
+ * @param calls denotes the array of {@link QuoteCallDataDTO} that the sender wants to make
17
+ */
18
+ export class QuoteRouteDataDTO implements QuoteRouteDataInterface {
19
+ @IsNotEmpty()
20
+ @ApiProperty()
21
+ @Transform(({ value }) => BigInt(value))
22
+ source: bigint
23
+
24
+ @IsNotEmpty()
25
+ @Transform(({ value }) => BigInt(value))
26
+ @ApiProperty()
27
+ destination: bigint
28
+
29
+ @ViemAddressTransform()
30
+ @IsNotEmpty()
31
+ @ApiProperty()
32
+ inbox: Hex
33
+
34
+ @IsArray()
35
+ @ArrayNotEmpty()
36
+ @ValidateNested()
37
+ @ApiProperty()
38
+ @Type(() => QuoteRewardTokensDTO)
39
+ tokens: QuoteRewardTokensDTO[]
40
+
41
+ @IsArray()
42
+ @ArrayNotEmpty()
43
+ @ValidateNested()
44
+ @ApiProperty()
45
+ @Type(() => QuoteCallDataDTO)
46
+ calls: QuoteCallDataDTO[]
47
+ }
48
+
49
+ /**
50
+ * The DTO for the call data that the sender wants to make.
51
+ * @param target denotes the target address of the call
52
+ * @param data denotes the data of the call
53
+ * @param value denotes the native token value of the call
54
+ */
55
+ export class QuoteCallDataDTO implements CallDataInterface {
56
+ @ViemAddressTransform()
57
+ @IsNotEmpty()
58
+ @ApiProperty()
59
+ target: Hex
60
+
61
+ @IsNotEmpty()
62
+ @ApiProperty()
63
+ data: Hex
64
+
65
+ @IsNotEmpty()
66
+ @Transform(({ value }) => BigInt(value))
67
+ @ApiProperty()
68
+ value: bigint
69
+ }
70
+
71
+ export interface QuoteRouteDataInterface extends Omit<RouteType, 'salt'> {}
@@ -0,0 +1,18 @@
1
+ import { RewardType, RouteType } from '@eco-foundation/routes-ts'
2
+ import { Prettify } from 'viem'
3
+
4
+ /**
5
+ * The DTO for the reward tokens that the sender has and wants to send.
6
+ * @param token denotes the token address
7
+ * @param amount denotes the amount of tokens the caller wants to send
8
+ * @param balance denotes the amount of tokens the caller can send
9
+ */
10
+ export type RewardTokensType = Prettify<RewardType['tokens'][number]>
11
+
12
+ /**
13
+ * The type for the route calls that the sender wants to make.
14
+ * @param target denotes the target address of the call
15
+ * @param data denotes the data of the call
16
+ * @param value denotes the native token value of the call
17
+ */
18
+ export type CallDataType = RouteType['calls'][number]
@@ -0,0 +1,215 @@
1
+ import { EcoError } from '@/common/errors/eco-error'
2
+ import { FeeAlgorithm } from '@/eco-configs/eco-config.types'
3
+ import { ValidationChecks } from '@/intent/validation.sevice'
4
+ import { Hex } from 'viem'
5
+
6
+ /**
7
+ * Errors that can be thrown by the quote service
8
+ */
9
+ export interface QuoteErrorsInterface {
10
+ statusCode: number
11
+ message: string
12
+ code: number
13
+ [key: string]: any
14
+ }
15
+
16
+ export type Quote400 = QuoteErrorsInterface & {
17
+ statusCode: 400
18
+ }
19
+
20
+ export type Quote500 = QuoteErrorsInterface & {
21
+ statusCode: 500
22
+ }
23
+
24
+ // The solver does not supoort the request prover
25
+ export const ProverUnsupported: Quote400 = {
26
+ statusCode: 400,
27
+ message: 'Bad Request: The prover selected is not supported.',
28
+ code: 1,
29
+ }
30
+
31
+ // The quote does not have a reward structure that would be accepted by solver
32
+ export const RewardInvalid: Quote400 = {
33
+ statusCode: 400,
34
+ message: "Bad Request: The reward structure is invalid. Solver doesn't accept the reward.",
35
+ code: 2,
36
+ }
37
+
38
+ // The quote does not support some of the callData
39
+ export const CallsUnsupported: Quote400 = {
40
+ statusCode: 400,
41
+ message: 'Bad Request: Some callData in calls are not supported.',
42
+ code: 3,
43
+ }
44
+
45
+ // The quote does not support some of the callData
46
+ export const SolverUnsupported: Quote400 = {
47
+ statusCode: 400,
48
+ message: "Bad Request: The solver doesn't support that chain.",
49
+ code: 4,
50
+ }
51
+
52
+ // The quote intent is deemed invalid by the validation service
53
+ export function InvalidQuoteIntent(validations: ValidationChecks): Quote400 {
54
+ return {
55
+ statusCode: 400,
56
+ message: 'Bad Request: The quote was deemed invalid.',
57
+ code: 4,
58
+ properties: {
59
+ validations,
60
+ },
61
+ }
62
+ }
63
+
64
+ /**
65
+ * The quote intent cannot be fulfilled because it doesn't have a
66
+ * reward hight enough to cover the ask
67
+ *
68
+ * @param totalAsk the total amount of the ask
69
+ * @param totalRewardAmount the total amount of the reward
70
+ * @returns
71
+ */
72
+ export function InsufficientBalance(totalAsk: bigint, totalFulfillmentAmount: bigint): Quote400 {
73
+ return {
74
+ statusCode: 400,
75
+ message:
76
+ 'Bad Request: The quote intent balance was insufficient for fulfillment. TotalAsk > TotalFulfillmentAmount',
77
+ code: 5,
78
+ properties: {
79
+ totalAsk,
80
+ totalFulfillmentAmount,
81
+ },
82
+ }
83
+ }
84
+
85
+ // The quote is deemed infeasible by the feasibility service
86
+ export function InfeasibleQuote(error: Error): Quote400 {
87
+ return {
88
+ statusCode: 400,
89
+ message: 'Bad Request: The quote was deemed infeasible.',
90
+ code: 6,
91
+ error,
92
+ }
93
+ }
94
+
95
+ // The quote is deemed invalid by the feasibility service
96
+ export function InvalidQuote(
97
+ results: (
98
+ | false
99
+ | {
100
+ solvent: boolean
101
+ profitable: boolean
102
+ }
103
+ | undefined
104
+ )[],
105
+ ): Quote400 {
106
+ return {
107
+ statusCode: 400,
108
+ message: 'Bad Request: The quote was deemed invalid.',
109
+ code: 7,
110
+ results,
111
+ }
112
+ }
113
+ // The quote is deemed to be insolvent or unprofitable by the feasibility service
114
+ export function InsolventUnprofitableQuote(
115
+ results: (
116
+ | false
117
+ | {
118
+ solvent: boolean
119
+ profitable: boolean
120
+ }
121
+ | undefined
122
+ )[],
123
+ ): Quote400 {
124
+ return {
125
+ statusCode: 400,
126
+ message: 'Bad Request: The quote was deemed to be insolvent or unprofitable.',
127
+ code: 8,
128
+ results,
129
+ }
130
+ }
131
+
132
+ /////////////
133
+
134
+ /**
135
+ * The server failed to save to db
136
+ * @param error the error that was thrown
137
+ * @returns
138
+ */
139
+ export function InternalSaveError(error: Error): Quote500 {
140
+ return {
141
+ statusCode: 500,
142
+ message: 'Internal Server Error: Failed to save quote intent.',
143
+ code: 1,
144
+ error,
145
+ }
146
+ }
147
+
148
+ /**
149
+ * The server failed to generate the quote
150
+ * @returns
151
+ */
152
+ export function InternalQuoteError(error?: Error): Quote500 {
153
+ return {
154
+ statusCode: 500,
155
+ message: 'Internal Server Error: Failed generate quote.',
156
+ code: 2,
157
+ error,
158
+ }
159
+ }
160
+
161
+ export class QuoteError extends Error {
162
+ static InvalidSolverAlgorithm(destination: bigint, algorithm: FeeAlgorithm) {
163
+ return new EcoError(
164
+ `The solver for destination chain ${destination} did not return a valid algorithm : ${algorithm} `,
165
+ )
166
+ }
167
+
168
+ static NoSolverForDestination(destination: bigint) {
169
+ return new EcoError(`No solver found for destination chain ${destination}`)
170
+ }
171
+
172
+ static NoIntentSourceForSource(source: bigint) {
173
+ return new EcoError(`No intent source found for source chain ${source}`)
174
+ }
175
+
176
+ static FetchingRewardTokensFailed(chainID: bigint) {
177
+ return new EcoError(`Error occured when fetching reward tokens for ${chainID}`)
178
+ }
179
+
180
+ static FetchingCallTokensFailed(chainID: bigint) {
181
+ return new EcoError(`Error occured when fetching call tokens for ${chainID}`)
182
+ }
183
+
184
+ static NonERC20TargetInCalls() {
185
+ return new EcoError(`One or more targets not erc20s`)
186
+ }
187
+
188
+ static SolverLacksLiquidity(
189
+ chainID: number,
190
+ target: Hex,
191
+ requested: bigint,
192
+ available: bigint,
193
+ normMinBalance: bigint,
194
+ ) {
195
+ return new EcoError(
196
+ `The solver on chain ${chainID} lacks liquidity for ${target} requested ${requested} available ${available} with a normMinBalance of ${normMinBalance}`,
197
+ )
198
+ }
199
+
200
+ static RouteIsInfeasable(ask: bigint, reward: bigint) {
201
+ return new EcoError(
202
+ `The route is not infeasable: the reward ${reward} is less than the ask ${ask}`,
203
+ )
204
+ }
205
+
206
+ static MultiFulfillRoute() {
207
+ return new EcoError(`A route with more than 1 erc20 target is not supported`)
208
+ }
209
+
210
+ static FailedToFetchTarget(chainID: bigint, target: Hex) {
211
+ return new EcoError(
212
+ `Cannot resolve the decimals of a call target ${target} on chain ${chainID}`,
213
+ )
214
+ }
215
+ }
@@ -0,0 +1,17 @@
1
+ import { Module } from '@nestjs/common'
2
+ import { QuoteService } from './quote.service'
3
+ import { MongooseModule } from '@nestjs/mongoose'
4
+ import { QuoteIntentModel, QuoteIntentSchema } from '@/quote/schemas/quote-intent.schema'
5
+ import { IntentModule } from '@/intent/intent.module'
6
+ import { FeeModule } from '@/fee/fee.module'
7
+
8
+ @Module({
9
+ imports: [
10
+ FeeModule,
11
+ IntentModule,
12
+ MongooseModule.forFeature([{ name: QuoteIntentModel.name, schema: QuoteIntentSchema }]),
13
+ ],
14
+ providers: [QuoteService],
15
+ exports: [QuoteService],
16
+ })
17
+ export class QuoteModule {}
@@ -0,0 +1,299 @@
1
+ import { EcoLogMessage } from '@/common/logging/eco-log-message'
2
+ import { RewardTokensInterface } from '@/contracts'
3
+ import { EcoConfigService } from '@/eco-configs/eco-config.service'
4
+ import { validationsSucceeded, ValidationService } from '@/intent/validation.sevice'
5
+ import { QuoteIntentDataDTO, QuoteIntentDataInterface } from '@/quote/dto/quote.intent.data.dto'
6
+ import {
7
+ InfeasibleQuote,
8
+ InsufficientBalance,
9
+ InternalQuoteError,
10
+ InternalSaveError,
11
+ InvalidQuoteIntent,
12
+ Quote400,
13
+ Quote500,
14
+ SolverUnsupported,
15
+ } from '@/quote/errors'
16
+ import { QuoteIntentModel } from '@/quote/schemas/quote-intent.schema'
17
+ import { Mathb } from '@/utils/bigint'
18
+ import { Injectable, Logger } from '@nestjs/common'
19
+ import { InjectModel } from '@nestjs/mongoose'
20
+ import { Model } from 'mongoose'
21
+ import * as dayjs from 'dayjs'
22
+ import { Hex } from 'viem'
23
+ import { FeeService } from '@/fee/fee.service'
24
+ import { CalculateTokensType } from '@/fee/types'
25
+
26
+ /**
27
+ * Service class for getting configs for the app
28
+ */
29
+ @Injectable()
30
+ export class QuoteService {
31
+ private logger = new Logger(QuoteService.name)
32
+
33
+ constructor(
34
+ @InjectModel(QuoteIntentModel.name) private quoteIntentModel: Model<QuoteIntentModel>,
35
+ private readonly feeService: FeeService,
36
+ private readonly validationService: ValidationService,
37
+ private readonly ecoConfigService: EcoConfigService,
38
+ ) {}
39
+
40
+ /**
41
+ * Generates a quote for the quote intent data.
42
+ * The network quoteIntentDataDTO is stored in the db.
43
+ *
44
+ * @param quoteIntentDataDTO the quote intent data
45
+ * @returns
46
+ */
47
+ async getQuote(quoteIntentDataDTO: QuoteIntentDataDTO) {
48
+ this.logger.log(
49
+ EcoLogMessage.fromDefault({
50
+ message: `Getting quote for intent`,
51
+ properties: {
52
+ quoteIntentDataDTO,
53
+ },
54
+ }),
55
+ )
56
+ const quoteIntent = await this.storeQuoteIntentData(quoteIntentDataDTO)
57
+ if (quoteIntent instanceof Error) {
58
+ return InternalSaveError(quoteIntent)
59
+ }
60
+ const res = await this.validateQuoteIntentData(quoteIntent)
61
+ if (res) {
62
+ return res
63
+ }
64
+
65
+ let quoteRes:
66
+ | Quote400
67
+ | Quote500
68
+ | {
69
+ tokens: RewardTokensInterface[]
70
+ expiryTime: string
71
+ }
72
+ | Error
73
+ try {
74
+ quoteRes = await this.generateQuote(quoteIntent)
75
+ } catch (e) {
76
+ quoteRes = InternalQuoteError(e)
77
+ } finally {
78
+ await this.updateQuoteDb(quoteIntent, quoteRes!)
79
+ }
80
+
81
+ return quoteRes
82
+ }
83
+
84
+ /**
85
+ * Stores the quote into the db
86
+ * @param quoteIntentDataDTO the quote intent data
87
+ * @returns the stored record or an error
88
+ */
89
+ async storeQuoteIntentData(
90
+ quoteIntentDataDTO: QuoteIntentDataDTO,
91
+ ): Promise<QuoteIntentModel | Error> {
92
+ try {
93
+ const record = await this.quoteIntentModel.create(quoteIntentDataDTO)
94
+ this.logger.log(
95
+ EcoLogMessage.fromDefault({
96
+ message: `Recorded quote intent`,
97
+ properties: {
98
+ record,
99
+ },
100
+ }),
101
+ )
102
+ return record
103
+ } catch (e) {
104
+ this.logger.error(
105
+ EcoLogMessage.fromDefault({
106
+ message: `Error in storeQuoteIntentData`,
107
+ properties: {
108
+ quoteIntentDataDTO,
109
+ error: e,
110
+ },
111
+ }),
112
+ )
113
+ return e
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Validates that the quote intent data is valid.
119
+ * Checks that there is a solver, that the assert validations pass,
120
+ * and that the quote intent is feasible.
121
+ * @param quoteIntentModel the model to validate
122
+ * @returns an res 400, or undefined if the quote intent is valid
123
+ */
124
+ async validateQuoteIntentData(quoteIntentModel: QuoteIntentModel): Promise<Quote400 | undefined> {
125
+ const solver = this.ecoConfigService.getSolver(quoteIntentModel.route.destination)
126
+ if (!solver) {
127
+ this.logger.log(
128
+ EcoLogMessage.fromDefault({
129
+ message: `validateQuoteIntentData: No solver found for destination : ${quoteIntentModel.route.destination}`,
130
+ properties: {
131
+ quoteIntentModel,
132
+ },
133
+ }),
134
+ )
135
+ await this.updateQuoteDb(quoteIntentModel, { error: SolverUnsupported })
136
+ return SolverUnsupported
137
+ }
138
+
139
+ const validations = await this.validationService.assertValidations(quoteIntentModel, solver)
140
+ if (!validationsSucceeded(validations)) {
141
+ this.logger.log(
142
+ EcoLogMessage.fromDefault({
143
+ message: `validateQuoteIntentData: Some validations failed`,
144
+ properties: {
145
+ quoteIntentModel,
146
+ validations,
147
+ },
148
+ }),
149
+ )
150
+ await this.updateQuoteDb(quoteIntentModel, { error: InvalidQuoteIntent(validations) })
151
+ return InvalidQuoteIntent(validations)
152
+ }
153
+
154
+ const { error } = await this.feeService.isRouteFeasible(quoteIntentModel)
155
+
156
+ if (error) {
157
+ const quoteError = InfeasibleQuote(error)
158
+ this.logger.log(
159
+ EcoLogMessage.fromDefault({
160
+ message: `validateQuoteIntentData: quote intent is not feasable ${quoteIntentModel._id}`,
161
+ properties: {
162
+ quoteIntentModel,
163
+ feasable: false,
164
+ error: quoteError,
165
+ },
166
+ }),
167
+ )
168
+ await this.updateQuoteDb(quoteIntentModel, { error: quoteError })
169
+ return quoteError
170
+ }
171
+ return
172
+ }
173
+
174
+ /**
175
+ * Generates a quote for the quote intent model. The quote is generated by:
176
+ * 1. Converting the call and reward tokens to a standard reserve value for comparisons
177
+ * 2. Adding a fee to the ask of the normalized call tokens
178
+ * 3. Fulfilling the ask with the reward tokens starting with any deficit tokens the solver
179
+ * has on the source chain
180
+ * 4. If there are any remaining tokens, they are used to fulfill the solver token
181
+ * starting with the smallest delta(minBalance - balance) tokens
182
+ * @param quoteIntentModel the quote intent model
183
+ * @returns the quote or an error 400 for insufficient reward to generate the quote
184
+ */
185
+ async generateQuote(quoteIntentModel: QuoteIntentDataInterface) {
186
+ const { calculated, error } = await this.feeService.calculateTokens(quoteIntentModel)
187
+ if (error || !calculated) {
188
+ return InternalQuoteError(error)
189
+ }
190
+
191
+ const { deficitDescending: fundable, calls, rewards } = calculated as CalculateTokensType
192
+
193
+ const totalFulfill = calls.reduce((acc, call) => acc + call.balance, 0n)
194
+ const totalAsk = this.feeService.getAsk(totalFulfill, quoteIntentModel)
195
+ const totalAvailableRewardAmount = rewards.reduce((acc, reward) => acc + reward.balance, 0n)
196
+ if (totalAsk > totalAvailableRewardAmount) {
197
+ return InsufficientBalance(totalAsk, totalAvailableRewardAmount)
198
+ }
199
+ let filled = 0n
200
+ const quoteRecord: Record<Hex, RewardTokensInterface> = {}
201
+ for (const deficit of fundable) {
202
+ if (filled >= totalAsk) {
203
+ break
204
+ }
205
+ const left = totalAsk - filled
206
+ //Only fill defits first pass
207
+ if (deficit.delta.balance < 0n) {
208
+ const reward = rewards.find((r) => r.address === deficit.delta.address)
209
+ if (reward) {
210
+ const amount = Mathb.min(
211
+ Mathb.min(Mathb.abs(deficit.delta.balance), reward.balance),
212
+ left,
213
+ )
214
+ if (amount > 0n) {
215
+ deficit.delta.balance += amount
216
+ reward.balance -= amount
217
+ filled += amount
218
+ //add to quote record
219
+ const tokenToFund = quoteRecord[deficit.delta.address] || {
220
+ token: deficit.delta.address,
221
+ amount: 0n,
222
+ }
223
+ tokenToFund.amount += this.feeService.deconvertNormalize(amount, deficit.delta).balance
224
+ quoteRecord[deficit.delta.address] = tokenToFund
225
+ }
226
+ }
227
+ }
228
+ }
229
+ //resort fundable to reflect first round of fills
230
+ fundable.sort((a, b) => Mathb.compare(a.delta.balance, b.delta.balance))
231
+
232
+ //if remaining funds, for those with smallest deltas
233
+ if (filled < totalAsk) {
234
+ for (const deficit of fundable) {
235
+ if (filled >= totalAsk) {
236
+ break
237
+ }
238
+ const left = totalAsk - filled
239
+ const reward = rewards.find((r) => r.address === deficit.delta.address)
240
+ if (reward) {
241
+ const amount = Mathb.min(left, reward.balance)
242
+ if (amount > 0n) {
243
+ deficit.delta.balance += amount
244
+ reward.balance -= amount
245
+ filled += amount
246
+ //add to quote record
247
+ const tokenToFund = quoteRecord[deficit.delta.address] || {
248
+ token: deficit.delta.address,
249
+ amount: 0n,
250
+ }
251
+ tokenToFund.amount += Mathb.abs(
252
+ this.feeService.deconvertNormalize(amount, deficit.delta).balance,
253
+ )
254
+ quoteRecord[deficit.delta.address] = tokenToFund
255
+ }
256
+ }
257
+ }
258
+ }
259
+
260
+ //todo save quote to record
261
+ return {
262
+ tokens: Object.values(quoteRecord),
263
+ expiryTime: this.getQuoteExpiryTime(),
264
+ }
265
+ }
266
+
267
+ /**
268
+ * @returns the expiry time of the quote
269
+ */
270
+ getQuoteExpiryTime(): string {
271
+ //todo implement expiry time logic
272
+ return dayjs().add(5, 'minutes').unix().toString()
273
+ }
274
+
275
+ /**
276
+ * Updates the quote intent model in the db
277
+ * @param quoteIntentModel the model to update
278
+ * @returns
279
+ */
280
+ async updateQuoteDb(quoteIntentModel: QuoteIntentModel, receipt?: any) {
281
+ try {
282
+ if (receipt) {
283
+ quoteIntentModel.receipt = receipt
284
+ }
285
+ await this.quoteIntentModel.updateOne({ _id: quoteIntentModel._id }, quoteIntentModel)
286
+ } catch (e) {
287
+ this.logger.error(
288
+ EcoLogMessage.fromDefault({
289
+ message: `Error in updateQuoteDb`,
290
+ properties: {
291
+ quoteIntentModel,
292
+ error: e,
293
+ },
294
+ }),
295
+ )
296
+ return e
297
+ }
298
+ }
299
+ }
@@ -0,0 +1,16 @@
1
+ import { CallDataInterface } from '@/contracts'
2
+ import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'
3
+ import { Hex } from 'viem'
4
+
5
+ @Schema({ timestamps: true })
6
+ export class QuoteRouteCallDataModel implements CallDataInterface {
7
+ @Prop({ required: true, type: String })
8
+ target: Hex
9
+ @Prop({ required: true, type: String })
10
+ data: Hex
11
+ @Prop({ required: true, type: BigInt })
12
+ value: bigint
13
+ }
14
+
15
+ export const QuoteRouteCallDataSchema = SchemaFactory.createForClass(QuoteRouteCallDataModel)
16
+ QuoteRouteCallDataSchema.index({ token: 1 }, { unique: false })
@@ -0,0 +1,27 @@
1
+ import { QuoteIntentDataInterface } from '@/quote/dto/quote.intent.data.dto'
2
+ import { QuoteRewardDataModel, QuoteRewardDataSchema } from '@/quote/schemas/quote-reward.schema'
3
+ import { QuoteRouteDataModel, QuoteRouteDataSchema } from '@/quote/schemas/quote-route.schema'
4
+ import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'
5
+ import { Types } from 'mongoose'
6
+
7
+ @Schema({ timestamps: true })
8
+ export class QuoteIntentModel implements QuoteIntentDataInterface {
9
+ _id: Types.ObjectId
10
+
11
+ @Prop({ required: true, type: String })
12
+ dAppID: string
13
+
14
+ @Prop({ required: true, type: QuoteRouteDataSchema })
15
+ route: QuoteRouteDataModel
16
+
17
+ @Prop({ required: true, type: QuoteRewardDataSchema })
18
+ reward: QuoteRewardDataModel
19
+
20
+ @Prop({ type: Object })
21
+ receipt: any
22
+ }
23
+
24
+ export const QuoteIntentSchema = SchemaFactory.createForClass(QuoteIntentModel)
25
+ QuoteIntentSchema.index({ dAppID: 1 }, { unique: false })
26
+ QuoteIntentSchema.index({ 'route.source': 1 }, { unique: false })
27
+ QuoteIntentSchema.index({ 'route.destination': 1 }, { unique: false })
@@ -0,0 +1,24 @@
1
+ import { QuoteRewardDataType } from '@/quote/dto/quote.reward.data.dto'
2
+ import {
3
+ QuoteRewardTokenDataModel,
4
+ QuoteRewardTokenDataSchema,
5
+ } from '@/quote/schemas/quote-token.schema'
6
+ import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'
7
+ import { Hex } from 'viem'
8
+
9
+ @Schema({ timestamps: true })
10
+ export class QuoteRewardDataModel implements QuoteRewardDataType {
11
+ @Prop({ required: true, type: String })
12
+ creator: Hex
13
+ @Prop({ required: true, type: String })
14
+ prover: Hex
15
+ @Prop({ required: true, type: BigInt })
16
+ deadline: bigint
17
+ @Prop({ required: true, type: BigInt })
18
+ nativeValue: bigint
19
+ @Prop({ required: true, type: [QuoteRewardTokenDataSchema] })
20
+ tokens: QuoteRewardTokenDataModel[]
21
+ }
22
+
23
+ export const QuoteRewardDataSchema = SchemaFactory.createForClass(QuoteRewardDataModel)
24
+ QuoteRewardDataSchema.index({ prover: 1 }, { unique: false })