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
package/src/main.ts ADDED
@@ -0,0 +1,63 @@
1
+ import { NestFactory } from '@nestjs/core'
2
+ import { AppModule } from './app.module'
3
+ import { NestExpressApplication } from '@nestjs/platform-express'
4
+ import { EcoConfigService } from './eco-configs/eco-config.service'
5
+ import { Logger, LoggerErrorInterceptor } from 'nestjs-pino'
6
+ import { NestApplicationOptions, ValidationPipe } from '@nestjs/common'
7
+ import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'
8
+ import { BigIntToStringInterceptor } from '@/interceptors/big-int.interceptor'
9
+
10
+ async function bootstrap() {
11
+ const app = await NestFactory.create<NestExpressApplication>(AppModule, getNestParams())
12
+ if (EcoConfigService.getStaticConfig().logger.usePino) {
13
+ app.useLogger(app.get(Logger))
14
+ app.useGlobalInterceptors(new LoggerErrorInterceptor())
15
+ }
16
+
17
+ //add dto validations, enable transformation
18
+ app.useGlobalPipes(
19
+ new ValidationPipe({
20
+ transform: true, // Enables DTO transformation for incoming requests
21
+ }),
22
+ )
23
+
24
+ //change all bigints to strings in the controller responses
25
+ app.useGlobalInterceptors(new BigIntToStringInterceptor())
26
+
27
+ //add swagger
28
+ addSwagger(app)
29
+
30
+ // Starts listening for shutdown hooks
31
+ app.enableShutdownHooks()
32
+ await app.listen(3000)
33
+ }
34
+
35
+ function getNestParams(): NestApplicationOptions {
36
+ let params = {
37
+ cors: true,
38
+ rawBody: true, //needed for AlchemyAuthMiddleware webhook verification
39
+ }
40
+ if (EcoConfigService.getStaticConfig().logger.usePino) {
41
+ params = {
42
+ ...params,
43
+ ...{
44
+ bufferLogs: true,
45
+ },
46
+ }
47
+ }
48
+
49
+ return params
50
+ }
51
+
52
+ function addSwagger(app: NestExpressApplication) {
53
+ const config = new DocumentBuilder()
54
+ .setTitle('Solver API')
55
+ .setDescription('The api for the solver queries')
56
+ .setVersion('0.1')
57
+ .addTag('solver')
58
+ .build()
59
+ const documentFactory = () => SwaggerModule.createDocument(app, config)
60
+ SwaggerModule.setup('api', app, documentFactory)
61
+ }
62
+
63
+ bootstrap()
@@ -0,0 +1,14 @@
1
+ import { ModuleMetadata, Type } from '@nestjs/common'
2
+ import { NestRedlockConfigFactory } from './nest-redlock.interface'
3
+ import { RedisConfig } from '../eco-configs/eco-config.types'
4
+
5
+ export interface NestRedlockConfig extends RedisConfig {}
6
+
7
+ export interface NestRedlockDynamicConfig extends Pick<ModuleMetadata, 'imports'> {
8
+ useFactory?: (...args: any[]) => Promise<NestRedlockConfig> | NestRedlockConfig
9
+ useClass?: Type<NestRedlockConfigFactory>
10
+ useExisting?: Type<NestRedlockConfigFactory>
11
+ inject?: any[]
12
+ }
13
+
14
+ export const NEST_REDLOCK_CONFIG = 'NEST_REDLOCK_CONFIG'
@@ -0,0 +1,5 @@
1
+ import { NestRedlockConfig } from './nest-redlock.config'
2
+
3
+ export interface NestRedlockConfigFactory {
4
+ createNestRedlockConfig(): Promise<NestRedlockConfig> | NestRedlockConfig
5
+ }
@@ -0,0 +1,64 @@
1
+ import { DynamicModule, Global, Module, Provider } from '@nestjs/common'
2
+ import { NestRedlockConfig, NestRedlockDynamicConfig } from './nest-redlock.config'
3
+ import { NEST_REDLOCK_CONFIG } from './nest-redlock.config'
4
+ import { NestRedlockConfigFactory } from './nest-redlock.interface'
5
+ import { RedlockService } from './nest-redlock.service'
6
+
7
+ @Global()
8
+ @Module({
9
+ imports: [],
10
+ providers: [RedlockService],
11
+ exports: [RedlockService],
12
+ })
13
+ export class RedlockModule {
14
+ static forRoot(config: NestRedlockConfig): DynamicModule {
15
+ return {
16
+ module: RedlockModule,
17
+ imports: [],
18
+ providers: [
19
+ {
20
+ provide: NEST_REDLOCK_CONFIG,
21
+ useValue: config,
22
+ },
23
+ ],
24
+ exports: [],
25
+ }
26
+ }
27
+
28
+ static forRootAsync(dynamicConfig: NestRedlockDynamicConfig): DynamicModule {
29
+ const providers = this.createAsyncProviders(dynamicConfig)
30
+
31
+ return {
32
+ module: RedlockModule,
33
+ imports: dynamicConfig.imports || [],
34
+ providers,
35
+ exports: [],
36
+ }
37
+ }
38
+
39
+ static createAsyncProviders(dynamicConfig: NestRedlockDynamicConfig): Provider[] {
40
+ if (dynamicConfig.useFactory) {
41
+ return [
42
+ {
43
+ provide: NEST_REDLOCK_CONFIG,
44
+ useFactory: dynamicConfig.useFactory,
45
+ inject: dynamicConfig.inject,
46
+ },
47
+ ]
48
+ }
49
+
50
+ if (dynamicConfig.useClass || dynamicConfig.useExisting) {
51
+ return [
52
+ {
53
+ provide: NEST_REDLOCK_CONFIG,
54
+ useFactory: async (configFactory: NestRedlockConfigFactory) => {
55
+ return await configFactory.createNestRedlockConfig()
56
+ },
57
+ inject: [dynamicConfig.useClass || dynamicConfig.useExisting!],
58
+ },
59
+ ]
60
+ }
61
+
62
+ return []
63
+ }
64
+ }
@@ -0,0 +1,59 @@
1
+ import { Inject, Injectable } from '@nestjs/common'
2
+ import Redlock, { Settings } from 'redlock'
3
+ import { Redis as IORedisClient, Cluster as IORedisCluster } from 'ioredis'
4
+ import { NestRedlockConfig, NEST_REDLOCK_CONFIG } from './nest-redlock.config'
5
+ import { RedisConnectionUtils } from '../common/redis/redis-connection-utils'
6
+ import { Lock } from 'redlock'
7
+
8
+ export type RedlockRedisClient = IORedisClient | IORedisCluster
9
+
10
+ @Injectable()
11
+ export class RedlockService extends Redlock {
12
+ constructor(@Inject(NEST_REDLOCK_CONFIG) redlockConfig: NestRedlockConfig) {
13
+ const { redlockSettings } = redlockConfig
14
+ const redisClients = RedisConnectionUtils.getClientsForRedlock(redlockConfig)
15
+
16
+ super(redisClients, redlockSettings)
17
+ }
18
+
19
+ /**
20
+ * Executes the callback if the lock key is required, otherwise it throws an error
21
+ * @param key the key to lock on
22
+ * @param callback the callback to execute
23
+ * @returns
24
+ */
25
+ async lockCall(key: string, callback: () => Promise<any>): Promise<any> {
26
+ const lock = await this.acquireLock([key], 5000)
27
+ if (!lock) {
28
+ throw new Error('Could not acquire lock')
29
+ }
30
+
31
+ try {
32
+ return await callback()
33
+ } catch (e) {
34
+ throw e
35
+ } finally {
36
+ await lock.release()
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Non-throwing lock aquire that returns null if the lock is not available
42
+ *
43
+ * @param resources
44
+ * @param duration time in ms
45
+ * @param settings
46
+ * @returns
47
+ */
48
+ async acquireLock(
49
+ resources: string[],
50
+ duration: number,
51
+ settings?: Partial<Settings>,
52
+ ): Promise<Lock | null> {
53
+ try {
54
+ return await this.acquire(resources, duration, settings)
55
+ } catch {
56
+ return null
57
+ }
58
+ }
59
+ }
@@ -0,0 +1,184 @@
1
+ import { Injectable, Logger, OnModuleInit } from '@nestjs/common'
2
+ import { addSeconds, compareAsc } from 'date-fns'
3
+ import { MultichainPublicClientService } from '../transaction/multichain-public-client.service'
4
+ import { Hex } from 'viem'
5
+ import {
6
+ PROOF_HYPERLANE,
7
+ PROOF_STORAGE,
8
+ ProofCall,
9
+ ProofType,
10
+ ProverInterfaceAbi,
11
+ } from '../contracts'
12
+ import { entries } from 'lodash'
13
+ import { EcoConfigService } from '../eco-configs/eco-config.service'
14
+ import { EcoLogMessage } from '../common/logging/eco-log-message'
15
+
16
+ /**
17
+ * Service class for getting information about the provers and their configurations.
18
+ */
19
+ @Injectable()
20
+ export class ProofService implements OnModuleInit {
21
+ private logger = new Logger(ProofService.name)
22
+
23
+ /**
24
+ * Variable storing the proof type for each prover address. Used to determine
25
+ * what function to call on the Inbox contract
26
+ */
27
+ private proofContracts: Record<Hex, ProofType> = {}
28
+
29
+ constructor(
30
+ private readonly publicClient: MultichainPublicClientService,
31
+ private readonly ecoConfigService: EcoConfigService,
32
+ ) {}
33
+
34
+ async onModuleInit() {
35
+ await this.loadProofTypes()
36
+ }
37
+
38
+ /**
39
+ * Returns the proof type for a given prover address
40
+ *
41
+ * @param proverAddress
42
+ * @returns the proof type, defaults to {@link PROOF_STORAGE}
43
+ */
44
+ getProofType(proverAddress: Hex): ProofType {
45
+ return this.proofContracts[proverAddress]
46
+ }
47
+
48
+ /**
49
+ * Checks if the prover is a hyperlane prover
50
+ * @param proverAddress the prover address
51
+ * @returns
52
+ */
53
+ isHyperlaneProver(proverAddress: Hex): boolean {
54
+ return this.getProofType(proverAddress) === PROOF_HYPERLANE
55
+ }
56
+
57
+ /**
58
+ * Checks if the prover is a storage prover
59
+ * @param proverAddress the prover address
60
+ * @returns
61
+ */
62
+ isStorageProver(proverAddress: Hex): boolean {
63
+ return this.getProofType(proverAddress) === PROOF_STORAGE
64
+ }
65
+
66
+ /**
67
+ * Returns all the prover addresses for a given proof type
68
+ * @param proofType the proof type
69
+ * @returns
70
+ */
71
+ getProvers(proofType: ProofType): Hex[] {
72
+ return entries(this.proofContracts)
73
+ .filter(([, type]) => type === proofType)
74
+ .map(([address]) => address as Hex)
75
+ }
76
+
77
+ /**
78
+ * Returns the prover type for a given prover address
79
+ * @param prover the prover address
80
+ * @returns
81
+ */
82
+ getProverType(prover: Hex): ProofType {
83
+ return this.proofContracts[prover]
84
+ }
85
+
86
+ /**
87
+ * Loads the proof types for each prover address into memory.
88
+ * Assume all provers must have the same proof type if their
89
+ * hex address is the same.
90
+ */
91
+ private async loadProofTypes() {
92
+ const proofPromises = this.ecoConfigService.getIntentSources().map(async (source) => {
93
+ return await this.getProofTypes(source.chainID, source.provers)
94
+ })
95
+
96
+ // get the proof types for each prover address from on chain
97
+ const proofs = await Promise.all(proofPromises)
98
+
99
+ // reduce the array of proof objects into a single object, removing duplicates
100
+ proofs.reduce((acc, proof) => {
101
+ entries(proof).forEach(([proverAddress, proofType]) => {
102
+ acc[proverAddress] = proofType
103
+ })
104
+ return acc
105
+ }, this.proofContracts)
106
+
107
+ this.logger.debug(
108
+ EcoLogMessage.fromDefault({
109
+ message: `loadProofTypes loaded all the proof types`,
110
+ properties: {
111
+ proofs: this.proofContracts,
112
+ },
113
+ }),
114
+ )
115
+ }
116
+
117
+ /**
118
+ * Fetches all the proof types for the provers on a given chain using {@link ViemMultichainClientService#multicall}
119
+ *
120
+ * @param chainID the chain id
121
+ * @param provers the prover addresses
122
+ * @returns
123
+ */
124
+ private async getProofTypes(chainID: number, provers: Hex[]): Promise<Record<Hex, ProofType>> {
125
+ const client = await this.publicClient.getClient(Number(chainID))
126
+ const proofCalls: ProofCall[] = provers.map((proverAddress) => {
127
+ return {
128
+ address: proverAddress,
129
+ abi: ProverInterfaceAbi,
130
+ functionName: 'getProofType',
131
+ }
132
+ })
133
+
134
+ const proofs = (await client.multicall({
135
+ contracts: proofCalls.flat(),
136
+ })) as any
137
+ let proof: ProofType = 0,
138
+ i = 0
139
+ const proofObj: Record<Hex, ProofType> = {}
140
+ while (proofs.length > 0 && ([{ result: proof }] = [proofs.shift()])) {
141
+ proofObj[provers[i]] = proof
142
+ i++
143
+ }
144
+
145
+ return proofObj
146
+ }
147
+
148
+ /**
149
+ * Check to see if the expiration of an intent is after the minimum proof time from now.
150
+ *
151
+ * @param prover the address of the prover
152
+ * @param expirationDate the expiration date
153
+ * @returns true if the intent can be proven before the minimum proof time, false otherwise
154
+ */
155
+ isIntentExpirationWithinProofMinimumDate(prover: Hex, expirationDate: Date): boolean {
156
+ return compareAsc(expirationDate, this.getProofMinimumDate(this.proofContracts[prover])) == 1
157
+ }
158
+
159
+ /**
160
+ * Gets the minimum date that a proof can be generated for a given chain id.
161
+ * @param chainID the chain id
162
+ * @returns
163
+ */
164
+ getProofMinimumDate(prover: ProofType): Date {
165
+ return addSeconds(new Date(), this.getProofMinimumDurationSeconds(prover))
166
+ }
167
+
168
+ /**
169
+ * The minimum duration that a proof can be generated for a given prover
170
+ *
171
+ * @param prover the address of the prover
172
+ * @returns
173
+ */
174
+ private getProofMinimumDurationSeconds(prover: ProofType): number {
175
+ const proofs = this.ecoConfigService.getIntentConfigs().proofs
176
+ switch (prover) {
177
+ case PROOF_HYPERLANE:
178
+ return proofs.hyperlane_duration_seconds
179
+ case PROOF_STORAGE:
180
+ default:
181
+ return proofs.storage_duration_seconds
182
+ }
183
+ }
184
+ }
@@ -0,0 +1,10 @@
1
+ import { Module } from '@nestjs/common'
2
+ import { ProofService } from './proof.service'
3
+ import { TransactionModule } from '../transaction/transaction.module'
4
+
5
+ @Module({
6
+ imports: [TransactionModule],
7
+ providers: [ProofService],
8
+ exports: [ProofService],
9
+ })
10
+ export class ProverModule {}
@@ -0,0 +1,154 @@
1
+ import { createMock, DeepMocked } from '@golevelup/ts-jest'
2
+ import { EcoConfigService } from '../../eco-configs/eco-config.service'
3
+ import { Test, TestingModule } from '@nestjs/testing'
4
+ import { ProofService } from '../../prover/proof.service'
5
+ import { PROOF_HYPERLANE, PROOF_STORAGE, ProofType } from '../../contracts'
6
+ import { Hex } from 'viem'
7
+ import { MultichainPublicClientService } from '../../transaction/multichain-public-client.service'
8
+ import { addSeconds } from 'date-fns'
9
+
10
+ describe('ProofService', () => {
11
+ let proofService: ProofService
12
+ let multichainPublicClientService: DeepMocked<MultichainPublicClientService>
13
+ let ecoConfigService: DeepMocked<EcoConfigService>
14
+
15
+ const mockLogDebug = jest.fn()
16
+ const mockLogLog = jest.fn()
17
+
18
+ beforeEach(async () => {
19
+ const chainMod: TestingModule = await Test.createTestingModule({
20
+ providers: [
21
+ ProofService,
22
+ {
23
+ provide: MultichainPublicClientService,
24
+ useValue: createMock<MultichainPublicClientService>(),
25
+ },
26
+ { provide: EcoConfigService, useValue: createMock<EcoConfigService>() },
27
+ ],
28
+ }).compile()
29
+
30
+ proofService = chainMod.get(ProofService)
31
+ multichainPublicClientService = chainMod.get(MultichainPublicClientService)
32
+ ecoConfigService = chainMod.get(EcoConfigService)
33
+
34
+ proofService['logger'].debug = mockLogDebug
35
+ proofService['logger'].log = mockLogLog
36
+ })
37
+
38
+ afterEach(async () => {
39
+ jest.restoreAllMocks()
40
+ mockLogDebug.mockClear()
41
+ mockLogLog.mockClear()
42
+ })
43
+
44
+ describe('on startup', () => {
45
+ it('should call loadProofTypes', async () => {
46
+ const mockLoad = jest.fn()
47
+ proofService['loadProofTypes'] = mockLoad
48
+ await proofService.onModuleInit()
49
+ expect(mockLoad).toHaveBeenCalledTimes(1)
50
+ })
51
+ })
52
+
53
+ describe('on loadProofTypes', () => {
54
+ const mockGetProofTypes = jest.fn()
55
+ const intentSources = [
56
+ { chainID: 1, provers: ['0x123', '0x456'] },
57
+ { chainID: 2, provers: ['0x123', '0x777'] },
58
+ ]
59
+ const proofContracts = {
60
+ [intentSources[0].provers[0]]: PROOF_HYPERLANE,
61
+ [intentSources[0].provers[1]]: PROOF_HYPERLANE,
62
+ [intentSources[1].provers[1]]: PROOF_STORAGE,
63
+ }
64
+ const proof1: Record<Hex, ProofType> = {}
65
+ const proof2: Record<Hex, ProofType> = {}
66
+ beforeEach(async () => {
67
+ intentSources[0].provers.forEach((s) => {
68
+ proof1[s] = PROOF_HYPERLANE
69
+ })
70
+ intentSources[1].provers.forEach((s) => {
71
+ if (s === intentSources[0].provers[0]) {
72
+ proof2[s] = PROOF_HYPERLANE
73
+ return
74
+ }
75
+ proof2[s] = PROOF_STORAGE
76
+ })
77
+ proofService['getProofTypes'] = mockGetProofTypes.mockImplementation(
78
+ async (chainID: number) => {
79
+ switch (chainID) {
80
+ case 1:
81
+ return proof1
82
+ case 2:
83
+ return proof2
84
+ }
85
+ },
86
+ )
87
+ ecoConfigService.getIntentSources = jest.fn().mockReturnValue(intentSources)
88
+ await proofService.onModuleInit()
89
+ })
90
+
91
+ afterEach(() => {
92
+ mockGetProofTypes.mockClear()
93
+ })
94
+
95
+ it('should call getProofTypes for all source intents', async () => {
96
+ expect(mockGetProofTypes).toHaveBeenCalledTimes(intentSources.length)
97
+ })
98
+
99
+ it('should set the proofContracts', async () => {
100
+ expect(proofService['proofContracts']).toEqual(proofContracts)
101
+ })
102
+
103
+ it('should should log', async () => {
104
+ expect(mockLogDebug).toHaveBeenCalledTimes(1)
105
+ expect(mockLogDebug).toHaveBeenCalledWith({
106
+ msg: 'loadProofTypes loaded all the proof types',
107
+ proofs: proofContracts,
108
+ })
109
+ })
110
+ })
111
+
112
+ describe('on utility methods', () => {
113
+ const intentConfigs = {
114
+ proofs: {
115
+ storage_duration_seconds: 10,
116
+ hyperlane_duration_seconds: 20,
117
+ },
118
+ }
119
+ beforeEach(async () => {
120
+ ecoConfigService.getIntentConfigs = jest.fn().mockReturnValue(intentConfigs)
121
+ })
122
+ it('should correctly check if its a hyperlane prover', async () => {
123
+ jest.spyOn(proofService, 'getProofType').mockReturnValue(PROOF_HYPERLANE)
124
+ expect(proofService.isHyperlaneProver('0x123')).toBe(true)
125
+ expect(proofService.isStorageProver('0x123')).toBe(false)
126
+ })
127
+
128
+ it('should correctly check if its a storage prover', async () => {
129
+ jest.spyOn(proofService, 'getProofType').mockReturnValue(PROOF_STORAGE)
130
+ expect(proofService.isHyperlaneProver('0x123')).toBe(false)
131
+ expect(proofService.isStorageProver('0x123')).toBe(true)
132
+ })
133
+
134
+ it('should return the correct minimum proof time', async () => {
135
+ expect(proofService['getProofMinimumDurationSeconds'](PROOF_HYPERLANE)).toBe(
136
+ intentConfigs.proofs.hyperlane_duration_seconds,
137
+ )
138
+
139
+ expect(proofService['getProofMinimumDurationSeconds'](PROOF_STORAGE)).toBe(
140
+ intentConfigs.proofs.storage_duration_seconds,
141
+ )
142
+ })
143
+
144
+ it('should return whether the intent expires too soon', async () => {
145
+ const seconds = 100
146
+ const expires = addSeconds(new Date(), seconds)
147
+ proofService['getProofMinimumDurationSeconds'] = jest.fn().mockReturnValue(seconds / 2)
148
+ expect(proofService.isIntentExpirationWithinProofMinimumDate('0x123', expires)).toBe(true)
149
+
150
+ proofService['getProofMinimumDurationSeconds'] = jest.fn().mockReturnValue(seconds * 2)
151
+ expect(proofService.isIntentExpirationWithinProofMinimumDate('0x123', expires)).toBe(false)
152
+ })
153
+ })
154
+ })
@@ -0,0 +1,35 @@
1
+ import { QuoteRewardDataDTO, QuoteRewardDataType } from '@/quote/dto/quote.reward.data.dto'
2
+ import { QuoteRouteDataDTO, QuoteRouteDataInterface } from '@/quote/dto/quote.route.data.dto'
3
+ import { ApiProperty } from '@nestjs/swagger'
4
+ import { Type } from 'class-transformer'
5
+ import { IsNotEmpty, ValidateNested } from 'class-validator'
6
+
7
+ /**
8
+ * The DTO for the intent data. Similar to {@link IntentType} except modified to
9
+ * include options for the solver to select fulfillment conditions, and with the
10
+ * on-chain data fields removed.
11
+ */
12
+ export class QuoteIntentDataDTO implements QuoteIntentDataInterface {
13
+ @IsNotEmpty()
14
+ @ApiProperty()
15
+ dAppID: string
16
+
17
+ @IsNotEmpty()
18
+ @ApiProperty()
19
+ @ValidateNested()
20
+ @Type(() => QuoteRouteDataDTO)
21
+ route: QuoteRouteDataDTO
22
+
23
+ @IsNotEmpty()
24
+ @ApiProperty()
25
+ @ValidateNested()
26
+ @Type(() => QuoteRewardDataDTO)
27
+ reward: QuoteRewardDataDTO
28
+ }
29
+
30
+ export interface QuoteIntentDataInterface {
31
+ // The dApp ID of the intent, optional so schema can be shared for onchain intents and offchain quotes
32
+ dAppID?: string
33
+ route: QuoteRouteDataInterface
34
+ reward: QuoteRewardDataType
35
+ }
@@ -0,0 +1,67 @@
1
+ import { RewardTokensInterface } from '@/contracts'
2
+ import { ViemAddressTransform } from '@/transforms/viem-address.decorator'
3
+ import { RewardType } from '@eco-foundation/routes-ts'
4
+ import { ApiProperty } from '@nestjs/swagger'
5
+ import { Transform, Type } from 'class-transformer'
6
+ import { ArrayNotEmpty, IsArray, IsNotEmpty, ValidateNested } from 'class-validator'
7
+ import { getAddress, Hex } from 'viem'
8
+
9
+ /**
10
+ * The DTO for the intent reward data. Similar to {@link RewardType} except
11
+ * that it does not contain the creator and tokens fields. Also has a modified
12
+ * tokens field that is an array of {@link QuoteRewardTokensDTO} which include the
13
+ * sender's willing token balance to use for the reward.
14
+ * @param prover denotes the prover address
15
+ * @param deadline denotes the deadline for the reward
16
+ * @param nativeValue denotes the native token value of the reward
17
+ * @param tokens denotes the array of {@link QuoteRewardTokensDTO} that the sender has
18
+ */
19
+ export class QuoteRewardDataDTO implements QuoteRewardDataType {
20
+ @ViemAddressTransform()
21
+ @IsNotEmpty()
22
+ @ApiProperty()
23
+ creator: Hex
24
+
25
+ @ViemAddressTransform()
26
+ @IsNotEmpty()
27
+ @ApiProperty()
28
+ prover: Hex
29
+
30
+ @IsNotEmpty()
31
+ @Transform(({ value }) => BigInt(value))
32
+ @ApiProperty()
33
+ deadline: bigint
34
+
35
+ @IsNotEmpty()
36
+ @Transform(({ value }) => BigInt(value))
37
+ @ApiProperty()
38
+ nativeValue: bigint
39
+
40
+ @IsArray()
41
+ @ArrayNotEmpty()
42
+ @ValidateNested()
43
+ @ApiProperty()
44
+ @Type(() => QuoteRewardTokensDTO)
45
+ tokens: QuoteRewardTokensDTO[]
46
+ }
47
+
48
+ /**
49
+ * The DTO for the reward tokens that the sender has and wants to send.
50
+ * @param token denotes the token address
51
+ * @param amount denotes the amount of tokens the caller wants to send
52
+ * @param balance denotes the amount of tokens the caller can send
53
+ */
54
+ export class QuoteRewardTokensDTO implements RewardTokensInterface {
55
+ @ViemAddressTransform()
56
+ @IsNotEmpty()
57
+ @ApiProperty()
58
+ @Transform(({ value }) => getAddress(value))
59
+ token: Hex
60
+
61
+ @IsNotEmpty()
62
+ @Transform(({ value }) => BigInt(value))
63
+ @ApiProperty()
64
+ amount: bigint
65
+ }
66
+ type QuoteRewardType = RewardType
67
+ export type QuoteRewardDataType = QuoteRewardType