eco-solver 0.0.1-security → 1.5.0

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 +66 -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,337 @@
1
+ const mockGetTransactionTargetData = jest.fn()
2
+ import { Test, TestingModule } from '@nestjs/testing'
3
+ import { createMock, DeepMocked } from '@golevelup/ts-jest'
4
+ import { TransactionTargetData, UtilsIntentService } from '@/intent/utils-intent.service'
5
+ import { ProofService } from '@/prover/proof.service'
6
+ import {
7
+ ValidationChecks,
8
+ ValidationService,
9
+ validationsSucceeded,
10
+ } from '@/intent/validation.sevice'
11
+ import { EcoConfigService } from '@/eco-configs/eco-config.service'
12
+ import { entries } from 'lodash'
13
+ import { FeeService } from '@/fee/fee.service'
14
+ import { FeeConfigType } from '@/eco-configs/eco-config.types'
15
+ jest.mock('@/intent/utils', () => {
16
+ return {
17
+ ...jest.requireActual('@/intent/utils'),
18
+ getTransactionTargetData: mockGetTransactionTargetData,
19
+ }
20
+ })
21
+ describe('ValidationService', () => {
22
+ let validationService: ValidationService
23
+ let proofService: DeepMocked<ProofService>
24
+ let feeService: DeepMocked<FeeService>
25
+ let ecoConfigService: DeepMocked<EcoConfigService>
26
+ let utilsIntentService: DeepMocked<UtilsIntentService>
27
+ const mockLogLog = jest.fn()
28
+
29
+ beforeEach(async () => {
30
+ const mod: TestingModule = await Test.createTestingModule({
31
+ providers: [
32
+ ValidationService,
33
+ { provide: ProofService, useValue: createMock<ProofService>() },
34
+ { provide: FeeService, useValue: createMock<FeeService>() },
35
+ { provide: EcoConfigService, useValue: createMock<EcoConfigService>() },
36
+ { provide: UtilsIntentService, useValue: createMock<UtilsIntentService>() },
37
+ ],
38
+ }).compile()
39
+
40
+ validationService = mod.get(ValidationService)
41
+ proofService = mod.get(ProofService)
42
+ feeService = mod.get(FeeService)
43
+ ecoConfigService = mod.get(EcoConfigService)
44
+ utilsIntentService = mod.get(UtilsIntentService)
45
+
46
+ validationService['logger'].log = mockLogLog
47
+
48
+ jest.spyOn(ecoConfigService, 'getIntentConfigs').mockReturnValueOnce({} as any)
49
+ })
50
+
51
+ afterEach(async () => {
52
+ jest.restoreAllMocks()
53
+ mockLogLog.mockClear()
54
+ })
55
+
56
+ describe('on validationsSucceeded', () => {
57
+ it('should return false if any validations are false', async () => {
58
+ const validations = {
59
+ supportedProver: true,
60
+ supportedTargets: true,
61
+ supportedSelectors: true,
62
+ validTransferLimit: true,
63
+ validExpirationTime: false,
64
+ validDestination: true,
65
+ fulfillOnDifferentChain: true,
66
+ } as ValidationChecks
67
+ expect(validationsSucceeded(validations)).toBe(false)
68
+ })
69
+
70
+ it('should return false if all validations are false', async () => {
71
+ const validations = {
72
+ supportedProver: false,
73
+ supportedTargets: false,
74
+ supportedSelectors: false,
75
+ validTransferLimit: false,
76
+ validExpirationTime: false,
77
+ validDestination: false,
78
+ fulfillOnDifferentChain: false,
79
+ } as ValidationChecks
80
+ expect(validationsSucceeded(validations)).toBe(false)
81
+ })
82
+
83
+ it('should return true if all validations are true', async () => {
84
+ const validations = {
85
+ supportedProver: true,
86
+ supportedTargets: true,
87
+ supportedSelectors: true,
88
+ validExpirationTime: true,
89
+ validDestination: true,
90
+ fulfillOnDifferentChain: true,
91
+ } as ValidationChecks
92
+ expect(validationsSucceeded(validations)).toBe(true)
93
+ })
94
+ })
95
+
96
+ describe('on individual validation cases', () => {
97
+ describe('on supportedProver', () => {
98
+ const sourceChainID = 1n
99
+ const chainID = sourceChainID
100
+ const prover = '0xcf25397DC87C750eEF006101172FFbeAeA98Aa76'
101
+ const unsupportedChain = 2n
102
+ const unsupportedProver = '0x26D2C47c5659aC8a1c4A29A052Fa7B2ccD45Ca43'
103
+ it('should fail if no source intent exists with the models source chain id', async () => {
104
+ const intent = { event: { sourceChainID } } as any
105
+ ecoConfigService.getIntentSources.mockReturnValueOnce([])
106
+ expect(validationService.supportedProver(intent)).toBe(false)
107
+ })
108
+
109
+ it('should fail if no source supports the prover', async () => {
110
+ const intent = { event: { sourceChainID }, intent: { reward: { prover } } } as any
111
+ ecoConfigService.getIntentSources.mockReturnValueOnce([
112
+ { provers: [unsupportedProver], chainID } as any,
113
+ ])
114
+ expect(validationService.supportedProver(intent)).toBe(false)
115
+ })
116
+
117
+ it('should fail if no source supports the prover on the required chain', async () => {
118
+ const intent = { event: { sourceChainID }, intent: { prover } } as any
119
+ ecoConfigService.getIntentSources.mockReturnValueOnce([
120
+ { provers: [prover], chainID: unsupportedChain } as any,
121
+ ])
122
+ expect(validationService.supportedProver(intent)).toBe(false)
123
+ })
124
+
125
+ it('should succeed if a single source supports the prover', async () => {
126
+ const intent = { sourceChainID, prover } as any
127
+ ecoConfigService.getIntentSources.mockReturnValueOnce([
128
+ { provers: [unsupportedProver], chainID } as any,
129
+ { provers: [prover], chainID } as any,
130
+ ])
131
+ expect(validationService.supportedProver(intent)).toBe(true)
132
+ })
133
+
134
+ it('should succeed if multiple sources supports the prover', async () => {
135
+ const intent = { sourceChainID, prover } as any
136
+ ecoConfigService.getIntentSources.mockReturnValueOnce([
137
+ { provers: [prover], chainID } as any,
138
+ { provers: [prover], chainID } as any,
139
+ ])
140
+ expect(validationService.supportedProver(intent)).toBe(true)
141
+ })
142
+ })
143
+
144
+ describe('on supportedTargets', () => {
145
+ const intent = { route: { calls: [] } } as any
146
+ const solver = { targets: {} } as any
147
+ it('should fail solver has no targets', async () => {
148
+ intent.route.calls = []
149
+ expect(validationService.supportedTargets(intent, solver)).toBe(false)
150
+ })
151
+
152
+ it('should fail intent has no targets', async () => {
153
+ intent.route.calls = [{ target: '0x1' }]
154
+ solver.targets = {}
155
+ expect(validationService.supportedTargets(intent, solver)).toBe(false)
156
+ })
157
+
158
+ it('should fail not all targets are supported on solver', async () => {
159
+ intent.route.calls = [{ target: '0x1' }, { target: '0x2' }]
160
+ solver.targets = { [intent.route.calls[0].target]: {} }
161
+ expect(validationService.supportedTargets(intent, solver)).toBe(false)
162
+ })
163
+
164
+ it('should succeed if targets supported ', async () => {
165
+ intent.route.calls = [{ target: '0x1' }, { target: '0x2' }]
166
+ solver.targets = { [intent.route.calls[0].target]: {}, [intent.route.calls[1].target]: {} }
167
+ expect(validationService.supportedTargets(intent, solver)).toBe(true)
168
+ })
169
+ })
170
+
171
+ describe('on supportedSelectors', () => {
172
+ const intent = { route: { calls: [] } } as any
173
+ const solver = { targets: {} } as any
174
+ it('should fail if there are no calls', async () => {
175
+ intent.route.calls = []
176
+ expect(validationService.supportedSelectors(intent, solver)).toBe(false)
177
+ expect(mockLogLog).toHaveBeenCalledTimes(1)
178
+ expect(mockLogLog).toHaveBeenCalledWith({ msg: 'supportedSelectors: Target/data invalid' })
179
+ })
180
+
181
+ it('should fail not every call is supported', async () => {
182
+ intent.route.calls = [{ target: '0x1' }, { target: '0x2' }]
183
+ mockGetTransactionTargetData.mockImplementation((solver, call) => {
184
+ return call.target == intent.route.calls[0].target
185
+ ? ({} as any as TransactionTargetData)
186
+ : null
187
+ })
188
+ expect(validationService.supportedSelectors(intent, solver)).toBe(false)
189
+ })
190
+
191
+ it('should succeed if every call is supported', async () => {
192
+ intent.route.calls = [{ target: '0x1' }, { target: '0x2' }]
193
+ mockGetTransactionTargetData.mockReturnValue({} as any as TransactionTargetData)
194
+ expect(validationService.supportedSelectors(intent, solver)).toBe(true)
195
+ })
196
+ })
197
+
198
+ describe('on validTransferLimit', () => {
199
+ const defaultFee: FeeConfigType = {
200
+ limitFillBase6: 1000n * 10n ** 6n,
201
+ algorithm: 'linear',
202
+ constants: {
203
+ baseFee: 20_000n,
204
+ tranche: {
205
+ unitFee: 15_000n,
206
+ unitSize: 100_000_000n,
207
+ },
208
+ },
209
+ }
210
+ it('should return false if feeService does', async () => {
211
+ const error = new Error('error here')
212
+ jest
213
+ .spyOn(feeService, 'getTotalFill')
214
+ .mockResolvedValueOnce({ totalFillNormalized: 1n, error })
215
+ expect(await validationService.validTransferLimit({} as any)).toBe(false)
216
+ })
217
+
218
+ it('should return false if the total fill above the max fill', async () => {
219
+ jest.spyOn(feeService, 'getTotalFill').mockResolvedValueOnce({
220
+ totalFillNormalized: defaultFee.limitFillBase6 + 1n,
221
+ })
222
+ const mockFeeConfig = jest.fn().mockReturnValue(defaultFee)
223
+ feeService.getFeeConfig = mockFeeConfig
224
+ expect(await validationService.validTransferLimit({} as any)).toBe(false)
225
+ expect(mockFeeConfig).toHaveBeenCalledTimes(1)
226
+ })
227
+
228
+ it('should return true if no error and the total fill is below max fill', async () => {
229
+ const mockFeeConfig = jest.fn().mockReturnValue(defaultFee)
230
+ feeService.getFeeConfig = mockFeeConfig
231
+
232
+ jest
233
+ .spyOn(feeService, 'getTotalFill')
234
+ .mockResolvedValueOnce({ totalFillNormalized: defaultFee.limitFillBase6 - 1n })
235
+ expect(await validationService.validTransferLimit({} as any)).toBe(true)
236
+ expect(mockFeeConfig).toHaveBeenCalledTimes(1)
237
+ })
238
+ })
239
+
240
+ describe('on validExpirationTime', () => {
241
+ //mostly covered in utilsIntentService
242
+ it('should return whatever UtilsIntentService does', async () => {
243
+ const intent = { reward: { deadline: 100 } } as any
244
+ proofService.isIntentExpirationWithinProofMinimumDate.mockReturnValueOnce(true)
245
+ expect(validationService['validExpirationTime'](intent)).toBe(true)
246
+ proofService.isIntentExpirationWithinProofMinimumDate.mockReturnValueOnce(false)
247
+ expect(validationService['validExpirationTime'](intent)).toBe(false)
248
+ })
249
+ })
250
+
251
+ describe('on validDestination', () => {
252
+ it('should fail if destination is not supported', async () => {
253
+ const intent = { route: { destination: 10n } } as any
254
+ ecoConfigService.getSupportedChains.mockReturnValueOnce([11n, 12n])
255
+ expect(validationService['validDestination'](intent)).toBe(false)
256
+ })
257
+
258
+ it('should fail if destination is not supported', async () => {
259
+ const intent = { route: { destination: 10n } } as any
260
+ ecoConfigService.getSupportedChains.mockReturnValueOnce([10n, 12n])
261
+ expect(validationService['validDestination'](intent)).toBe(true)
262
+ })
263
+ })
264
+
265
+ describe('on fulfillOnDifferentChain', () => {
266
+ it('should fail if the fulfillment is on the same chain as the event', async () => {
267
+ const intent = {
268
+ route: { destination: 10, source: 10 },
269
+ } as any
270
+ expect(validationService['fulfillOnDifferentChain'](intent)).toBe(false)
271
+ })
272
+
273
+ it('should succeed if the fulfillment is on a different chain as the event', async () => {
274
+ const intent = {
275
+ route: { destination: 10, source: 20 },
276
+ } as any
277
+ proofService.isIntentExpirationWithinProofMinimumDate.mockReturnValueOnce(true)
278
+ expect(validationService['fulfillOnDifferentChain'](intent)).toBe(true)
279
+ })
280
+ })
281
+ })
282
+
283
+ describe('on assertValidations', () => {
284
+ const updateInvalidIntentModel = jest.fn()
285
+ const assetCases: Record<keyof ValidationChecks, string> = {
286
+ supportedProver: 'supportedProver',
287
+ supportedTargets: 'supportedTargets',
288
+ supportedSelectors: 'supportedSelectors',
289
+ validTransferLimit: 'validTransferLimit',
290
+ validExpirationTime: 'validExpirationTime',
291
+ validDestination: 'validDestination',
292
+ fulfillOnDifferentChain: 'fulfillOnDifferentChain',
293
+ }
294
+ beforeEach(() => {
295
+ utilsIntentService.updateInvalidIntentModel = updateInvalidIntentModel
296
+ })
297
+
298
+ afterEach(() => {
299
+ jest.clearAllMocks()
300
+ })
301
+
302
+ entries(assetCases).forEach(([fun, boolVarName]: [string, string]) => {
303
+ it(`should fail on ${fun}`, async () => {
304
+ const intent = {
305
+ reward: {
306
+ creator: '0xa',
307
+ prover: '0xb',
308
+ deadline: 100,
309
+ tokens: [
310
+ { token: '0x1', amount: 1n },
311
+ { token: '0x2', amount: 2n },
312
+ ],
313
+ },
314
+ route: {
315
+ salt: '0x1',
316
+ destination: 10,
317
+ source: 11,
318
+ calls: [],
319
+ },
320
+ } as any
321
+ const solver = { targets: {} } as any
322
+ const logObj = entries(assetCases).reduce(
323
+ (ac, [, a]) => ({ ...ac, [a]: a == boolVarName }),
324
+ {},
325
+ )
326
+ if (boolVarName == 'fulfillOnDifferentChain') {
327
+ intent.route.destination = intent.route.source
328
+ }
329
+ const now = new Date()
330
+ proofService.getProofMinimumDate = jest.fn().mockReturnValueOnce(now)
331
+ validationService[fun] = jest.fn().mockReturnValueOnce(false)
332
+ const validations = await validationService['assertValidations'](intent, solver)
333
+ expect(validations[boolVarName]).toBe(false)
334
+ })
335
+ })
336
+ })
337
+ })
@@ -0,0 +1,168 @@
1
+ import { Injectable, Logger } from '@nestjs/common'
2
+ import { InjectModel } from '@nestjs/mongoose'
3
+ import { IntentSourceModel } from './schemas/intent-source.schema'
4
+ import { Model } from 'mongoose'
5
+ import { EcoLogMessage } from '../common/logging/eco-log-message'
6
+ import { EcoConfigService } from '../eco-configs/eco-config.service'
7
+ import { Solver, TargetContract } from '../eco-configs/eco-config.types'
8
+ import { EcoError } from '../common/errors/eco-error'
9
+ import { DecodeFunctionDataReturnType, Hex } from 'viem'
10
+ import { FulfillmentLog } from '@/contracts/inbox'
11
+ import { Network } from 'alchemy-sdk'
12
+ import { ValidationChecks } from '@/intent/validation.sevice'
13
+
14
+ /**
15
+ * Data for a transaction target
16
+ */
17
+ export interface TransactionTargetData {
18
+ decodedFunctionData: DecodeFunctionDataReturnType
19
+ selector: Hex
20
+ targetConfig: TargetContract
21
+ }
22
+
23
+ /**
24
+ * Type for logging in validations
25
+ */
26
+ export interface IntentLogType {
27
+ hash?: Hex
28
+ sourceNetwork?: Network
29
+ }
30
+
31
+ /**
32
+ * Model and solver for the intent
33
+ */
34
+ export interface IntentProcessData {
35
+ model: IntentSourceModel | null
36
+ solver: Solver | null
37
+ err?: EcoError
38
+ }
39
+
40
+ /**
41
+ * Service class for solving an intent on chain
42
+ */
43
+ @Injectable()
44
+ export class UtilsIntentService {
45
+ private logger = new Logger(UtilsIntentService.name)
46
+
47
+ constructor(
48
+ @InjectModel(IntentSourceModel.name) private intentModel: Model<IntentSourceModel>,
49
+ private readonly ecoConfigService: EcoConfigService,
50
+ ) {}
51
+
52
+ /**
53
+ * updateOne the intent model in the database, using the intent hash as the query
54
+ *
55
+ * @param intentModel the model factory to use
56
+ * @param model the new model data
57
+ */
58
+ async updateIntentModel(model: IntentSourceModel) {
59
+ return await this.intentModel.updateOne({ 'intent.hash': model.intent.hash }, model)
60
+ }
61
+
62
+ /**
63
+ * Updates the intent model with the invalid cause, using {@link updateIntentModel}
64
+ *
65
+ * @param intentModel the model factory to use
66
+ * @param model the new model data
67
+ * @param invalidCause the reason the intent is invalid
68
+ * @returns
69
+ */
70
+ async updateInvalidIntentModel(model: IntentSourceModel, invalidCause: ValidationChecks) {
71
+ model.status = 'INVALID'
72
+ model.receipt = invalidCause as any
73
+ return await this.updateIntentModel(model)
74
+ }
75
+
76
+ /**
77
+ * Updates the intent model with the infeasable cause and receipt, using {@link updateIntentModel}
78
+ *
79
+ * @param intentModel the model factory to use
80
+ * @param model the new model data
81
+ * @param infeasable the infeasable result
82
+ * @returns
83
+ */
84
+ async updateInfeasableIntentModel(model: IntentSourceModel, infeasable: Error) {
85
+ model.status = 'INFEASABLE'
86
+ model.receipt = infeasable as any
87
+ return await this.updateIntentModel(model)
88
+ }
89
+
90
+ /**
91
+ * Updates the intent model with the fulfillment status. If the intent was fulfilled by this solver, then
92
+ * the status should already be SOLVED: in that case this function does nothing.
93
+ *
94
+ * @param fulfillment the fulfillment log event
95
+ */
96
+ async updateOnFulfillment(fulfillment: FulfillmentLog) {
97
+ const model = await this.intentModel.findOne({
98
+ 'intent.hash': fulfillment.args._hash,
99
+ })
100
+ if (model) {
101
+ model.status = 'SOLVED'
102
+ await this.intentModel.updateOne({ 'intent.hash': fulfillment.args._hash }, model)
103
+ } else {
104
+ this.logger.warn(
105
+ EcoLogMessage.fromDefault({
106
+ message: `Intent not found for fulfillment ${fulfillment.args._hash}`,
107
+ properties: {
108
+ fulfillment,
109
+ },
110
+ }),
111
+ )
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Finds the the intent model in the database by the intent hash and the solver that can fulfill
117
+ * on the destination chain for that intent
118
+ *
119
+ * @param intentHash the intent hash
120
+ * @returns Intent model and solver
121
+ */
122
+ async getIntentProcessData(intentHash: string): Promise<IntentProcessData | undefined> {
123
+ try {
124
+ const model = await this.intentModel.findOne({
125
+ 'intent.hash': intentHash,
126
+ })
127
+ if (!model) {
128
+ return { model, solver: null, err: EcoError.IntentSourceDataNotFound(intentHash) }
129
+ }
130
+
131
+ const solver = await this.getSolver(model.intent.route.destination, {
132
+ intentHash: intentHash,
133
+ sourceNetwork: model.event.sourceNetwork,
134
+ })
135
+ if (!solver) {
136
+ return
137
+ }
138
+ return { model, solver }
139
+ } catch (e) {
140
+ this.logger.error(
141
+ EcoLogMessage.fromDefault({
142
+ message: `Error in getIntentProcessData ${intentHash}`,
143
+ properties: {
144
+ intentHash: intentHash,
145
+ error: e,
146
+ },
147
+ }),
148
+ )
149
+ return
150
+ }
151
+ }
152
+
153
+ async getSolver(destination: bigint, opts?: any): Promise<Solver | undefined> {
154
+ const solver = this.ecoConfigService.getSolver(destination)
155
+ if (!solver) {
156
+ this.logger.log(
157
+ EcoLogMessage.fromDefault({
158
+ message: `No solver found for chain ${destination}`,
159
+ properties: {
160
+ ...(opts ? opts : {}),
161
+ },
162
+ }),
163
+ )
164
+ return
165
+ }
166
+ return solver
167
+ }
168
+ }
@@ -0,0 +1,37 @@
1
+ import { EcoError } from '@/common/errors/eco-error'
2
+ import { getFunctionBytes } from '@/common/viem/contracts'
3
+ import { CallDataInterface, getERCAbi } from '@/contracts'
4
+ import { Solver, TargetContract } from '@/eco-configs/eco-config.types'
5
+ import { TransactionTargetData } from '@/intent/utils-intent.service'
6
+ import { includes } from 'lodash'
7
+ import { decodeFunctionData, toFunctionSelector } from 'viem'
8
+
9
+ /**
10
+ * Decodes the function data for a target contract
11
+ *
12
+ * @param solver the solver for the intent
13
+ * @param call the call to decode
14
+ * @returns
15
+ */
16
+ export function getTransactionTargetData(
17
+ solver: Solver,
18
+ call: CallDataInterface,
19
+ ): TransactionTargetData | null {
20
+ const targetConfig = solver.targets[call.target as string] as TargetContract
21
+ if (!targetConfig) {
22
+ //shouldn't happen since we do this.targetsSupported(model, solver) before this call
23
+ throw EcoError.IntentSourceTargetConfigNotFound(call.target as string)
24
+ }
25
+
26
+ const tx = decodeFunctionData({
27
+ abi: getERCAbi(targetConfig.contractType),
28
+ data: call.data,
29
+ })
30
+ const selector = getFunctionBytes(call.data)
31
+ const supportedSelectors = targetConfig.selectors.map((s) => toFunctionSelector(s))
32
+ const supported = tx && includes(supportedSelectors, selector)
33
+ if (!supported) {
34
+ return null
35
+ }
36
+ return { decodedFunctionData: tx, selector, targetConfig }
37
+ }