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,67 @@
1
+ import { Injectable, Logger } from '@nestjs/common'
2
+ import { HealthIndicator, HealthIndicatorResult } from '@nestjs/terminus'
3
+ import { exec } from 'child_process'
4
+ import { readFileSync } from 'fs'
5
+ import { join } from 'path'
6
+ import { EcoLogMessage } from '../../common/logging/eco-log-message'
7
+
8
+ const ECO_ROUTES_PACKAGE_NAME = '@eco-foundation/routes-ts'
9
+ @Injectable()
10
+ export class GitCommitHealthIndicator extends HealthIndicator {
11
+ private logger = new Logger(GitCommitHealthIndicator.name)
12
+ constructor() {
13
+ super()
14
+ }
15
+
16
+ async gitCommit(): Promise<HealthIndicatorResult> {
17
+ const npmLib = this.getDependencyVersion(ECO_ROUTES_PACKAGE_NAME)
18
+ return this.getStatus('git-commit', !!npmLib, {
19
+ commitHash: await this.getCommitHash(),
20
+ ecoRoutesVersion: npmLib,
21
+ })
22
+ }
23
+
24
+ private async getCommitHash(): Promise<string> {
25
+ return new Promise((resolve, reject) => {
26
+ exec('git rev-parse HEAD', (error, stdout) => {
27
+ if (error) {
28
+ reject(error)
29
+ } else {
30
+ resolve(stdout.trim())
31
+ }
32
+ })
33
+ })
34
+ }
35
+
36
+ private getDependencyVersion(
37
+ dependencyName: string,
38
+ ): { version: string; npm: string } | undefined {
39
+ try {
40
+ // Path to the project's package.json file
41
+ const packageJsonPath = join(process.cwd(), 'package.json')
42
+
43
+ // Read and parse the package.json file
44
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'))
45
+
46
+ // Check dependencies and devDependencies for the specified dependency
47
+ const version =
48
+ packageJson.dependencies?.[dependencyName] ||
49
+ packageJson.devDependencies?.[dependencyName] ||
50
+ 'undefined'
51
+ return {
52
+ version,
53
+ npm: `https://www.npmjs.com/package/${dependencyName}/v/${version.replace('^', '')}?activeTab=code`,
54
+ }
55
+ } catch (error) {
56
+ this.logger.error(
57
+ EcoLogMessage.fromDefault({
58
+ message: 'Error reading package.json:',
59
+ properties: {
60
+ error,
61
+ },
62
+ }),
63
+ )
64
+ return undefined // Return undefined if there is an error
65
+ }
66
+ }
67
+ }
@@ -0,0 +1,11 @@
1
+ import { Injectable, Logger } from '@nestjs/common'
2
+ import { HealthIndicatorResult, MongooseHealthIndicator } from '@nestjs/terminus'
3
+
4
+ @Injectable()
5
+ export class MongoDBHealthIndicator extends MongooseHealthIndicator {
6
+ private logger = new Logger(MongoDBHealthIndicator.name)
7
+
8
+ async checkMongoDB(): Promise<HealthIndicatorResult> {
9
+ return this.pingCheck('mongoDB')
10
+ }
11
+ }
@@ -0,0 +1,64 @@
1
+ import { Injectable, Logger } from '@nestjs/common'
2
+ import { HealthCheckError, HealthIndicator, HealthIndicatorResult } from '@nestjs/terminus'
3
+ import { EcoConfigService } from '../../eco-configs/eco-config.service'
4
+ import { Solver } from '../../eco-configs/eco-config.types'
5
+ import { Hex } from 'viem'
6
+ import { KernelAccountClientService } from '../../transaction/smart-wallets/kernel/kernel-account-client.service'
7
+ import { InboxAbi } from '@eco-foundation/routes-ts'
8
+
9
+ @Injectable()
10
+ export class PermissionHealthIndicator extends HealthIndicator {
11
+ private logger = new Logger(PermissionHealthIndicator.name)
12
+ private readonly solverPermissions: Map<string, { account: Hex; whitelisted: boolean }> =
13
+ new Map()
14
+
15
+ constructor(
16
+ private readonly kernelAccountClientService: KernelAccountClientService,
17
+ private readonly configService: EcoConfigService,
18
+ ) {
19
+ super()
20
+ }
21
+
22
+ async checkPermissions(): Promise<HealthIndicatorResult> {
23
+ //iterate over all solvers
24
+ await Promise.all(
25
+ Object.entries(this.configService.getSolvers()).map(async (entry) => {
26
+ const [, solver] = entry
27
+ await this.loadPermissions(solver)
28
+ }),
29
+ )
30
+ const isHealthy = Array.from(this.solverPermissions.values()).every(
31
+ (value) => value.whitelisted,
32
+ )
33
+ const permissionsString = {}
34
+ this.solverPermissions.forEach((value, key) => {
35
+ permissionsString[key] = value
36
+ })
37
+
38
+ const results = this.getStatus('permissions', isHealthy, { permissions: permissionsString })
39
+ if (isHealthy) {
40
+ return results
41
+ }
42
+ throw new HealthCheckError('Permissions failed', results)
43
+ }
44
+
45
+ private async loadPermissions(solver: Solver) {
46
+ const key = this.getSolverKey(solver.network, solver.chainID, solver.inboxAddress)
47
+ const client = await this.kernelAccountClientService.getClient(solver.chainID)
48
+ const address = client.kernelAccount.address
49
+ const whitelisted = await client.readContract({
50
+ address: solver.inboxAddress,
51
+ abi: InboxAbi,
52
+ functionName: 'solverWhitelist',
53
+ args: [address],
54
+ })
55
+ this.solverPermissions.set(key, {
56
+ account: address,
57
+ whitelisted,
58
+ })
59
+ }
60
+
61
+ private getSolverKey(network: string, chainID: number, address: Hex): string {
62
+ return `${network}-${chainID}-${address}`
63
+ }
64
+ }
@@ -0,0 +1,129 @@
1
+ import { Injectable, Logger, OnModuleInit } from '@nestjs/common'
2
+ import { EcoConfigService } from '../eco-configs/eco-config.service'
3
+ import { EcoLogMessage } from '../common/logging/eco-log-message'
4
+ import { QUEUES } from '../common/redis/constants'
5
+ import { JobsOptions, Queue } from 'bullmq'
6
+ import { InjectQueue } from '@nestjs/bullmq'
7
+ import { IntentSourceModel } from './schemas/intent-source.schema'
8
+ import { InjectModel } from '@nestjs/mongoose'
9
+ import { Model } from 'mongoose'
10
+ import { getIntentJobId } from '../common/utils/strings'
11
+ import { Hex } from 'viem'
12
+ import { ValidSmartWalletService } from '../solver/filters/valid-smart-wallet.service'
13
+ import { decodeCreateIntentLog, IntentCreatedLog } from '../contracts'
14
+ import { IntentDataModel } from './schemas/intent-data.schema'
15
+ import { FlagService } from '../flags/flags.service'
16
+ import { deserialize, Serialize } from '@/liquidity-manager/utils/serialize'
17
+
18
+ /**
19
+ * This service is responsible for creating a new intent record in the database. It is
20
+ * triggered when a new intent is created recieved in {@link WatchIntentService}.
21
+ * It validates that the record doesn't exist yet, and that its creator is a valid BEND wallet
22
+ */
23
+ @Injectable()
24
+ export class CreateIntentService implements OnModuleInit {
25
+ private logger = new Logger(CreateIntentService.name)
26
+ private intentJobConfig: JobsOptions
27
+
28
+ constructor(
29
+ @InjectQueue(QUEUES.SOURCE_INTENT.queue) private readonly intentQueue: Queue,
30
+ @InjectModel(IntentSourceModel.name) private intentModel: Model<IntentSourceModel>,
31
+ private readonly validSmartWalletService: ValidSmartWalletService,
32
+ private readonly flagService: FlagService,
33
+ private readonly ecoConfigService: EcoConfigService,
34
+ ) {}
35
+
36
+ onModuleInit() {
37
+ this.intentJobConfig = this.ecoConfigService.getRedis().jobs.intentJobConfig
38
+ }
39
+
40
+ /**
41
+ * Decodes the intent log, validates the creator is a valid BEND wallet, and creates a new record in the database
42
+ * if one doesn't yet exist. Finally it enqueue the intent for validation
43
+ *
44
+ * @param serializedIntentWs the serialized intent created log
45
+ * @returns
46
+ */
47
+ async createIntent(serializedIntentWs: Serialize<IntentCreatedLog>) {
48
+ const intentWs = deserialize(serializedIntentWs)
49
+
50
+ this.logger.debug(
51
+ EcoLogMessage.fromDefault({
52
+ message: `createIntent ${intentWs.transactionHash}`,
53
+ properties: {
54
+ intentHash: intentWs.transactionHash,
55
+ },
56
+ }),
57
+ )
58
+
59
+ const ei = decodeCreateIntentLog(intentWs.data, intentWs.topics)
60
+ const intent = IntentDataModel.fromEvent(ei, intentWs.logIndex || 0)
61
+
62
+ try {
63
+ //check db if the intent is already filled
64
+ const model = await this.intentModel.findOne({
65
+ 'intent.hash': intent.hash,
66
+ })
67
+
68
+ if (model) {
69
+ // Record already exists, do nothing and return
70
+ this.logger.debug(
71
+ EcoLogMessage.fromDefault({
72
+ message: `Record for intent already exists ${intent.hash}`,
73
+ properties: {
74
+ intentHash: intent.hash,
75
+ intent: intent,
76
+ },
77
+ }),
78
+ )
79
+ return
80
+ }
81
+
82
+ const validWallet = this.flagService.getFlagValue('bendWalletOnly')
83
+ ? await this.validSmartWalletService.validateSmartWallet(
84
+ intent.reward.creator as Hex,
85
+ intentWs.sourceChainID,
86
+ )
87
+ : true
88
+
89
+ //create db record
90
+ const record = await this.intentModel.create({
91
+ event: intentWs,
92
+ intent: intent,
93
+ receipt: null,
94
+ status: validWallet ? 'PENDING' : 'NON-BEND-WALLET',
95
+ })
96
+
97
+ const jobId = getIntentJobId('create', intent.hash as Hex, intent.logIndex)
98
+ if (validWallet) {
99
+ //add to processing queue
100
+ await this.intentQueue.add(QUEUES.SOURCE_INTENT.jobs.validate_intent, intent.hash, {
101
+ jobId,
102
+ ...this.intentJobConfig,
103
+ })
104
+ }
105
+
106
+ this.logger.log(
107
+ EcoLogMessage.fromDefault({
108
+ message: `Recorded intent ${record.intent.hash}`,
109
+ properties: {
110
+ intentHash: intent.hash,
111
+ intent: record.intent,
112
+ validWallet,
113
+ ...(validWallet ? { jobId } : {}),
114
+ },
115
+ }),
116
+ )
117
+ } catch (e) {
118
+ this.logger.error(
119
+ EcoLogMessage.fromDefault({
120
+ message: `Error in createIntent ${intentWs.transactionHash}`,
121
+ properties: {
122
+ intentHash: intentWs.transactionHash,
123
+ error: e,
124
+ },
125
+ }),
126
+ )
127
+ }
128
+ }
129
+ }
@@ -0,0 +1,80 @@
1
+ import { Injectable, Logger, OnModuleInit } from '@nestjs/common'
2
+ import { EcoConfigService } from '../eco-configs/eco-config.service'
3
+ import { JobsOptions, Queue } from 'bullmq'
4
+ import { InjectQueue } from '@nestjs/bullmq'
5
+ import { QUEUES } from '../common/redis/constants'
6
+ import { UtilsIntentService } from './utils-intent.service'
7
+ import { EcoLogMessage } from '../common/logging/eco-log-message'
8
+ import { getIntentJobId } from '../common/utils/strings'
9
+ import { Hex } from 'viem'
10
+ import { QuoteIntentModel } from '@/quote/schemas/quote-intent.schema'
11
+ import { FeeService } from '@/fee/fee.service'
12
+
13
+ /**
14
+ * Service class for getting configs for the app
15
+ */
16
+ @Injectable()
17
+ export class FeasableIntentService implements OnModuleInit {
18
+ private logger = new Logger(FeasableIntentService.name)
19
+ private intentJobConfig: JobsOptions
20
+ constructor(
21
+ @InjectQueue(QUEUES.SOURCE_INTENT.queue) private readonly intentQueue: Queue,
22
+ private readonly feeService: FeeService,
23
+ private readonly utilsIntentService: UtilsIntentService,
24
+ private readonly ecoConfigService: EcoConfigService,
25
+ ) {}
26
+
27
+ async onModuleInit() {
28
+ this.intentJobConfig = this.ecoConfigService.getRedis().jobs.intentJobConfig
29
+ }
30
+ async feasableQuote(quoteIntent: QuoteIntentModel) {
31
+ this.logger.debug(
32
+ EcoLogMessage.fromDefault({
33
+ message: `feasableQuote intent ${quoteIntent._id}`,
34
+ }),
35
+ )
36
+ }
37
+ /**
38
+ * Validates that the execution of the intent is feasible. This means that the solver can execute
39
+ * the transaction and that transaction cost is profitable to the solver.
40
+ * @param intentHash the intent hash to fetch the intent data from the db with
41
+ * @returns
42
+ */
43
+ async feasableIntent(intentHash: Hex) {
44
+ this.logger.debug(
45
+ EcoLogMessage.fromDefault({
46
+ message: `FeasableIntent intent ${intentHash}`,
47
+ }),
48
+ )
49
+ const data = await this.utilsIntentService.getIntentProcessData(intentHash)
50
+ const { model, solver, err } = data ?? {}
51
+ if (!model || !solver) {
52
+ if (err) {
53
+ throw err
54
+ }
55
+ return
56
+ }
57
+
58
+ const { error } = await this.feeService.isRouteFeasible(model.intent)
59
+
60
+ const jobId = getIntentJobId('feasable', intentHash, model!.intent.logIndex)
61
+ this.logger.debug(
62
+ EcoLogMessage.fromDefault({
63
+ message: `FeasableIntent intent ${intentHash}`,
64
+ properties: {
65
+ feasable: !error,
66
+ ...(!error ? { jobId } : {}),
67
+ },
68
+ }),
69
+ )
70
+ if (!error) {
71
+ //add to processing queue
72
+ await this.intentQueue.add(QUEUES.SOURCE_INTENT.jobs.fulfill_intent, intentHash, {
73
+ jobId,
74
+ ...this.intentJobConfig,
75
+ })
76
+ } else {
77
+ await this.utilsIntentService.updateInfeasableIntentModel(model, error)
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,318 @@
1
+ import { Injectable, Logger } from '@nestjs/common'
2
+ import {
3
+ ContractFunctionArgs,
4
+ ContractFunctionName,
5
+ encodeAbiParameters,
6
+ encodeFunctionData,
7
+ erc20Abi,
8
+ Hex,
9
+ pad,
10
+ zeroAddress,
11
+ } from 'viem'
12
+ import {
13
+ IntentProcessData,
14
+ TransactionTargetData,
15
+ UtilsIntentService,
16
+ } from './utils-intent.service'
17
+ import { getERC20Selector } from '../contracts'
18
+ import { EcoError } from '../common/errors/eco-error'
19
+ import { EcoLogMessage } from '../common/logging/eco-log-message'
20
+ import { Solver } from '../eco-configs/eco-config.types'
21
+ import { IntentSourceModel } from './schemas/intent-source.schema'
22
+ import { EcoConfigService } from '../eco-configs/eco-config.service'
23
+ import { ProofService } from '../prover/proof.service'
24
+ import { ExecuteSmartWalletArg } from '../transaction/smart-wallets/smart-wallet.types'
25
+ import { KernelAccountClientService } from '../transaction/smart-wallets/kernel/kernel-account-client.service'
26
+ import { InboxAbi } from '@eco-foundation/routes-ts'
27
+ import { getTransactionTargetData } from '@/intent/utils'
28
+ import { FeeService } from '@/fee/fee.service'
29
+ import { IntentDataModel } from '@/intent/schemas/intent-data.schema'
30
+
31
+ type FulfillmentMethod = ContractFunctionName<typeof InboxAbi>
32
+
33
+ /**
34
+ * This class fulfills an intent by creating the transactions for the intent targets and the fulfill intent transaction.
35
+ */
36
+ @Injectable()
37
+ export class FulfillIntentService {
38
+ private logger = new Logger(FulfillIntentService.name)
39
+
40
+ constructor(
41
+ private readonly kernelAccountClientService: KernelAccountClientService,
42
+ private readonly proofService: ProofService,
43
+ private readonly feeService: FeeService,
44
+ private readonly utilsIntentService: UtilsIntentService,
45
+ private readonly ecoConfigService: EcoConfigService,
46
+ ) {}
47
+
48
+ /**
49
+ * Executes the fulfill intent process for an intent. It creates the transaction for fulfillment, and posts it
50
+ * to the chain. It then updates the db model of the intent with the status and receipt.
51
+ *
52
+ * @param intentHash the intent hash to fulfill
53
+ * @returns
54
+ */
55
+ async executeFulfillIntent(intentHash: Hex) {
56
+ const data = await this.utilsIntentService.getIntentProcessData(intentHash)
57
+ const { model, solver, err } = data ?? {}
58
+ if (!data || !model || !solver) {
59
+ if (err) {
60
+ throw err
61
+ }
62
+ return
63
+ }
64
+ // If the intent is already solved, return
65
+ // Could happen if redis has pending job while this is still executing
66
+ if (model.status === 'SOLVED') {
67
+ return
68
+ }
69
+
70
+ const kernelAccountClient = await this.kernelAccountClientService.getClient(solver.chainID)
71
+
72
+ // Create transactions for intent targets
73
+ const targetSolveTxs = this.getTransactionsForTargets(data)
74
+
75
+ // Create fulfill tx
76
+ const fulfillTx = await this.getFulfillIntentTx(solver.inboxAddress, model)
77
+
78
+ // Combine all transactions
79
+ const transactions = [...targetSolveTxs, fulfillTx]
80
+
81
+ this.logger.debug(
82
+ EcoLogMessage.fromDefault({
83
+ message: `Fulfilling ${this.getFulfillment()} transaction`,
84
+ properties: {
85
+ transactions,
86
+ },
87
+ }),
88
+ )
89
+
90
+ try {
91
+ await this.finalFeasibilityCheck(model.intent)
92
+
93
+ const transactionHash = await kernelAccountClient.execute(transactions)
94
+
95
+ const receipt = await kernelAccountClient.waitForTransactionReceipt({ hash: transactionHash })
96
+
97
+ // set the status and receipt for the model
98
+ model.receipt = receipt as any
99
+ if (receipt.status === 'reverted') {
100
+ throw EcoError.FulfillIntentRevertError(receipt)
101
+ }
102
+ model.status = 'SOLVED'
103
+
104
+ this.logger.debug(
105
+ EcoLogMessage.fromDefault({
106
+ message: `Fulfilled transactionHash ${receipt.transactionHash}`,
107
+ properties: {
108
+ userOPHash: receipt,
109
+ destinationChainID: model.intent.route.destination,
110
+ sourceChainID: model.event.sourceChainID,
111
+ },
112
+ }),
113
+ )
114
+ } catch (e) {
115
+ model.status = 'FAILED'
116
+ model.receipt = model.receipt ? { previous: model.receipt, current: e } : e
117
+
118
+ this.logger.error(
119
+ EcoLogMessage.withError({
120
+ message: `fulfillIntent: Invalid transaction`,
121
+ error: EcoError.FulfillIntentBatchError,
122
+ properties: {
123
+ model: model,
124
+ flatExecuteData: transactions,
125
+ errorPassed: e,
126
+ },
127
+ }),
128
+ )
129
+
130
+ // Throw error to retry job
131
+ throw e
132
+ } finally {
133
+ // Update the db model
134
+ await this.utilsIntentService.updateIntentModel(model)
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Checks that the intent is feasible for the fulfillment. This
140
+ * could occur due to changes to the fees/limits of the intent. A failed
141
+ * intent might retry later when its no longer profitable, etc.
142
+ * Throws an error if the intent is not feasible.
143
+ * @param intent the intent to check
144
+ */
145
+ async finalFeasibilityCheck(intent: IntentDataModel) {
146
+ const { error } = await this.feeService.isRouteFeasible(intent)
147
+ if (error) {
148
+ throw error
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Checks if the transaction is feasible for an erc20 token transfer.
154
+ *
155
+ * @param tt the transaction target data
156
+ * @param solver the target solver
157
+ * @param target the target ERC20 address
158
+ * @returns
159
+ */
160
+ handleErc20(tt: TransactionTargetData, solver: Solver, target: Hex) {
161
+ switch (tt.selector) {
162
+ case getERC20Selector('transfer'):
163
+ const dstAmount = tt.decodedFunctionData.args?.[1] as bigint
164
+ // Approve the inbox to spend the amount, inbox contract pulls the funds
165
+ // then does the transfer call for the target
166
+ const transferFunctionData = encodeFunctionData({
167
+ abi: erc20Abi,
168
+ functionName: 'approve',
169
+ args: [solver.inboxAddress, dstAmount], //spender, amount
170
+ })
171
+
172
+ return [{ to: target, data: transferFunctionData }]
173
+ default:
174
+ return []
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Returns the transactions for the intent targets
180
+ * @param intentProcessData
181
+ * @private
182
+ */
183
+ private getTransactionsForTargets(intentProcessData: IntentProcessData) {
184
+ const { model, solver } = intentProcessData
185
+ if (!model || !solver) {
186
+ return []
187
+ }
188
+
189
+ // Create transactions for intent targets
190
+ return model.intent.route.calls.flatMap((call) => {
191
+ const tt = getTransactionTargetData(solver, call)
192
+ if (tt === null) {
193
+ this.logger.error(
194
+ EcoLogMessage.withError({
195
+ message: `fulfillIntent: Invalid transaction data`,
196
+ error: EcoError.FulfillIntentNoTransactionError,
197
+ properties: {
198
+ model: model,
199
+ },
200
+ }),
201
+ )
202
+ return []
203
+ }
204
+
205
+ switch (tt.targetConfig.contractType) {
206
+ case 'erc20':
207
+ return this.handleErc20(tt, solver, call.target)
208
+ case 'erc721':
209
+ case 'erc1155':
210
+ default:
211
+ return []
212
+ }
213
+ })
214
+ }
215
+
216
+ /**
217
+ * Returns the fulfill intent data
218
+ * @param inboxAddress
219
+ * @param model
220
+ * @private
221
+ */
222
+ private async getFulfillIntentTx(
223
+ inboxAddress: Hex,
224
+ model: IntentSourceModel,
225
+ ): Promise<ExecuteSmartWalletArg> {
226
+ const claimant = this.ecoConfigService.getEth().claimant
227
+ const isHyperlane = this.proofService.isHyperlaneProver(model.intent.reward.prover)
228
+ const functionName: FulfillmentMethod = this.proofService.isStorageProver(
229
+ model.intent.reward.prover,
230
+ )
231
+ ? 'fulfillStorage'
232
+ : this.getFulfillment()
233
+
234
+ const args = [
235
+ model.intent.route,
236
+ // @ts-expect-error we dynamically set the args
237
+ model.intent.reward.getHash(),
238
+ claimant,
239
+ // @ts-expect-error we dynamically set the args
240
+ model.intent.getHash().intentHash,
241
+ ]
242
+
243
+ if (isHyperlane) {
244
+ args.push(model.intent.reward.prover)
245
+ if (functionName === 'fulfillHyperInstantWithRelayer') {
246
+ args.push('0x0')
247
+ args.push(zeroAddress)
248
+ }
249
+ }
250
+ let fee = 0n
251
+ if (isHyperlane && functionName === 'fulfillHyperInstantWithRelayer') {
252
+ fee = BigInt((await this.getHyperlaneFee(inboxAddress, model)) || '0x0')
253
+ }
254
+
255
+ const fulfillIntentData = encodeFunctionData({
256
+ abi: InboxAbi,
257
+ functionName,
258
+ // @ts-expect-error we dynamically set the args
259
+ args,
260
+ })
261
+
262
+ return {
263
+ to: inboxAddress,
264
+ data: fulfillIntentData,
265
+ // ...(isHyperlane && fee > 0 && { value: fee }),
266
+ value: fee,
267
+ }
268
+ }
269
+
270
+ /**
271
+ * Returns the hyperlane fee
272
+ * @param prover
273
+ * @private
274
+ */
275
+ private async getHyperlaneFee(
276
+ inboxAddress: Hex,
277
+ model: IntentSourceModel,
278
+ ): Promise<Hex | undefined> {
279
+ const client = await this.kernelAccountClientService.getClient(
280
+ Number(model.intent.route.destination),
281
+ )
282
+ const encodedMessageBody = encodeAbiParameters(
283
+ [{ type: 'bytes[]' }, { type: 'address[]' }],
284
+ [[model.intent.hash], [this.ecoConfigService.getEth().claimant]],
285
+ )
286
+ const functionName = 'fetchFee'
287
+ const args: ContractFunctionArgs<typeof InboxAbi, 'view', typeof functionName> = [
288
+ model.event.sourceChainID, //_sourceChainID
289
+ pad(model.intent.reward.prover), //_prover
290
+ encodedMessageBody, //_messageBody
291
+ '0x0', //_metadata
292
+ zeroAddress, //_postDispatchHook
293
+ ]
294
+ const callData = encodeFunctionData({
295
+ abi: InboxAbi,
296
+ functionName,
297
+ args,
298
+ })
299
+ const proverData = await client.call({
300
+ to: inboxAddress,
301
+ data: callData,
302
+ })
303
+ return proverData.data
304
+ }
305
+
306
+ /**
307
+ * @returns the fulfillment method
308
+ */
309
+ private getFulfillment(): FulfillmentMethod {
310
+ switch (this.ecoConfigService.getFulfill().run) {
311
+ case 'batch':
312
+ return 'fulfillHyperBatched'
313
+ case 'single':
314
+ default:
315
+ return 'fulfillHyperInstantWithRelayer'
316
+ }
317
+ }
318
+ }