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
package/src/main.ts
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
import { NestFactory } from '@nestjs/core'
|
2
|
+
import { AppModule } from './app.module'
|
3
|
+
import { NestExpressApplication } from '@nestjs/platform-express'
|
4
|
+
import { EcoConfigService } from './eco-configs/eco-config.service'
|
5
|
+
import { Logger, LoggerErrorInterceptor } from 'nestjs-pino'
|
6
|
+
import { NestApplicationOptions, ValidationPipe } from '@nestjs/common'
|
7
|
+
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'
|
8
|
+
import { BigIntToStringInterceptor } from '@/interceptors/big-int.interceptor'
|
9
|
+
|
10
|
+
async function bootstrap() {
|
11
|
+
const app = await NestFactory.create<NestExpressApplication>(AppModule, getNestParams())
|
12
|
+
if (EcoConfigService.getStaticConfig().logger.usePino) {
|
13
|
+
app.useLogger(app.get(Logger))
|
14
|
+
app.useGlobalInterceptors(new LoggerErrorInterceptor())
|
15
|
+
}
|
16
|
+
|
17
|
+
//add dto validations, enable transformation
|
18
|
+
app.useGlobalPipes(
|
19
|
+
new ValidationPipe({
|
20
|
+
transform: true, // Enables DTO transformation for incoming requests
|
21
|
+
}),
|
22
|
+
)
|
23
|
+
|
24
|
+
//change all bigints to strings in the controller responses
|
25
|
+
app.useGlobalInterceptors(new BigIntToStringInterceptor())
|
26
|
+
|
27
|
+
//add swagger
|
28
|
+
addSwagger(app)
|
29
|
+
|
30
|
+
// Starts listening for shutdown hooks
|
31
|
+
app.enableShutdownHooks()
|
32
|
+
await app.listen(3000)
|
33
|
+
}
|
34
|
+
|
35
|
+
function getNestParams(): NestApplicationOptions {
|
36
|
+
let params = {
|
37
|
+
cors: true,
|
38
|
+
rawBody: true, //needed for AlchemyAuthMiddleware webhook verification
|
39
|
+
}
|
40
|
+
if (EcoConfigService.getStaticConfig().logger.usePino) {
|
41
|
+
params = {
|
42
|
+
...params,
|
43
|
+
...{
|
44
|
+
bufferLogs: true,
|
45
|
+
},
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
return params
|
50
|
+
}
|
51
|
+
|
52
|
+
function addSwagger(app: NestExpressApplication) {
|
53
|
+
const config = new DocumentBuilder()
|
54
|
+
.setTitle('Solver API')
|
55
|
+
.setDescription('The api for the solver queries')
|
56
|
+
.setVersion('0.1')
|
57
|
+
.addTag('solver')
|
58
|
+
.build()
|
59
|
+
const documentFactory = () => SwaggerModule.createDocument(app, config)
|
60
|
+
SwaggerModule.setup('api', app, documentFactory)
|
61
|
+
}
|
62
|
+
|
63
|
+
bootstrap()
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import { ModuleMetadata, Type } from '@nestjs/common'
|
2
|
+
import { NestRedlockConfigFactory } from './nest-redlock.interface'
|
3
|
+
import { RedisConfig } from '../eco-configs/eco-config.types'
|
4
|
+
|
5
|
+
export interface NestRedlockConfig extends RedisConfig {}
|
6
|
+
|
7
|
+
export interface NestRedlockDynamicConfig extends Pick<ModuleMetadata, 'imports'> {
|
8
|
+
useFactory?: (...args: any[]) => Promise<NestRedlockConfig> | NestRedlockConfig
|
9
|
+
useClass?: Type<NestRedlockConfigFactory>
|
10
|
+
useExisting?: Type<NestRedlockConfigFactory>
|
11
|
+
inject?: any[]
|
12
|
+
}
|
13
|
+
|
14
|
+
export const NEST_REDLOCK_CONFIG = 'NEST_REDLOCK_CONFIG'
|
@@ -0,0 +1,64 @@
|
|
1
|
+
import { DynamicModule, Global, Module, Provider } from '@nestjs/common'
|
2
|
+
import { NestRedlockConfig, NestRedlockDynamicConfig } from './nest-redlock.config'
|
3
|
+
import { NEST_REDLOCK_CONFIG } from './nest-redlock.config'
|
4
|
+
import { NestRedlockConfigFactory } from './nest-redlock.interface'
|
5
|
+
import { RedlockService } from './nest-redlock.service'
|
6
|
+
|
7
|
+
@Global()
|
8
|
+
@Module({
|
9
|
+
imports: [],
|
10
|
+
providers: [RedlockService],
|
11
|
+
exports: [RedlockService],
|
12
|
+
})
|
13
|
+
export class RedlockModule {
|
14
|
+
static forRoot(config: NestRedlockConfig): DynamicModule {
|
15
|
+
return {
|
16
|
+
module: RedlockModule,
|
17
|
+
imports: [],
|
18
|
+
providers: [
|
19
|
+
{
|
20
|
+
provide: NEST_REDLOCK_CONFIG,
|
21
|
+
useValue: config,
|
22
|
+
},
|
23
|
+
],
|
24
|
+
exports: [],
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
static forRootAsync(dynamicConfig: NestRedlockDynamicConfig): DynamicModule {
|
29
|
+
const providers = this.createAsyncProviders(dynamicConfig)
|
30
|
+
|
31
|
+
return {
|
32
|
+
module: RedlockModule,
|
33
|
+
imports: dynamicConfig.imports || [],
|
34
|
+
providers,
|
35
|
+
exports: [],
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
static createAsyncProviders(dynamicConfig: NestRedlockDynamicConfig): Provider[] {
|
40
|
+
if (dynamicConfig.useFactory) {
|
41
|
+
return [
|
42
|
+
{
|
43
|
+
provide: NEST_REDLOCK_CONFIG,
|
44
|
+
useFactory: dynamicConfig.useFactory,
|
45
|
+
inject: dynamicConfig.inject,
|
46
|
+
},
|
47
|
+
]
|
48
|
+
}
|
49
|
+
|
50
|
+
if (dynamicConfig.useClass || dynamicConfig.useExisting) {
|
51
|
+
return [
|
52
|
+
{
|
53
|
+
provide: NEST_REDLOCK_CONFIG,
|
54
|
+
useFactory: async (configFactory: NestRedlockConfigFactory) => {
|
55
|
+
return await configFactory.createNestRedlockConfig()
|
56
|
+
},
|
57
|
+
inject: [dynamicConfig.useClass || dynamicConfig.useExisting!],
|
58
|
+
},
|
59
|
+
]
|
60
|
+
}
|
61
|
+
|
62
|
+
return []
|
63
|
+
}
|
64
|
+
}
|
@@ -0,0 +1,59 @@
|
|
1
|
+
import { Inject, Injectable } from '@nestjs/common'
|
2
|
+
import Redlock, { Settings } from 'redlock'
|
3
|
+
import { Redis as IORedisClient, Cluster as IORedisCluster } from 'ioredis'
|
4
|
+
import { NestRedlockConfig, NEST_REDLOCK_CONFIG } from './nest-redlock.config'
|
5
|
+
import { RedisConnectionUtils } from '../common/redis/redis-connection-utils'
|
6
|
+
import { Lock } from 'redlock'
|
7
|
+
|
8
|
+
export type RedlockRedisClient = IORedisClient | IORedisCluster
|
9
|
+
|
10
|
+
@Injectable()
|
11
|
+
export class RedlockService extends Redlock {
|
12
|
+
constructor(@Inject(NEST_REDLOCK_CONFIG) redlockConfig: NestRedlockConfig) {
|
13
|
+
const { redlockSettings } = redlockConfig
|
14
|
+
const redisClients = RedisConnectionUtils.getClientsForRedlock(redlockConfig)
|
15
|
+
|
16
|
+
super(redisClients, redlockSettings)
|
17
|
+
}
|
18
|
+
|
19
|
+
/**
|
20
|
+
* Executes the callback if the lock key is required, otherwise it throws an error
|
21
|
+
* @param key the key to lock on
|
22
|
+
* @param callback the callback to execute
|
23
|
+
* @returns
|
24
|
+
*/
|
25
|
+
async lockCall(key: string, callback: () => Promise<any>): Promise<any> {
|
26
|
+
const lock = await this.acquireLock([key], 5000)
|
27
|
+
if (!lock) {
|
28
|
+
throw new Error('Could not acquire lock')
|
29
|
+
}
|
30
|
+
|
31
|
+
try {
|
32
|
+
return await callback()
|
33
|
+
} catch (e) {
|
34
|
+
throw e
|
35
|
+
} finally {
|
36
|
+
await lock.release()
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
/**
|
41
|
+
* Non-throwing lock aquire that returns null if the lock is not available
|
42
|
+
*
|
43
|
+
* @param resources
|
44
|
+
* @param duration time in ms
|
45
|
+
* @param settings
|
46
|
+
* @returns
|
47
|
+
*/
|
48
|
+
async acquireLock(
|
49
|
+
resources: string[],
|
50
|
+
duration: number,
|
51
|
+
settings?: Partial<Settings>,
|
52
|
+
): Promise<Lock | null> {
|
53
|
+
try {
|
54
|
+
return await this.acquire(resources, duration, settings)
|
55
|
+
} catch {
|
56
|
+
return null
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
@@ -0,0 +1,184 @@
|
|
1
|
+
import { Injectable, Logger, OnModuleInit } from '@nestjs/common'
|
2
|
+
import { addSeconds, compareAsc } from 'date-fns'
|
3
|
+
import { MultichainPublicClientService } from '../transaction/multichain-public-client.service'
|
4
|
+
import { Hex } from 'viem'
|
5
|
+
import {
|
6
|
+
PROOF_HYPERLANE,
|
7
|
+
PROOF_STORAGE,
|
8
|
+
ProofCall,
|
9
|
+
ProofType,
|
10
|
+
ProverInterfaceAbi,
|
11
|
+
} from '../contracts'
|
12
|
+
import { entries } from 'lodash'
|
13
|
+
import { EcoConfigService } from '../eco-configs/eco-config.service'
|
14
|
+
import { EcoLogMessage } from '../common/logging/eco-log-message'
|
15
|
+
|
16
|
+
/**
|
17
|
+
* Service class for getting information about the provers and their configurations.
|
18
|
+
*/
|
19
|
+
@Injectable()
|
20
|
+
export class ProofService implements OnModuleInit {
|
21
|
+
private logger = new Logger(ProofService.name)
|
22
|
+
|
23
|
+
/**
|
24
|
+
* Variable storing the proof type for each prover address. Used to determine
|
25
|
+
* what function to call on the Inbox contract
|
26
|
+
*/
|
27
|
+
private proofContracts: Record<Hex, ProofType> = {}
|
28
|
+
|
29
|
+
constructor(
|
30
|
+
private readonly publicClient: MultichainPublicClientService,
|
31
|
+
private readonly ecoConfigService: EcoConfigService,
|
32
|
+
) {}
|
33
|
+
|
34
|
+
async onModuleInit() {
|
35
|
+
await this.loadProofTypes()
|
36
|
+
}
|
37
|
+
|
38
|
+
/**
|
39
|
+
* Returns the proof type for a given prover address
|
40
|
+
*
|
41
|
+
* @param proverAddress
|
42
|
+
* @returns the proof type, defaults to {@link PROOF_STORAGE}
|
43
|
+
*/
|
44
|
+
getProofType(proverAddress: Hex): ProofType {
|
45
|
+
return this.proofContracts[proverAddress]
|
46
|
+
}
|
47
|
+
|
48
|
+
/**
|
49
|
+
* Checks if the prover is a hyperlane prover
|
50
|
+
* @param proverAddress the prover address
|
51
|
+
* @returns
|
52
|
+
*/
|
53
|
+
isHyperlaneProver(proverAddress: Hex): boolean {
|
54
|
+
return this.getProofType(proverAddress) === PROOF_HYPERLANE
|
55
|
+
}
|
56
|
+
|
57
|
+
/**
|
58
|
+
* Checks if the prover is a storage prover
|
59
|
+
* @param proverAddress the prover address
|
60
|
+
* @returns
|
61
|
+
*/
|
62
|
+
isStorageProver(proverAddress: Hex): boolean {
|
63
|
+
return this.getProofType(proverAddress) === PROOF_STORAGE
|
64
|
+
}
|
65
|
+
|
66
|
+
/**
|
67
|
+
* Returns all the prover addresses for a given proof type
|
68
|
+
* @param proofType the proof type
|
69
|
+
* @returns
|
70
|
+
*/
|
71
|
+
getProvers(proofType: ProofType): Hex[] {
|
72
|
+
return entries(this.proofContracts)
|
73
|
+
.filter(([, type]) => type === proofType)
|
74
|
+
.map(([address]) => address as Hex)
|
75
|
+
}
|
76
|
+
|
77
|
+
/**
|
78
|
+
* Returns the prover type for a given prover address
|
79
|
+
* @param prover the prover address
|
80
|
+
* @returns
|
81
|
+
*/
|
82
|
+
getProverType(prover: Hex): ProofType {
|
83
|
+
return this.proofContracts[prover]
|
84
|
+
}
|
85
|
+
|
86
|
+
/**
|
87
|
+
* Loads the proof types for each prover address into memory.
|
88
|
+
* Assume all provers must have the same proof type if their
|
89
|
+
* hex address is the same.
|
90
|
+
*/
|
91
|
+
private async loadProofTypes() {
|
92
|
+
const proofPromises = this.ecoConfigService.getIntentSources().map(async (source) => {
|
93
|
+
return await this.getProofTypes(source.chainID, source.provers)
|
94
|
+
})
|
95
|
+
|
96
|
+
// get the proof types for each prover address from on chain
|
97
|
+
const proofs = await Promise.all(proofPromises)
|
98
|
+
|
99
|
+
// reduce the array of proof objects into a single object, removing duplicates
|
100
|
+
proofs.reduce((acc, proof) => {
|
101
|
+
entries(proof).forEach(([proverAddress, proofType]) => {
|
102
|
+
acc[proverAddress] = proofType
|
103
|
+
})
|
104
|
+
return acc
|
105
|
+
}, this.proofContracts)
|
106
|
+
|
107
|
+
this.logger.debug(
|
108
|
+
EcoLogMessage.fromDefault({
|
109
|
+
message: `loadProofTypes loaded all the proof types`,
|
110
|
+
properties: {
|
111
|
+
proofs: this.proofContracts,
|
112
|
+
},
|
113
|
+
}),
|
114
|
+
)
|
115
|
+
}
|
116
|
+
|
117
|
+
/**
|
118
|
+
* Fetches all the proof types for the provers on a given chain using {@link ViemMultichainClientService#multicall}
|
119
|
+
*
|
120
|
+
* @param chainID the chain id
|
121
|
+
* @param provers the prover addresses
|
122
|
+
* @returns
|
123
|
+
*/
|
124
|
+
private async getProofTypes(chainID: number, provers: Hex[]): Promise<Record<Hex, ProofType>> {
|
125
|
+
const client = await this.publicClient.getClient(Number(chainID))
|
126
|
+
const proofCalls: ProofCall[] = provers.map((proverAddress) => {
|
127
|
+
return {
|
128
|
+
address: proverAddress,
|
129
|
+
abi: ProverInterfaceAbi,
|
130
|
+
functionName: 'getProofType',
|
131
|
+
}
|
132
|
+
})
|
133
|
+
|
134
|
+
const proofs = (await client.multicall({
|
135
|
+
contracts: proofCalls.flat(),
|
136
|
+
})) as any
|
137
|
+
let proof: ProofType = 0,
|
138
|
+
i = 0
|
139
|
+
const proofObj: Record<Hex, ProofType> = {}
|
140
|
+
while (proofs.length > 0 && ([{ result: proof }] = [proofs.shift()])) {
|
141
|
+
proofObj[provers[i]] = proof
|
142
|
+
i++
|
143
|
+
}
|
144
|
+
|
145
|
+
return proofObj
|
146
|
+
}
|
147
|
+
|
148
|
+
/**
|
149
|
+
* Check to see if the expiration of an intent is after the minimum proof time from now.
|
150
|
+
*
|
151
|
+
* @param prover the address of the prover
|
152
|
+
* @param expirationDate the expiration date
|
153
|
+
* @returns true if the intent can be proven before the minimum proof time, false otherwise
|
154
|
+
*/
|
155
|
+
isIntentExpirationWithinProofMinimumDate(prover: Hex, expirationDate: Date): boolean {
|
156
|
+
return compareAsc(expirationDate, this.getProofMinimumDate(this.proofContracts[prover])) == 1
|
157
|
+
}
|
158
|
+
|
159
|
+
/**
|
160
|
+
* Gets the minimum date that a proof can be generated for a given chain id.
|
161
|
+
* @param chainID the chain id
|
162
|
+
* @returns
|
163
|
+
*/
|
164
|
+
getProofMinimumDate(prover: ProofType): Date {
|
165
|
+
return addSeconds(new Date(), this.getProofMinimumDurationSeconds(prover))
|
166
|
+
}
|
167
|
+
|
168
|
+
/**
|
169
|
+
* The minimum duration that a proof can be generated for a given prover
|
170
|
+
*
|
171
|
+
* @param prover the address of the prover
|
172
|
+
* @returns
|
173
|
+
*/
|
174
|
+
private getProofMinimumDurationSeconds(prover: ProofType): number {
|
175
|
+
const proofs = this.ecoConfigService.getIntentConfigs().proofs
|
176
|
+
switch (prover) {
|
177
|
+
case PROOF_HYPERLANE:
|
178
|
+
return proofs.hyperlane_duration_seconds
|
179
|
+
case PROOF_STORAGE:
|
180
|
+
default:
|
181
|
+
return proofs.storage_duration_seconds
|
182
|
+
}
|
183
|
+
}
|
184
|
+
}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import { Module } from '@nestjs/common'
|
2
|
+
import { ProofService } from './proof.service'
|
3
|
+
import { TransactionModule } from '../transaction/transaction.module'
|
4
|
+
|
5
|
+
@Module({
|
6
|
+
imports: [TransactionModule],
|
7
|
+
providers: [ProofService],
|
8
|
+
exports: [ProofService],
|
9
|
+
})
|
10
|
+
export class ProverModule {}
|
@@ -0,0 +1,154 @@
|
|
1
|
+
import { createMock, DeepMocked } from '@golevelup/ts-jest'
|
2
|
+
import { EcoConfigService } from '../../eco-configs/eco-config.service'
|
3
|
+
import { Test, TestingModule } from '@nestjs/testing'
|
4
|
+
import { ProofService } from '../../prover/proof.service'
|
5
|
+
import { PROOF_HYPERLANE, PROOF_STORAGE, ProofType } from '../../contracts'
|
6
|
+
import { Hex } from 'viem'
|
7
|
+
import { MultichainPublicClientService } from '../../transaction/multichain-public-client.service'
|
8
|
+
import { addSeconds } from 'date-fns'
|
9
|
+
|
10
|
+
describe('ProofService', () => {
|
11
|
+
let proofService: ProofService
|
12
|
+
let multichainPublicClientService: DeepMocked<MultichainPublicClientService>
|
13
|
+
let ecoConfigService: DeepMocked<EcoConfigService>
|
14
|
+
|
15
|
+
const mockLogDebug = jest.fn()
|
16
|
+
const mockLogLog = jest.fn()
|
17
|
+
|
18
|
+
beforeEach(async () => {
|
19
|
+
const chainMod: TestingModule = await Test.createTestingModule({
|
20
|
+
providers: [
|
21
|
+
ProofService,
|
22
|
+
{
|
23
|
+
provide: MultichainPublicClientService,
|
24
|
+
useValue: createMock<MultichainPublicClientService>(),
|
25
|
+
},
|
26
|
+
{ provide: EcoConfigService, useValue: createMock<EcoConfigService>() },
|
27
|
+
],
|
28
|
+
}).compile()
|
29
|
+
|
30
|
+
proofService = chainMod.get(ProofService)
|
31
|
+
multichainPublicClientService = chainMod.get(MultichainPublicClientService)
|
32
|
+
ecoConfigService = chainMod.get(EcoConfigService)
|
33
|
+
|
34
|
+
proofService['logger'].debug = mockLogDebug
|
35
|
+
proofService['logger'].log = mockLogLog
|
36
|
+
})
|
37
|
+
|
38
|
+
afterEach(async () => {
|
39
|
+
jest.restoreAllMocks()
|
40
|
+
mockLogDebug.mockClear()
|
41
|
+
mockLogLog.mockClear()
|
42
|
+
})
|
43
|
+
|
44
|
+
describe('on startup', () => {
|
45
|
+
it('should call loadProofTypes', async () => {
|
46
|
+
const mockLoad = jest.fn()
|
47
|
+
proofService['loadProofTypes'] = mockLoad
|
48
|
+
await proofService.onModuleInit()
|
49
|
+
expect(mockLoad).toHaveBeenCalledTimes(1)
|
50
|
+
})
|
51
|
+
})
|
52
|
+
|
53
|
+
describe('on loadProofTypes', () => {
|
54
|
+
const mockGetProofTypes = jest.fn()
|
55
|
+
const intentSources = [
|
56
|
+
{ chainID: 1, provers: ['0x123', '0x456'] },
|
57
|
+
{ chainID: 2, provers: ['0x123', '0x777'] },
|
58
|
+
]
|
59
|
+
const proofContracts = {
|
60
|
+
[intentSources[0].provers[0]]: PROOF_HYPERLANE,
|
61
|
+
[intentSources[0].provers[1]]: PROOF_HYPERLANE,
|
62
|
+
[intentSources[1].provers[1]]: PROOF_STORAGE,
|
63
|
+
}
|
64
|
+
const proof1: Record<Hex, ProofType> = {}
|
65
|
+
const proof2: Record<Hex, ProofType> = {}
|
66
|
+
beforeEach(async () => {
|
67
|
+
intentSources[0].provers.forEach((s) => {
|
68
|
+
proof1[s] = PROOF_HYPERLANE
|
69
|
+
})
|
70
|
+
intentSources[1].provers.forEach((s) => {
|
71
|
+
if (s === intentSources[0].provers[0]) {
|
72
|
+
proof2[s] = PROOF_HYPERLANE
|
73
|
+
return
|
74
|
+
}
|
75
|
+
proof2[s] = PROOF_STORAGE
|
76
|
+
})
|
77
|
+
proofService['getProofTypes'] = mockGetProofTypes.mockImplementation(
|
78
|
+
async (chainID: number) => {
|
79
|
+
switch (chainID) {
|
80
|
+
case 1:
|
81
|
+
return proof1
|
82
|
+
case 2:
|
83
|
+
return proof2
|
84
|
+
}
|
85
|
+
},
|
86
|
+
)
|
87
|
+
ecoConfigService.getIntentSources = jest.fn().mockReturnValue(intentSources)
|
88
|
+
await proofService.onModuleInit()
|
89
|
+
})
|
90
|
+
|
91
|
+
afterEach(() => {
|
92
|
+
mockGetProofTypes.mockClear()
|
93
|
+
})
|
94
|
+
|
95
|
+
it('should call getProofTypes for all source intents', async () => {
|
96
|
+
expect(mockGetProofTypes).toHaveBeenCalledTimes(intentSources.length)
|
97
|
+
})
|
98
|
+
|
99
|
+
it('should set the proofContracts', async () => {
|
100
|
+
expect(proofService['proofContracts']).toEqual(proofContracts)
|
101
|
+
})
|
102
|
+
|
103
|
+
it('should should log', async () => {
|
104
|
+
expect(mockLogDebug).toHaveBeenCalledTimes(1)
|
105
|
+
expect(mockLogDebug).toHaveBeenCalledWith({
|
106
|
+
msg: 'loadProofTypes loaded all the proof types',
|
107
|
+
proofs: proofContracts,
|
108
|
+
})
|
109
|
+
})
|
110
|
+
})
|
111
|
+
|
112
|
+
describe('on utility methods', () => {
|
113
|
+
const intentConfigs = {
|
114
|
+
proofs: {
|
115
|
+
storage_duration_seconds: 10,
|
116
|
+
hyperlane_duration_seconds: 20,
|
117
|
+
},
|
118
|
+
}
|
119
|
+
beforeEach(async () => {
|
120
|
+
ecoConfigService.getIntentConfigs = jest.fn().mockReturnValue(intentConfigs)
|
121
|
+
})
|
122
|
+
it('should correctly check if its a hyperlane prover', async () => {
|
123
|
+
jest.spyOn(proofService, 'getProofType').mockReturnValue(PROOF_HYPERLANE)
|
124
|
+
expect(proofService.isHyperlaneProver('0x123')).toBe(true)
|
125
|
+
expect(proofService.isStorageProver('0x123')).toBe(false)
|
126
|
+
})
|
127
|
+
|
128
|
+
it('should correctly check if its a storage prover', async () => {
|
129
|
+
jest.spyOn(proofService, 'getProofType').mockReturnValue(PROOF_STORAGE)
|
130
|
+
expect(proofService.isHyperlaneProver('0x123')).toBe(false)
|
131
|
+
expect(proofService.isStorageProver('0x123')).toBe(true)
|
132
|
+
})
|
133
|
+
|
134
|
+
it('should return the correct minimum proof time', async () => {
|
135
|
+
expect(proofService['getProofMinimumDurationSeconds'](PROOF_HYPERLANE)).toBe(
|
136
|
+
intentConfigs.proofs.hyperlane_duration_seconds,
|
137
|
+
)
|
138
|
+
|
139
|
+
expect(proofService['getProofMinimumDurationSeconds'](PROOF_STORAGE)).toBe(
|
140
|
+
intentConfigs.proofs.storage_duration_seconds,
|
141
|
+
)
|
142
|
+
})
|
143
|
+
|
144
|
+
it('should return whether the intent expires too soon', async () => {
|
145
|
+
const seconds = 100
|
146
|
+
const expires = addSeconds(new Date(), seconds)
|
147
|
+
proofService['getProofMinimumDurationSeconds'] = jest.fn().mockReturnValue(seconds / 2)
|
148
|
+
expect(proofService.isIntentExpirationWithinProofMinimumDate('0x123', expires)).toBe(true)
|
149
|
+
|
150
|
+
proofService['getProofMinimumDurationSeconds'] = jest.fn().mockReturnValue(seconds * 2)
|
151
|
+
expect(proofService.isIntentExpirationWithinProofMinimumDate('0x123', expires)).toBe(false)
|
152
|
+
})
|
153
|
+
})
|
154
|
+
})
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import { QuoteRewardDataDTO, QuoteRewardDataType } from '@/quote/dto/quote.reward.data.dto'
|
2
|
+
import { QuoteRouteDataDTO, QuoteRouteDataInterface } from '@/quote/dto/quote.route.data.dto'
|
3
|
+
import { ApiProperty } from '@nestjs/swagger'
|
4
|
+
import { Type } from 'class-transformer'
|
5
|
+
import { IsNotEmpty, ValidateNested } from 'class-validator'
|
6
|
+
|
7
|
+
/**
|
8
|
+
* The DTO for the intent data. Similar to {@link IntentType} except modified to
|
9
|
+
* include options for the solver to select fulfillment conditions, and with the
|
10
|
+
* on-chain data fields removed.
|
11
|
+
*/
|
12
|
+
export class QuoteIntentDataDTO implements QuoteIntentDataInterface {
|
13
|
+
@IsNotEmpty()
|
14
|
+
@ApiProperty()
|
15
|
+
dAppID: string
|
16
|
+
|
17
|
+
@IsNotEmpty()
|
18
|
+
@ApiProperty()
|
19
|
+
@ValidateNested()
|
20
|
+
@Type(() => QuoteRouteDataDTO)
|
21
|
+
route: QuoteRouteDataDTO
|
22
|
+
|
23
|
+
@IsNotEmpty()
|
24
|
+
@ApiProperty()
|
25
|
+
@ValidateNested()
|
26
|
+
@Type(() => QuoteRewardDataDTO)
|
27
|
+
reward: QuoteRewardDataDTO
|
28
|
+
}
|
29
|
+
|
30
|
+
export interface QuoteIntentDataInterface {
|
31
|
+
// The dApp ID of the intent, optional so schema can be shared for onchain intents and offchain quotes
|
32
|
+
dAppID?: string
|
33
|
+
route: QuoteRouteDataInterface
|
34
|
+
reward: QuoteRewardDataType
|
35
|
+
}
|
@@ -0,0 +1,67 @@
|
|
1
|
+
import { RewardTokensInterface } from '@/contracts'
|
2
|
+
import { ViemAddressTransform } from '@/transforms/viem-address.decorator'
|
3
|
+
import { RewardType } from '@eco-foundation/routes-ts'
|
4
|
+
import { ApiProperty } from '@nestjs/swagger'
|
5
|
+
import { Transform, Type } from 'class-transformer'
|
6
|
+
import { ArrayNotEmpty, IsArray, IsNotEmpty, ValidateNested } from 'class-validator'
|
7
|
+
import { getAddress, Hex } from 'viem'
|
8
|
+
|
9
|
+
/**
|
10
|
+
* The DTO for the intent reward data. Similar to {@link RewardType} except
|
11
|
+
* that it does not contain the creator and tokens fields. Also has a modified
|
12
|
+
* tokens field that is an array of {@link QuoteRewardTokensDTO} which include the
|
13
|
+
* sender's willing token balance to use for the reward.
|
14
|
+
* @param prover denotes the prover address
|
15
|
+
* @param deadline denotes the deadline for the reward
|
16
|
+
* @param nativeValue denotes the native token value of the reward
|
17
|
+
* @param tokens denotes the array of {@link QuoteRewardTokensDTO} that the sender has
|
18
|
+
*/
|
19
|
+
export class QuoteRewardDataDTO implements QuoteRewardDataType {
|
20
|
+
@ViemAddressTransform()
|
21
|
+
@IsNotEmpty()
|
22
|
+
@ApiProperty()
|
23
|
+
creator: Hex
|
24
|
+
|
25
|
+
@ViemAddressTransform()
|
26
|
+
@IsNotEmpty()
|
27
|
+
@ApiProperty()
|
28
|
+
prover: Hex
|
29
|
+
|
30
|
+
@IsNotEmpty()
|
31
|
+
@Transform(({ value }) => BigInt(value))
|
32
|
+
@ApiProperty()
|
33
|
+
deadline: bigint
|
34
|
+
|
35
|
+
@IsNotEmpty()
|
36
|
+
@Transform(({ value }) => BigInt(value))
|
37
|
+
@ApiProperty()
|
38
|
+
nativeValue: bigint
|
39
|
+
|
40
|
+
@IsArray()
|
41
|
+
@ArrayNotEmpty()
|
42
|
+
@ValidateNested()
|
43
|
+
@ApiProperty()
|
44
|
+
@Type(() => QuoteRewardTokensDTO)
|
45
|
+
tokens: QuoteRewardTokensDTO[]
|
46
|
+
}
|
47
|
+
|
48
|
+
/**
|
49
|
+
* The DTO for the reward tokens that the sender has and wants to send.
|
50
|
+
* @param token denotes the token address
|
51
|
+
* @param amount denotes the amount of tokens the caller wants to send
|
52
|
+
* @param balance denotes the amount of tokens the caller can send
|
53
|
+
*/
|
54
|
+
export class QuoteRewardTokensDTO implements RewardTokensInterface {
|
55
|
+
@ViemAddressTransform()
|
56
|
+
@IsNotEmpty()
|
57
|
+
@ApiProperty()
|
58
|
+
@Transform(({ value }) => getAddress(value))
|
59
|
+
token: Hex
|
60
|
+
|
61
|
+
@IsNotEmpty()
|
62
|
+
@Transform(({ value }) => BigInt(value))
|
63
|
+
@ApiProperty()
|
64
|
+
amount: bigint
|
65
|
+
}
|
66
|
+
type QuoteRewardType = RewardType
|
67
|
+
export type QuoteRewardDataType = QuoteRewardType
|