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.
- 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 +66 -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,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
|
+
}
|