eco-solver 0.0.1-security → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of eco-solver might be problematic. Click here for more details.
- package/.eslintignore +8 -0
- package/.eslintrc.js +24 -0
- package/.github/workflows/ci.yaml +38 -0
- package/.nvmrc +1 -0
- package/.prettierignore +3 -0
- package/.prettierrc +8 -0
- package/Dockerfile +11 -0
- package/LICENSE +21 -0
- package/README.md +29 -5
- package/config/default.ts +135 -0
- package/config/development.ts +95 -0
- package/config/preproduction.ts +17 -0
- package/config/production.ts +17 -0
- package/config/staging.ts +17 -0
- package/config/test.ts +7 -0
- package/index.js +43 -0
- package/jest.config.ts +14 -0
- package/nest-cli.json +8 -0
- package/package.json +115 -6
- package/src/api/api.module.ts +27 -0
- package/src/api/balance.controller.ts +41 -0
- package/src/api/quote.controller.ts +54 -0
- package/src/api/tests/balance.controller.spec.ts +113 -0
- package/src/api/tests/quote.controller.spec.ts +83 -0
- package/src/app.module.ts +74 -0
- package/src/balance/balance.module.ts +14 -0
- package/src/balance/balance.service.ts +230 -0
- package/src/balance/balance.ws.service.ts +104 -0
- package/src/balance/types.ts +16 -0
- package/src/bullmq/bullmq.helper.ts +41 -0
- package/src/bullmq/processors/eth-ws.processor.ts +47 -0
- package/src/bullmq/processors/inbox.processor.ts +55 -0
- package/src/bullmq/processors/interval.processor.ts +54 -0
- package/src/bullmq/processors/processor.module.ts +14 -0
- package/src/bullmq/processors/signer.processor.ts +41 -0
- package/src/bullmq/processors/solve-intent.processor.ts +73 -0
- package/src/bullmq/processors/tests/solve-intent.processor.spec.ts +3 -0
- package/src/bullmq/utils/queue.ts +22 -0
- package/src/chain-monitor/chain-monitor.module.ts +12 -0
- package/src/chain-monitor/chain-sync.service.ts +134 -0
- package/src/chain-monitor/tests/chain-sync.service.spec.ts +190 -0
- package/src/commander/.eslintrc.js +6 -0
- package/src/commander/balance/balance-command.module.ts +12 -0
- package/src/commander/balance/balance.command.ts +73 -0
- package/src/commander/command-main.ts +15 -0
- package/src/commander/commander-app.module.ts +31 -0
- package/src/commander/eco-config.command.ts +20 -0
- package/src/commander/safe/safe-command.module.ts +11 -0
- package/src/commander/safe/safe.command.ts +70 -0
- package/src/commander/transfer/client.command.ts +24 -0
- package/src/commander/transfer/transfer-command.module.ts +26 -0
- package/src/commander/transfer/transfer.command.ts +138 -0
- package/src/commander/utils.ts +8 -0
- package/src/common/chains/definitions/arbitrum.ts +12 -0
- package/src/common/chains/definitions/base.ts +21 -0
- package/src/common/chains/definitions/eco.ts +54 -0
- package/src/common/chains/definitions/ethereum.ts +22 -0
- package/src/common/chains/definitions/helix.ts +53 -0
- package/src/common/chains/definitions/mantle.ts +12 -0
- package/src/common/chains/definitions/optimism.ts +22 -0
- package/src/common/chains/definitions/polygon.ts +12 -0
- package/src/common/chains/supported.ts +26 -0
- package/src/common/chains/transport.ts +19 -0
- package/src/common/errors/eco-error.ts +155 -0
- package/src/common/events/constants.ts +3 -0
- package/src/common/events/viem.ts +22 -0
- package/src/common/logging/eco-log-message.ts +74 -0
- package/src/common/redis/constants.ts +55 -0
- package/src/common/redis/redis-connection-utils.ts +106 -0
- package/src/common/routes/constants.ts +3 -0
- package/src/common/utils/objects.ts +34 -0
- package/src/common/utils/strings.ts +49 -0
- package/src/common/utils/tests/objects.spec.ts +23 -0
- package/src/common/utils/tests/strings.spec.ts +22 -0
- package/src/common/viem/contracts.ts +25 -0
- package/src/common/viem/tests/utils.spec.ts +115 -0
- package/src/common/viem/utils.ts +78 -0
- package/src/contracts/ERC20.contract.ts +389 -0
- package/src/contracts/EntryPoint.V6.contract.ts +1309 -0
- package/src/contracts/KernelAccount.abi.ts +87 -0
- package/src/contracts/OwnableExecutor.abi.ts +128 -0
- package/src/contracts/SimpleAccount.contract.ts +524 -0
- package/src/contracts/inbox.ts +8 -0
- package/src/contracts/index.ts +9 -0
- package/src/contracts/intent-source.ts +55 -0
- package/src/contracts/interfaces/index.ts +1 -0
- package/src/contracts/interfaces/prover.interface.ts +22 -0
- package/src/contracts/prover.ts +9 -0
- package/src/contracts/tests/erc20.contract.spec.ts +59 -0
- package/src/contracts/utils.ts +31 -0
- package/src/decoder/decoder.interface.ts +3 -0
- package/src/decoder/tests/utils.spec.ts +36 -0
- package/src/decoder/utils.ts +24 -0
- package/src/decorators/cacheable.decorator.ts +48 -0
- package/src/eco-configs/aws-config.service.ts +75 -0
- package/src/eco-configs/eco-config.module.ts +44 -0
- package/src/eco-configs/eco-config.service.ts +220 -0
- package/src/eco-configs/eco-config.types.ts +278 -0
- package/src/eco-configs/interfaces/config-source.interface.ts +3 -0
- package/src/eco-configs/tests/aws-config.service.spec.ts +52 -0
- package/src/eco-configs/tests/eco-config.service.spec.ts +137 -0
- package/src/eco-configs/tests/utils.spec.ts +84 -0
- package/src/eco-configs/utils.ts +49 -0
- package/src/fee/fee.module.ts +10 -0
- package/src/fee/fee.service.ts +467 -0
- package/src/fee/tests/fee.service.spec.ts +909 -0
- package/src/fee/tests/utils.spec.ts +49 -0
- package/src/fee/types.ts +44 -0
- package/src/fee/utils.ts +23 -0
- package/src/flags/flags.module.ts +10 -0
- package/src/flags/flags.service.ts +112 -0
- package/src/flags/tests/flags.service.spec.ts +68 -0
- package/src/flags/utils.ts +22 -0
- package/src/health/constants.ts +1 -0
- package/src/health/health.controller.ts +23 -0
- package/src/health/health.module.ts +25 -0
- package/src/health/health.service.ts +40 -0
- package/src/health/indicators/balance.indicator.ts +196 -0
- package/src/health/indicators/eco-redis.indicator.ts +23 -0
- package/src/health/indicators/git-commit.indicator.ts +67 -0
- package/src/health/indicators/mongodb.indicator.ts +11 -0
- package/src/health/indicators/permission.indicator.ts +64 -0
- package/src/intent/create-intent.service.ts +129 -0
- package/src/intent/feasable-intent.service.ts +80 -0
- package/src/intent/fulfill-intent.service.ts +318 -0
- package/src/intent/intent.controller.ts +199 -0
- package/src/intent/intent.module.ts +49 -0
- package/src/intent/schemas/intent-call-data.schema.ts +16 -0
- package/src/intent/schemas/intent-data.schema.ts +114 -0
- package/src/intent/schemas/intent-source.schema.ts +33 -0
- package/src/intent/schemas/intent-token-amount.schema.ts +14 -0
- package/src/intent/schemas/reward-data.schema.ts +48 -0
- package/src/intent/schemas/route-data.schema.ts +52 -0
- package/src/intent/schemas/watch-event.schema.ts +32 -0
- package/src/intent/tests/create-intent.service.spec.ts +215 -0
- package/src/intent/tests/feasable-intent.service.spec.ts +155 -0
- package/src/intent/tests/fulfill-intent.service.spec.ts +564 -0
- package/src/intent/tests/utils-intent.service.spec.ts +308 -0
- package/src/intent/tests/utils.spec.ts +62 -0
- package/src/intent/tests/validate-intent.service.spec.ts +297 -0
- package/src/intent/tests/validation.service.spec.ts +337 -0
- package/src/intent/utils-intent.service.ts +168 -0
- package/src/intent/utils.ts +37 -0
- package/src/intent/validate-intent.service.ts +176 -0
- package/src/intent/validation.sevice.ts +223 -0
- package/src/interceptors/big-int.interceptor.ts +30 -0
- package/src/intervals/interval.module.ts +18 -0
- package/src/intervals/retry-infeasable-intents.service.ts +89 -0
- package/src/intervals/tests/retry-infeasable-intents.service.spec.ts +167 -0
- package/src/kms/errors.ts +0 -0
- package/src/kms/kms.module.ts +12 -0
- package/src/kms/kms.service.ts +65 -0
- package/src/kms/tests/kms.service.spec.ts +60 -0
- package/src/liquidity-manager/jobs/check-balances-cron.job.ts +229 -0
- package/src/liquidity-manager/jobs/liquidity-manager.job.ts +52 -0
- package/src/liquidity-manager/jobs/rebalance.job.ts +61 -0
- package/src/liquidity-manager/liquidity-manager.module.ts +29 -0
- package/src/liquidity-manager/processors/base.processor.ts +117 -0
- package/src/liquidity-manager/processors/eco-protocol-intents.processor.ts +34 -0
- package/src/liquidity-manager/processors/grouped-jobs.processor.ts +103 -0
- package/src/liquidity-manager/queues/liquidity-manager.queue.ts +48 -0
- package/src/liquidity-manager/schemas/rebalance-token.schema.ts +32 -0
- package/src/liquidity-manager/schemas/rebalance.schema.ts +32 -0
- package/src/liquidity-manager/services/liquidity-manager.service.ts +188 -0
- package/src/liquidity-manager/services/liquidity-provider.service.ts +25 -0
- package/src/liquidity-manager/services/liquidity-providers/LiFi/lifi-provider.service.spec.ts +125 -0
- package/src/liquidity-manager/services/liquidity-providers/LiFi/lifi-provider.service.ts +117 -0
- package/src/liquidity-manager/services/liquidity-providers/LiFi/utils/get-transaction-hashes.ts +16 -0
- package/src/liquidity-manager/tests/liquidity-manager.service.spec.ts +142 -0
- package/src/liquidity-manager/types/token-state.enum.ts +5 -0
- package/src/liquidity-manager/types/types.d.ts +52 -0
- package/src/liquidity-manager/utils/address.ts +5 -0
- package/src/liquidity-manager/utils/math.ts +9 -0
- package/src/liquidity-manager/utils/serialize.spec.ts +24 -0
- package/src/liquidity-manager/utils/serialize.ts +47 -0
- package/src/liquidity-manager/utils/token.ts +91 -0
- package/src/main.ts +63 -0
- package/src/nest-redlock/nest-redlock.config.ts +14 -0
- package/src/nest-redlock/nest-redlock.interface.ts +5 -0
- package/src/nest-redlock/nest-redlock.module.ts +64 -0
- package/src/nest-redlock/nest-redlock.service.ts +59 -0
- package/src/prover/proof.service.ts +184 -0
- package/src/prover/prover.module.ts +10 -0
- package/src/prover/tests/proof.service.spec.ts +154 -0
- package/src/quote/dto/quote.intent.data.dto.ts +35 -0
- package/src/quote/dto/quote.reward.data.dto.ts +67 -0
- package/src/quote/dto/quote.route.data.dto.ts +71 -0
- package/src/quote/dto/types.ts +18 -0
- package/src/quote/errors.ts +215 -0
- package/src/quote/quote.module.ts +17 -0
- package/src/quote/quote.service.ts +299 -0
- package/src/quote/schemas/quote-call.schema.ts +16 -0
- package/src/quote/schemas/quote-intent.schema.ts +27 -0
- package/src/quote/schemas/quote-reward.schema.ts +24 -0
- package/src/quote/schemas/quote-route.schema.ts +30 -0
- package/src/quote/schemas/quote-token.schema.ts +14 -0
- package/src/quote/tests/quote.service.spec.ts +444 -0
- package/src/sign/atomic-signer.service.ts +24 -0
- package/src/sign/atomic.nonce.service.ts +114 -0
- package/src/sign/kms-account/kmsToAccount.ts +73 -0
- package/src/sign/kms-account/signKms.ts +30 -0
- package/src/sign/kms-account/signKmsTransaction.ts +37 -0
- package/src/sign/kms-account/signKmsTypedData.ts +21 -0
- package/src/sign/nonce.service.ts +89 -0
- package/src/sign/schemas/nonce.schema.ts +36 -0
- package/src/sign/sign.controller.ts +52 -0
- package/src/sign/sign.helper.ts +23 -0
- package/src/sign/sign.module.ts +27 -0
- package/src/sign/signer-kms.service.ts +27 -0
- package/src/sign/signer.service.ts +26 -0
- package/src/solver/filters/tests/valid-smart-wallet.service.spec.ts +87 -0
- package/src/solver/filters/valid-smart-wallet.service.ts +58 -0
- package/src/solver/solver.module.ts +10 -0
- package/src/transaction/multichain-public-client.service.ts +15 -0
- package/src/transaction/smart-wallets/kernel/actions/encodeData.kernel.ts +57 -0
- package/src/transaction/smart-wallets/kernel/create-kernel-client-v2.account.ts +183 -0
- package/src/transaction/smart-wallets/kernel/create.kernel.account.ts +270 -0
- package/src/transaction/smart-wallets/kernel/index.ts +2 -0
- package/src/transaction/smart-wallets/kernel/kernel-account-client-v2.service.ts +90 -0
- package/src/transaction/smart-wallets/kernel/kernel-account-client.service.ts +107 -0
- package/src/transaction/smart-wallets/kernel/kernel-account.client.ts +105 -0
- package/src/transaction/smart-wallets/kernel/kernel-account.config.ts +34 -0
- package/src/transaction/smart-wallets/simple-account/create.simple.account.ts +19 -0
- package/src/transaction/smart-wallets/simple-account/index.ts +2 -0
- package/src/transaction/smart-wallets/simple-account/simple-account-client.service.ts +42 -0
- package/src/transaction/smart-wallets/simple-account/simple-account.client.ts +83 -0
- package/src/transaction/smart-wallets/simple-account/simple-account.config.ts +5 -0
- package/src/transaction/smart-wallets/smart-wallet.types.ts +38 -0
- package/src/transaction/smart-wallets/utils.ts +14 -0
- package/src/transaction/transaction.module.ts +25 -0
- package/src/transaction/viem_multichain_client.service.ts +100 -0
- package/src/transforms/viem-address.decorator.ts +14 -0
- package/src/utils/bigint.ts +44 -0
- package/src/utils/types.ts +18 -0
- package/src/watch/intent/tests/watch-create-intent.service.spec.ts +257 -0
- package/src/watch/intent/tests/watch-fulfillment.service.spec.ts +141 -0
- package/src/watch/intent/watch-create-intent.service.ts +106 -0
- package/src/watch/intent/watch-event.service.ts +133 -0
- package/src/watch/intent/watch-fulfillment.service.ts +115 -0
- package/src/watch/watch.module.ts +13 -0
- package/test/app.e2e-spec.ts +21 -0
- package/test/jest-e2e.json +9 -0
- package/tsconfig.build.json +4 -0
- 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
|
+
}
|