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,49 @@
1
+ import { Hex } from 'viem'
2
+
3
+ export function getRandomString() {
4
+ return Math.random().toString(36).slice(2)
5
+ }
6
+
7
+ export function getDestinationNetworkAddressKey(
8
+ chainID: number | bigint,
9
+ tokenAddress: string,
10
+ ): string {
11
+ return `${chainID}-${tokenAddress}`
12
+ }
13
+
14
+ /**
15
+ * Appends the service name to the intent hash for the job id, else it will be the same for all intents
16
+ * as they progress down the processing pipe and interfere in the queue
17
+ *
18
+ * @param intentHash the hash of the intent to fulfill
19
+ * @param logIndex the transaction index of the intent to fulfill. Necessary if multiple intents are in the same transaction
20
+ * @returns
21
+ */
22
+ export function getIntentJobId(
23
+ serviceName: string,
24
+ intentHash: Hex | undefined,
25
+ logIndex: number = 0,
26
+ ): string {
27
+ return `${serviceName}-${intentHash}-${logIndex}`
28
+ }
29
+
30
+ /**
31
+ * Obscures the center of a string, leaving a number of characters visible at the start and end
32
+ * @param str the string to obscure
33
+ * @param visibleChars number of characters at the start and end of the string to leave visible
34
+ * @returns
35
+ */
36
+ export function obscureCenter(str: string, visibleChars: number = 2): string {
37
+ if (visibleChars <= 0) {
38
+ return str
39
+ }
40
+ if (str.length <= visibleChars * 2) {
41
+ return '*'.repeat(str.length) // If string is too short, obscure all characters
42
+ }
43
+
44
+ const start = str.slice(0, visibleChars)
45
+ const end = str.slice(-visibleChars)
46
+ const middle = '*'.repeat(str.length - visibleChars * 2)
47
+
48
+ return `${start}${middle}${end}`
49
+ }
@@ -0,0 +1,23 @@
1
+ import { hashObject, serializeObject } from '@/common/utils/objects'
2
+ import { keccak256, toBytes } from 'viem'
3
+
4
+ describe('serializeObject', () => {
5
+ it('should serialize an object with sorted keys', () => {
6
+ const obj = { b: 2, a: 1 }
7
+ const expected = '{"a":1,"b":2}'
8
+ expect(serializeObject(obj)).toBe(expected)
9
+ })
10
+
11
+ it('should handle an empty object', () => {
12
+ expect(serializeObject({})).toBe('{}')
13
+ })
14
+ })
15
+
16
+ describe('hashObject', () => {
17
+ it('should return a keccak256 hash of the serialized object', () => {
18
+ const obj = { foo: 'bar', num: 42 }
19
+ const json = serializeObject(obj)
20
+ const expectedHash = keccak256(toBytes(json))
21
+ expect(hashObject(obj)).toBe(expectedHash)
22
+ })
23
+ })
@@ -0,0 +1,22 @@
1
+ import { obscureCenter } from '@/common/utils/strings'
2
+
3
+ describe('obscureCenter', () => {
4
+ it('should obscure the center of a string with default visible characters', () => {
5
+ expect(obscureCenter('hello world')).toBe('he*******ld')
6
+ })
7
+
8
+ it('should obscure the center with a custom number of visible characters', () => {
9
+ expect(obscureCenter('typescript', 3)).toBe('typ****ipt')
10
+ })
11
+
12
+ it('should return a string of asterisks if the input length is less than or equal to twice the visibleChars', () => {
13
+ expect(obscureCenter('test', 2)).toBe('****')
14
+ expect(obscureCenter('hi', 1)).toBe('**')
15
+ expect(obscureCenter('abcdef', 3)).toBe('******')
16
+ })
17
+
18
+ it('should return the same string if the visible chars is negative or 0', () => {
19
+ expect(obscureCenter('abcdef', -1)).toBe('abcdef')
20
+ expect(obscureCenter('abcdef', 0)).toBe('abcdef')
21
+ })
22
+ })
@@ -0,0 +1,25 @@
1
+ import {
2
+ Abi,
3
+ ContractFunctionName,
4
+ Hex,
5
+ prepareEncodeFunctionData,
6
+ PrepareEncodeFunctionDataParameters,
7
+ slice as vslice,
8
+ } from 'viem'
9
+
10
+ /**
11
+ * Gets the hex selector of the function.
12
+ * @param parameters the parameters to encode, abi and functionName
13
+ * @returns the hex selector of the function
14
+ */
15
+ export function getSelector<
16
+ const abi extends Abi | readonly unknown[],
17
+ functionName extends ContractFunctionName<abi>,
18
+ >(parameters: PrepareEncodeFunctionDataParameters<abi, functionName>): Hex {
19
+ return prepareEncodeFunctionData(parameters).functionName
20
+ }
21
+
22
+ // Get the first 4 bytes of the data that is the hash of the function signature
23
+ export function getFunctionBytes(data: Hex): Hex {
24
+ return vslice(data, 0, 4)
25
+ }
@@ -0,0 +1,115 @@
1
+ import { getRpcUrl, addressKeys, convertBigIntsToStrings } from '../utils'
2
+ import { Chain, InvalidAddressError } from 'viem'
3
+
4
+ describe('Viem Utils', () => {
5
+ describe('getRpcUrl', () => {
6
+ let mockChain: Chain
7
+
8
+ beforeEach(() => {
9
+ mockChain = {
10
+ rpcUrls: {
11
+ default: { http: ['https://default-url'], webSocket: ['wss://default-url'] },
12
+ secondary: { http: ['https://secondary-url'], webSocket: ['wss://secondary-url'] },
13
+ },
14
+ } as any
15
+ })
16
+
17
+ describe('when the chain has no secondary RPC URL', () => {
18
+ beforeEach(() => {
19
+ delete mockChain.rpcUrls.secondary
20
+ })
21
+
22
+ it('should return the default HTTP URL when websocket is disabled', () => {
23
+ const result = getRpcUrl(mockChain, undefined, false)
24
+ expect(result).toEqual({ url: mockChain.rpcUrls.default.http[0], isWebsocket: false })
25
+ })
26
+
27
+ it('should return the default WebSocket URL when websocket is enabled', () => {
28
+ const result = getRpcUrl(mockChain, undefined, true)
29
+ expect(result).toEqual({ url: mockChain.rpcUrls.default.webSocket![0], isWebsocket: true })
30
+ })
31
+ })
32
+
33
+ describe('when the chain has a secondary RPC URL', () => {
34
+ it('should return the secondary HTTP URL when websocket is disabled', () => {
35
+ const result = getRpcUrl(mockChain, undefined, false)
36
+ expect(result).toEqual({ url: mockChain.rpcUrls.secondary.http[0], isWebsocket: false })
37
+ })
38
+
39
+ it('should return the secondary WebSocket URL when websocket is enabled', () => {
40
+ const result = getRpcUrl(mockChain, undefined, true)
41
+ expect(result).toEqual({
42
+ url: mockChain.rpcUrls.secondary.webSocket![0],
43
+ isWebsocket: true,
44
+ })
45
+ })
46
+
47
+ it('should return the secondary HTTP URL when websocket is enabled but doesn`t exist', () => {
48
+ delete mockChain.rpcUrls.secondary.webSocket
49
+ const result = getRpcUrl(mockChain, undefined, true)
50
+ expect(result).toEqual({ url: mockChain.rpcUrls.secondary.http[0], isWebsocket: false })
51
+ })
52
+
53
+ it('should append the API key to the URL if provided', () => {
54
+ const apiKey = 'api-key'
55
+ const result = getRpcUrl(mockChain, apiKey, false)
56
+ expect(result).toEqual({
57
+ url: mockChain.rpcUrls.secondary.http[0] + `/${apiKey}`,
58
+ isWebsocket: false,
59
+ })
60
+ })
61
+ })
62
+ })
63
+
64
+ describe('addressKeys', () => {
65
+ const add = '0x6d9EedE368621F173E5c93384CFcCbfeE19f9609'
66
+ const unchecksumedAdd = '0x6d9eede368621f173e5c93384cfccbfee19f9609'
67
+ it('should return empty if the input is empty', () => {
68
+ const result = addressKeys({})
69
+ expect(result).toStrictEqual({})
70
+ })
71
+
72
+ it('should throw if a key isn`t a valid eth address', () => {
73
+ const invalidAddress = '0x123'
74
+ expect(() => addressKeys({ [add]: 11, [invalidAddress]: 22 })).toThrow(
75
+ new InvalidAddressError({ address: invalidAddress }),
76
+ )
77
+ })
78
+
79
+ it('should checksum all address keys in the top level of the object', () => {
80
+ const input = { [unchecksumedAdd]: 123 }
81
+ const result = addressKeys(input)
82
+ expect(result).toEqual({ [add]: 123 })
83
+ })
84
+ })
85
+
86
+ describe('convertBigIntsToStrings', () => {
87
+ it('should return null if the input is null', () => {
88
+ const result = convertBigIntsToStrings(null)
89
+ expect(result).toBeNull()
90
+ })
91
+
92
+ it('should return undefined if the input is undefined', () => {
93
+ const result = convertBigIntsToStrings(undefined)
94
+ expect(result).toBeUndefined()
95
+ })
96
+
97
+ it('should convert BigInt values to strings', () => {
98
+ const input = { a: BigInt(123), b: 456n }
99
+ const result = convertBigIntsToStrings(input)
100
+ expect(result).toEqual({ a: '123', b: '456' })
101
+ })
102
+
103
+ it('should handle nested objects with BigInt values', () => {
104
+ const input = { a: { b: BigInt(123) } }
105
+ const result = convertBigIntsToStrings(input)
106
+ expect(result).toEqual({ a: { b: '123' } })
107
+ })
108
+
109
+ it('should handle arrays with BigInt values', () => {
110
+ const input = [BigInt(123), 456n]
111
+ const result = convertBigIntsToStrings(input)
112
+ expect(result).toEqual(['123', '456'])
113
+ })
114
+ })
115
+ })
@@ -0,0 +1,78 @@
1
+ import { Chain, getAddress, Hex } from 'viem'
2
+
3
+ /**
4
+ * Gets the url for a chain with the given api key, either websocket or http. It tries
5
+ * to find a non default rpc url if it exists and returns the first one it finds,
6
+ * otherwise it returns the default one.
7
+ *
8
+ * @param chain the chain to get the url for
9
+ * @param apiKey the api key if it is required
10
+ * @param websocketEnabled whether to try the websocket url if there is one
11
+ * @returns
12
+ */
13
+ export function getRpcUrl(
14
+ chain: Chain,
15
+ apiKey?: string,
16
+ websocketEnabled: boolean = false,
17
+ ): { url: string; isWebsocket: boolean } {
18
+ let rpcUrl = chain.rpcUrls.default
19
+ for (const key in chain.rpcUrls) {
20
+ if (key === 'default') {
21
+ continue
22
+ }
23
+ rpcUrl = chain.rpcUrls[key]
24
+ break
25
+ }
26
+ const isWebsocket =
27
+ websocketEnabled && rpcUrl.webSocket != undefined && rpcUrl.webSocket.length > 0
28
+ const url = isWebsocket ? rpcUrl.webSocket![0] : rpcUrl.http[0]
29
+
30
+ return {
31
+ url: apiKey ? url + '/' + apiKey : url,
32
+ isWebsocket,
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Lowercase all top-level keys of the given `object` to lowercase.
38
+ *
39
+ * @returns {Object}
40
+ */
41
+ export function addressKeys(obj: Record<Hex, any>): Record<Hex, any> {
42
+ return Object.entries(obj).reduce((carry, [key, value]) => {
43
+ carry[getAddress(key)] = value
44
+ return carry
45
+ }, {})
46
+ }
47
+
48
+ /**
49
+ * Recursively converts all BigInt values in an object to strings.
50
+ *
51
+ * @param {Object} obj - The object to process.
52
+ * @returns {Object} - The new object with BigInt values as strings.
53
+ */
54
+ export function convertBigIntsToStrings(obj: any): any {
55
+ if (obj === null || obj === undefined) {
56
+ return obj
57
+ }
58
+
59
+ if (typeof obj === 'bigint') {
60
+ return obj.toString()
61
+ }
62
+
63
+ if (Array.isArray(obj)) {
64
+ return obj.map(convertBigIntsToStrings)
65
+ }
66
+
67
+ if (typeof obj === 'object') {
68
+ return Object.entries(obj).reduce(
69
+ (carry, [key, value]) => {
70
+ carry[key] = convertBigIntsToStrings(value)
71
+ return carry
72
+ },
73
+ {} as Record<string, any>,
74
+ )
75
+ }
76
+
77
+ return obj
78
+ }
@@ -0,0 +1,389 @@
1
+ import { ContractFunctionName, decodeEventLog, Hex } from 'viem'
2
+ import { getSelector } from '../common/viem/contracts'
3
+ import { TargetContractType } from '../eco-configs/eco-config.types'
4
+ import { EcoError } from '../common/errors/eco-error'
5
+ import { TransactionTargetData } from '@/intent/utils-intent.service'
6
+
7
+ // Need to define the ABI as a const array to use in the type definition
8
+ export const ERC20Abi = [
9
+ {
10
+ inputs: [
11
+ {
12
+ internalType: 'string',
13
+ name: 'name_',
14
+ type: 'string',
15
+ },
16
+ {
17
+ internalType: 'string',
18
+ name: 'symbol_',
19
+ type: 'string',
20
+ },
21
+ {
22
+ internalType: 'uint256',
23
+ name: 'amount',
24
+ type: 'uint256',
25
+ },
26
+ ],
27
+ stateMutability: 'nonpayable',
28
+ type: 'constructor',
29
+ },
30
+ {
31
+ inputs: [
32
+ {
33
+ internalType: 'address',
34
+ name: 'spender',
35
+ type: 'address',
36
+ },
37
+ {
38
+ internalType: 'uint256',
39
+ name: 'allowance',
40
+ type: 'uint256',
41
+ },
42
+ {
43
+ internalType: 'uint256',
44
+ name: 'needed',
45
+ type: 'uint256',
46
+ },
47
+ ],
48
+ name: 'ERC20InsufficientAllowance',
49
+ type: 'error',
50
+ },
51
+ {
52
+ inputs: [
53
+ {
54
+ internalType: 'address',
55
+ name: 'sender',
56
+ type: 'address',
57
+ },
58
+ {
59
+ internalType: 'uint256',
60
+ name: 'balance',
61
+ type: 'uint256',
62
+ },
63
+ {
64
+ internalType: 'uint256',
65
+ name: 'needed',
66
+ type: 'uint256',
67
+ },
68
+ ],
69
+ name: 'ERC20InsufficientBalance',
70
+ type: 'error',
71
+ },
72
+ {
73
+ inputs: [
74
+ {
75
+ internalType: 'address',
76
+ name: 'approver',
77
+ type: 'address',
78
+ },
79
+ ],
80
+ name: 'ERC20InvalidApprover',
81
+ type: 'error',
82
+ },
83
+ {
84
+ inputs: [
85
+ {
86
+ internalType: 'address',
87
+ name: 'receiver',
88
+ type: 'address',
89
+ },
90
+ ],
91
+ name: 'ERC20InvalidReceiver',
92
+ type: 'error',
93
+ },
94
+ {
95
+ inputs: [
96
+ {
97
+ internalType: 'address',
98
+ name: 'sender',
99
+ type: 'address',
100
+ },
101
+ ],
102
+ name: 'ERC20InvalidSender',
103
+ type: 'error',
104
+ },
105
+ {
106
+ inputs: [
107
+ {
108
+ internalType: 'address',
109
+ name: 'spender',
110
+ type: 'address',
111
+ },
112
+ ],
113
+ name: 'ERC20InvalidSpender',
114
+ type: 'error',
115
+ },
116
+ {
117
+ anonymous: false,
118
+ inputs: [
119
+ {
120
+ indexed: true,
121
+ internalType: 'address',
122
+ name: 'owner',
123
+ type: 'address',
124
+ },
125
+ {
126
+ indexed: true,
127
+ internalType: 'address',
128
+ name: 'spender',
129
+ type: 'address',
130
+ },
131
+ {
132
+ indexed: false,
133
+ internalType: 'uint256',
134
+ name: 'value',
135
+ type: 'uint256',
136
+ },
137
+ ],
138
+ name: 'Approval',
139
+ type: 'event',
140
+ },
141
+ {
142
+ anonymous: false,
143
+ inputs: [
144
+ {
145
+ indexed: true,
146
+ internalType: 'address',
147
+ name: 'from',
148
+ type: 'address',
149
+ },
150
+ {
151
+ indexed: true,
152
+ internalType: 'address',
153
+ name: 'to',
154
+ type: 'address',
155
+ },
156
+ {
157
+ indexed: false,
158
+ internalType: 'uint256',
159
+ name: 'value',
160
+ type: 'uint256',
161
+ },
162
+ ],
163
+ name: 'Transfer',
164
+ type: 'event',
165
+ },
166
+ {
167
+ inputs: [
168
+ {
169
+ internalType: 'address',
170
+ name: 'owner',
171
+ type: 'address',
172
+ },
173
+ {
174
+ internalType: 'address',
175
+ name: 'spender',
176
+ type: 'address',
177
+ },
178
+ ],
179
+ name: 'allowance',
180
+ outputs: [
181
+ {
182
+ internalType: 'uint256',
183
+ name: '',
184
+ type: 'uint256',
185
+ },
186
+ ],
187
+ stateMutability: 'view',
188
+ type: 'function',
189
+ },
190
+ {
191
+ inputs: [
192
+ {
193
+ internalType: 'address',
194
+ name: 'spender',
195
+ type: 'address',
196
+ },
197
+ {
198
+ internalType: 'uint256',
199
+ name: 'value',
200
+ type: 'uint256',
201
+ },
202
+ ],
203
+ name: 'approve',
204
+ outputs: [
205
+ {
206
+ internalType: 'bool',
207
+ name: '',
208
+ type: 'bool',
209
+ },
210
+ ],
211
+ stateMutability: 'nonpayable',
212
+ type: 'function',
213
+ },
214
+ {
215
+ inputs: [
216
+ {
217
+ internalType: 'address',
218
+ name: 'account',
219
+ type: 'address',
220
+ },
221
+ ],
222
+ name: 'balanceOf',
223
+ outputs: [
224
+ {
225
+ internalType: 'uint256',
226
+ name: '',
227
+ type: 'uint256',
228
+ },
229
+ ],
230
+ stateMutability: 'view',
231
+ type: 'function',
232
+ },
233
+ {
234
+ inputs: [],
235
+ name: 'decimals',
236
+ outputs: [
237
+ {
238
+ internalType: 'uint8',
239
+ name: '',
240
+ type: 'uint8',
241
+ },
242
+ ],
243
+ stateMutability: 'view',
244
+ type: 'function',
245
+ },
246
+ {
247
+ inputs: [],
248
+ name: 'name',
249
+ outputs: [
250
+ {
251
+ internalType: 'string',
252
+ name: '',
253
+ type: 'string',
254
+ },
255
+ ],
256
+ stateMutability: 'view',
257
+ type: 'function',
258
+ },
259
+ {
260
+ inputs: [],
261
+ name: 'symbol',
262
+ outputs: [
263
+ {
264
+ internalType: 'string',
265
+ name: '',
266
+ type: 'string',
267
+ },
268
+ ],
269
+ stateMutability: 'view',
270
+ type: 'function',
271
+ },
272
+ {
273
+ inputs: [],
274
+ name: 'totalSupply',
275
+ outputs: [
276
+ {
277
+ internalType: 'uint256',
278
+ name: '',
279
+ type: 'uint256',
280
+ },
281
+ ],
282
+ stateMutability: 'view',
283
+ type: 'function',
284
+ },
285
+ {
286
+ inputs: [
287
+ {
288
+ internalType: 'address',
289
+ name: 'to',
290
+ type: 'address',
291
+ },
292
+ {
293
+ internalType: 'uint256',
294
+ name: 'value',
295
+ type: 'uint256',
296
+ },
297
+ ],
298
+ name: 'transfer',
299
+ outputs: [
300
+ {
301
+ internalType: 'bool',
302
+ name: '',
303
+ type: 'bool',
304
+ },
305
+ ],
306
+ stateMutability: 'nonpayable',
307
+ type: 'function',
308
+ },
309
+ {
310
+ inputs: [
311
+ {
312
+ internalType: 'address',
313
+ name: 'from',
314
+ type: 'address',
315
+ },
316
+ {
317
+ internalType: 'address',
318
+ name: 'to',
319
+ type: 'address',
320
+ },
321
+ {
322
+ internalType: 'uint256',
323
+ name: 'value',
324
+ type: 'uint256',
325
+ },
326
+ ],
327
+ name: 'transferFrom',
328
+ outputs: [
329
+ {
330
+ internalType: 'bool',
331
+ name: '',
332
+ type: 'bool',
333
+ },
334
+ ],
335
+ stateMutability: 'nonpayable',
336
+ type: 'function',
337
+ },
338
+ ] as const
339
+
340
+ // The ERC20 contract function selectors
341
+ export function getERC20Selector<functionName extends ContractFunctionName<typeof ERC20Abi>>(
342
+ functionName: functionName,
343
+ ) {
344
+ return getSelector({ abi: ERC20Abi, functionName })
345
+ }
346
+
347
+ export function decodeTransferLog(data: Hex, topics: [signature: Hex, ...args: Hex[]] | []) {
348
+ return decodeEventLog({
349
+ abi: ERC20Abi,
350
+ eventName: 'Transfer',
351
+ topics,
352
+ data,
353
+ })
354
+ }
355
+
356
+ export function isSupportedTokenType(targetType: TargetContractType): boolean {
357
+ switch (targetType) {
358
+ case 'erc20':
359
+ return true
360
+ case 'erc721':
361
+ case 'erc1155':
362
+ default:
363
+ throw EcoError.IntentSourceUnsupportedTargetType(targetType)
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Verifies that a target is of type erc20 and that the selector is supported
369
+ * @param ttd the transaction target data
370
+ * @param permittedSelector the selector to check against, if not provided it will check against all erc20 selectors
371
+ * @returns
372
+ */
373
+ export function isERC20Target(ttd: TransactionTargetData | null, permittedSelector?: Hex): boolean {
374
+ if (!ttd) {
375
+ return false
376
+ }
377
+ const isERC20 = ttd.targetConfig.contractType === 'erc20'
378
+ if (permittedSelector && ttd.selector !== permittedSelector) {
379
+ return false
380
+ }
381
+ switch (ttd.selector) {
382
+ case getERC20Selector('transfer'):
383
+ const correctArgs =
384
+ !!ttd.decodedFunctionData.args && ttd.decodedFunctionData.args.length === 2
385
+ return isERC20 && correctArgs
386
+ default:
387
+ return false
388
+ }
389
+ }