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,467 @@
|
|
1
|
+
import { BalanceService, TokenFetchAnalysis } from '@/balance/balance.service'
|
2
|
+
import { EcoLogMessage } from '@/common/logging/eco-log-message'
|
3
|
+
import { getERC20Selector, isERC20Target } from '@/contracts'
|
4
|
+
import { EcoConfigService } from '@/eco-configs/eco-config.service'
|
5
|
+
import {
|
6
|
+
FeeAlgorithmConfig,
|
7
|
+
FeeConfigType,
|
8
|
+
WhitelistFeeRecord,
|
9
|
+
} from '@/eco-configs/eco-config.types'
|
10
|
+
import { CalculateTokensType, NormalizedToken } from '@/fee/types'
|
11
|
+
import { normalizeBalance } from '@/fee/utils'
|
12
|
+
import { getTransactionTargetData } from '@/intent/utils'
|
13
|
+
import { QuoteIntentDataInterface } from '@/quote/dto/quote.intent.data.dto'
|
14
|
+
import { QuoteError } from '@/quote/errors'
|
15
|
+
import { Mathb } from '@/utils/bigint'
|
16
|
+
import { Injectable, Logger, OnModuleInit } from '@nestjs/common'
|
17
|
+
import { getAddress, Hex } from 'viem'
|
18
|
+
import * as _ from 'lodash'
|
19
|
+
import { QuoteRouteDataInterface } from '@/quote/dto/quote.route.data.dto'
|
20
|
+
|
21
|
+
/**
|
22
|
+
* The base decimal number for erc20 tokens.
|
23
|
+
*/
|
24
|
+
export const BASE_DECIMALS: number = 6
|
25
|
+
|
26
|
+
@Injectable()
|
27
|
+
export class FeeService implements OnModuleInit {
|
28
|
+
private logger = new Logger(FeeService.name)
|
29
|
+
private defaultFee: FeeConfigType
|
30
|
+
private whitelist: WhitelistFeeRecord
|
31
|
+
|
32
|
+
constructor(
|
33
|
+
private readonly balanceService: BalanceService,
|
34
|
+
private readonly ecoConfigService: EcoConfigService,
|
35
|
+
) {}
|
36
|
+
|
37
|
+
onModuleInit() {
|
38
|
+
this.defaultFee = this.ecoConfigService.getIntentConfigs().defaultFee
|
39
|
+
this.whitelist = this.ecoConfigService.getWhitelist()
|
40
|
+
}
|
41
|
+
|
42
|
+
/**
|
43
|
+
* Returns the fee for a transaction, if the intent is provided
|
44
|
+
* then it returns a special fee for that intent if there is one,
|
45
|
+
* otherwise it returns the default fee
|
46
|
+
*
|
47
|
+
* @param intent the optional intent for the fee
|
48
|
+
* @param defaultFeeArg the default fee to use if the intent is not provided,
|
49
|
+
* usually from the solvers own config
|
50
|
+
* @returns
|
51
|
+
*/
|
52
|
+
getFeeConfig(args?: {
|
53
|
+
intent?: QuoteIntentDataInterface
|
54
|
+
defaultFeeArg?: FeeConfigType
|
55
|
+
}): FeeConfigType {
|
56
|
+
const { intent, defaultFeeArg } = args || {}
|
57
|
+
let feeConfig = defaultFeeArg || this.defaultFee
|
58
|
+
if (intent) {
|
59
|
+
const destDefaultFee = this.getAskRouteDestinationSolver(intent.route).fee
|
60
|
+
feeConfig = defaultFeeArg || destDefaultFee
|
61
|
+
const specialFee = this.whitelist[intent.reward.creator]
|
62
|
+
if (specialFee) {
|
63
|
+
const chainFee = specialFee[Number(intent.route.source)]
|
64
|
+
// return a fee that is a merge of the default fee, the special fee and the chain fee
|
65
|
+
// merges left to right with the rightmost object taking precedence. In this
|
66
|
+
// case that is the user and chain specific fee
|
67
|
+
feeConfig = _.merge({}, feeConfig, specialFee.default, chainFee)
|
68
|
+
}
|
69
|
+
}
|
70
|
+
return feeConfig
|
71
|
+
}
|
72
|
+
|
73
|
+
/**
|
74
|
+
* Gets the ask for the quote
|
75
|
+
*
|
76
|
+
* @param totalFulfill the total amount to fulfill, assumes base6
|
77
|
+
* @param route the route of the quote intent
|
78
|
+
* @returns a bigint representing the ask
|
79
|
+
*/
|
80
|
+
getAsk(totalFulfill: bigint, intent: QuoteIntentDataInterface) {
|
81
|
+
const route = intent.route
|
82
|
+
//hardcode the destination to eth mainnet/sepolia if its part of the route
|
83
|
+
const solver = this.getAskRouteDestinationSolver(route)
|
84
|
+
|
85
|
+
let fee = 0n
|
86
|
+
const feeConfig = this.getFeeConfig({ intent, defaultFeeArg: solver.fee })
|
87
|
+
switch (feeConfig.algorithm) {
|
88
|
+
// the default
|
89
|
+
// 0.02 cents + $0.015 per 100$
|
90
|
+
// 20_000n + (totalFulfill / 100_000_000n) * 15_000n
|
91
|
+
case 'linear':
|
92
|
+
const { tranche } = feeConfig.constants as FeeAlgorithmConfig<'linear'>
|
93
|
+
fee =
|
94
|
+
BigInt(feeConfig.constants.baseFee) +
|
95
|
+
(totalFulfill / BigInt(tranche.unitSize)) * BigInt(tranche.unitFee)
|
96
|
+
break
|
97
|
+
default:
|
98
|
+
throw QuoteError.InvalidSolverAlgorithm(route.destination, solver.fee.algorithm)
|
99
|
+
}
|
100
|
+
return fee + totalFulfill
|
101
|
+
}
|
102
|
+
|
103
|
+
/**
|
104
|
+
* Checks if the route is feasible for the quote intent:
|
105
|
+
* 1) the solver can fulfill the transaction
|
106
|
+
* 2) the route is profitable for the solver, ie the rewards cover the ask
|
107
|
+
* @param quote the quote
|
108
|
+
* @returns the error is undefined, error is defined if its not feasible
|
109
|
+
*/
|
110
|
+
async isRouteFeasible(quote: QuoteIntentDataInterface): Promise<{ error?: Error }> {
|
111
|
+
if (quote.route.calls.length != 1) {
|
112
|
+
//todo support multiple calls after testing
|
113
|
+
return { error: QuoteError.MultiFulfillRoute() }
|
114
|
+
}
|
115
|
+
const { totalFillNormalized, error } = await this.getTotalFill(quote)
|
116
|
+
if (!!error) {
|
117
|
+
return { error }
|
118
|
+
}
|
119
|
+
const { totalRewardsNormalized, error: error1 } = await this.getTotalRewards(quote)
|
120
|
+
if (!!error1) {
|
121
|
+
return { error: error1 }
|
122
|
+
}
|
123
|
+
const ask = this.getAsk(totalFillNormalized, quote)
|
124
|
+
return {
|
125
|
+
error:
|
126
|
+
totalRewardsNormalized >= ask
|
127
|
+
? undefined
|
128
|
+
: QuoteError.RouteIsInfeasable(ask, totalRewardsNormalized),
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
/**
|
133
|
+
* Calculates the total normalized fill for the quote intent
|
134
|
+
*
|
135
|
+
* @param quote the quote intent
|
136
|
+
* @returns
|
137
|
+
*/
|
138
|
+
async getTotalFill(
|
139
|
+
quote: QuoteIntentDataInterface,
|
140
|
+
): Promise<{ totalFillNormalized: bigint; error?: Error }> {
|
141
|
+
const { calls, error } = await this.getCallsNormalized(quote)
|
142
|
+
if (error) {
|
143
|
+
return { totalFillNormalized: 0n, error }
|
144
|
+
}
|
145
|
+
return { totalFillNormalized: calls.reduce((acc, call) => acc + call.balance, 0n) }
|
146
|
+
}
|
147
|
+
|
148
|
+
/**
|
149
|
+
* Calculates the total normalized and acceoted rewards for the quote intent
|
150
|
+
* @param quote the quote intent
|
151
|
+
* @returns
|
152
|
+
*/
|
153
|
+
async getTotalRewards(
|
154
|
+
quote: QuoteIntentDataInterface,
|
155
|
+
): Promise<{ totalRewardsNormalized: bigint; error?: Error }> {
|
156
|
+
const { rewards, error } = await this.getRewardsNormalized(quote)
|
157
|
+
if (error) {
|
158
|
+
return { totalRewardsNormalized: 0n, error }
|
159
|
+
}
|
160
|
+
return { totalRewardsNormalized: rewards.reduce((acc, reward) => acc + reward.balance, 0n) }
|
161
|
+
}
|
162
|
+
|
163
|
+
/**
|
164
|
+
* Gets the solver tokens for the source chain and orders them in
|
165
|
+
* a normalized delta descending order. delta = (balance - minBalance) * decimals
|
166
|
+
* @param route the route
|
167
|
+
* @returns
|
168
|
+
*/
|
169
|
+
async calculateTokens(quote: QuoteIntentDataInterface): Promise<{
|
170
|
+
calculated?: CalculateTokensType
|
171
|
+
error?: Error
|
172
|
+
}> {
|
173
|
+
const route = quote.route
|
174
|
+
const srcChainID = route.source
|
175
|
+
const destChainID = route.destination
|
176
|
+
|
177
|
+
const source = this.ecoConfigService
|
178
|
+
.getIntentSources()
|
179
|
+
.find((intent) => BigInt(intent.chainID) == srcChainID)!
|
180
|
+
const solver = this.ecoConfigService.getSolver(destChainID)!
|
181
|
+
|
182
|
+
if (!source || !solver) {
|
183
|
+
let error: Error | undefined
|
184
|
+
if (!source) {
|
185
|
+
error = QuoteError.NoIntentSourceForSource(srcChainID)
|
186
|
+
} else if (!solver) {
|
187
|
+
error = QuoteError.NoSolverForDestination(destChainID)
|
188
|
+
}
|
189
|
+
if (error) {
|
190
|
+
this.logger.error(
|
191
|
+
EcoLogMessage.fromDefault({
|
192
|
+
message: error.message,
|
193
|
+
properties: {
|
194
|
+
error,
|
195
|
+
source,
|
196
|
+
solver,
|
197
|
+
},
|
198
|
+
}),
|
199
|
+
)
|
200
|
+
return { error }
|
201
|
+
}
|
202
|
+
}
|
203
|
+
|
204
|
+
//Get the tokens the solver accepts on the source chain
|
205
|
+
const balance = await this.balanceService.fetchTokenData(Number(srcChainID))
|
206
|
+
if (!balance) {
|
207
|
+
throw QuoteError.FetchingCallTokensFailed(quote.route.source)
|
208
|
+
}
|
209
|
+
const deficitDescending = balance
|
210
|
+
.filter((tokenAnalysis) => source.tokens.includes(tokenAnalysis.token.address))
|
211
|
+
.map((token) => {
|
212
|
+
return {
|
213
|
+
...token,
|
214
|
+
//calculates, converts and normalizes the delta
|
215
|
+
delta: this.calculateDelta(token),
|
216
|
+
}
|
217
|
+
})
|
218
|
+
//Sort tokens with leading deficits than: inrange/surplus reordered in accending order
|
219
|
+
.sort((a, b) => -1 * Mathb.compare(a.delta.balance, b.delta.balance))
|
220
|
+
|
221
|
+
//ge/calculate the rewards for the quote intent
|
222
|
+
const { rewards, error: errorRewards } = await this.getRewardsNormalized(quote)
|
223
|
+
const { calls, error: errorCalls } = await this.getCallsNormalized(quote)
|
224
|
+
if (errorCalls || errorRewards) {
|
225
|
+
return { error: errorCalls || errorRewards }
|
226
|
+
}
|
227
|
+
return {
|
228
|
+
calculated: {
|
229
|
+
solver,
|
230
|
+
rewards,
|
231
|
+
calls,
|
232
|
+
deficitDescending, //token liquidity with deficit first descending
|
233
|
+
},
|
234
|
+
}
|
235
|
+
}
|
236
|
+
|
237
|
+
/**
|
238
|
+
* Fetches the rewardes for the quote intent, grabs their info from the erc20 contracts and then converts
|
239
|
+
* and normalizes their values
|
240
|
+
* @param quote the quote intent
|
241
|
+
*/
|
242
|
+
async getRewardsNormalized(
|
243
|
+
quote: QuoteIntentDataInterface,
|
244
|
+
): Promise<{ rewards: NormalizedToken[]; error?: Error }> {
|
245
|
+
const srcChainID = quote.route.source
|
246
|
+
const source = this.ecoConfigService
|
247
|
+
.getIntentSources()
|
248
|
+
.find((intent) => BigInt(intent.chainID) == srcChainID)
|
249
|
+
if (!source) {
|
250
|
+
return { rewards: [], error: QuoteError.NoIntentSourceForSource(srcChainID) }
|
251
|
+
}
|
252
|
+
const acceptedTokens = quote.reward.tokens
|
253
|
+
.filter((reward) => source.tokens.includes(reward.token))
|
254
|
+
.map((reward) => reward.token)
|
255
|
+
const erc20Rewards = await this.balanceService.fetchTokenBalances(
|
256
|
+
Number(srcChainID),
|
257
|
+
acceptedTokens,
|
258
|
+
)
|
259
|
+
if (Object.keys(erc20Rewards).length === 0) {
|
260
|
+
return { rewards: [], error: QuoteError.FetchingRewardTokensFailed(BigInt(srcChainID)) }
|
261
|
+
}
|
262
|
+
|
263
|
+
return {
|
264
|
+
rewards: Object.values(erc20Rewards).map((tb) => {
|
265
|
+
const token = quote.reward.tokens.find((reward) => getAddress(reward.token) === tb.address)
|
266
|
+
return this.convertNormalize(token!.amount, {
|
267
|
+
chainID: srcChainID,
|
268
|
+
address: tb.address,
|
269
|
+
decimals: tb.decimals,
|
270
|
+
})
|
271
|
+
}),
|
272
|
+
}
|
273
|
+
}
|
274
|
+
|
275
|
+
/**
|
276
|
+
* Fetches the call tokens for the quote intent, grabs their info from the erc20 contracts and then converts
|
277
|
+
* to a standard reserve value for comparisons
|
278
|
+
*
|
279
|
+
* Throws if there is not enought liquidity for the call
|
280
|
+
*
|
281
|
+
* @param quote the quote intent
|
282
|
+
* @param solver the solver for the quote intent
|
283
|
+
* @returns
|
284
|
+
*/
|
285
|
+
async getCallsNormalized(quote: QuoteIntentDataInterface): Promise<{
|
286
|
+
calls: NormalizedToken[]
|
287
|
+
error: Error | undefined
|
288
|
+
}> {
|
289
|
+
const solver = this.ecoConfigService.getSolver(quote.route.destination)
|
290
|
+
if (!solver) {
|
291
|
+
return { calls: [], error: QuoteError.NoSolverForDestination(quote.route.destination) }
|
292
|
+
}
|
293
|
+
const callERC20Balances = await this.balanceService.fetchTokenBalances(
|
294
|
+
solver.chainID,
|
295
|
+
quote.route.calls.map((call) => call.target),
|
296
|
+
)
|
297
|
+
|
298
|
+
if (Object.keys(callERC20Balances).length === 0) {
|
299
|
+
return { calls: [], error: QuoteError.FetchingCallTokensFailed(BigInt(solver.chainID)) }
|
300
|
+
}
|
301
|
+
const erc20Balances = Object.values(callERC20Balances).reduce(
|
302
|
+
(acc, tokenBalance) => {
|
303
|
+
const config = solver.targets[tokenBalance.address]
|
304
|
+
acc[tokenBalance.address] = {
|
305
|
+
token: tokenBalance,
|
306
|
+
config: {
|
307
|
+
...config,
|
308
|
+
chainId: solver.chainID,
|
309
|
+
address: tokenBalance.address,
|
310
|
+
type: 'erc20',
|
311
|
+
},
|
312
|
+
chainId: solver.chainID,
|
313
|
+
}
|
314
|
+
return acc
|
315
|
+
},
|
316
|
+
{} as Record<Hex, TokenFetchAnalysis>,
|
317
|
+
)
|
318
|
+
|
319
|
+
let error: Error | undefined
|
320
|
+
|
321
|
+
let calls: NormalizedToken[] = []
|
322
|
+
try {
|
323
|
+
calls = quote.route.calls.map((call) => {
|
324
|
+
const ttd = getTransactionTargetData(solver, call)
|
325
|
+
if (!isERC20Target(ttd, getERC20Selector('transfer'))) {
|
326
|
+
const err = QuoteError.NonERC20TargetInCalls()
|
327
|
+
this.logger.error(
|
328
|
+
EcoLogMessage.fromDefault({
|
329
|
+
message: err.message,
|
330
|
+
properties: {
|
331
|
+
error: err,
|
332
|
+
call,
|
333
|
+
ttd,
|
334
|
+
},
|
335
|
+
}),
|
336
|
+
)
|
337
|
+
throw err
|
338
|
+
}
|
339
|
+
const callTarget = erc20Balances[call.target]
|
340
|
+
if (!callTarget) {
|
341
|
+
throw QuoteError.FailedToFetchTarget(BigInt(solver.chainID), call.target)
|
342
|
+
}
|
343
|
+
|
344
|
+
const transferAmount = ttd!.decodedFunctionData.args![1] as bigint
|
345
|
+
const normMinBalance = this.getNormalizedMinBalance(callTarget)
|
346
|
+
if (transferAmount > callTarget.token.balance - normMinBalance) {
|
347
|
+
const err = QuoteError.SolverLacksLiquidity(
|
348
|
+
solver.chainID,
|
349
|
+
call.target,
|
350
|
+
transferAmount,
|
351
|
+
callTarget.token.balance,
|
352
|
+
normMinBalance,
|
353
|
+
)
|
354
|
+
this.logger.error(
|
355
|
+
EcoLogMessage.fromDefault({
|
356
|
+
message: QuoteError.SolverLacksLiquidity.name,
|
357
|
+
properties: {
|
358
|
+
error: err,
|
359
|
+
quote,
|
360
|
+
callTarget,
|
361
|
+
},
|
362
|
+
}),
|
363
|
+
)
|
364
|
+
throw err
|
365
|
+
}
|
366
|
+
return this.convertNormalize(transferAmount, {
|
367
|
+
chainID: BigInt(solver.chainID),
|
368
|
+
address: call.target,
|
369
|
+
decimals: callTarget.token.decimals,
|
370
|
+
})
|
371
|
+
})
|
372
|
+
} catch (e) {
|
373
|
+
error = e
|
374
|
+
}
|
375
|
+
|
376
|
+
return { calls, error }
|
377
|
+
}
|
378
|
+
|
379
|
+
/**
|
380
|
+
* Calculates the delta for the token as defined as the balance - minBalance
|
381
|
+
* @param token the token to us
|
382
|
+
* @returns
|
383
|
+
*/
|
384
|
+
calculateDelta(token: TokenFetchAnalysis) {
|
385
|
+
const minBalance = this.getNormalizedMinBalance(token)
|
386
|
+
const delta = token.token.balance - minBalance
|
387
|
+
return this.convertNormalize(delta, {
|
388
|
+
chainID: BigInt(token.chainId),
|
389
|
+
address: token.config.address,
|
390
|
+
decimals: token.token.decimals,
|
391
|
+
})
|
392
|
+
}
|
393
|
+
|
394
|
+
/**
|
395
|
+
* Returns the normalized min balance for the token. Assumes that the minBalance is
|
396
|
+
* set with a decimal of 0, ie in normal dollar units
|
397
|
+
* @param tokenAnalysis the token to use
|
398
|
+
* @returns
|
399
|
+
*/
|
400
|
+
getNormalizedMinBalance(tokenAnalysis: TokenFetchAnalysis) {
|
401
|
+
return normalizeBalance(
|
402
|
+
{ balance: BigInt(tokenAnalysis.config.minBalance), decimal: 0 },
|
403
|
+
tokenAnalysis.token.decimals,
|
404
|
+
).balance
|
405
|
+
}
|
406
|
+
|
407
|
+
/**
|
408
|
+
* Converts and normalizes the token to a standard reserve value for comparisons
|
409
|
+
* @param value the value to convert
|
410
|
+
* @param token the token to us
|
411
|
+
* @returns
|
412
|
+
*/
|
413
|
+
convertNormalize(
|
414
|
+
value: bigint,
|
415
|
+
token: { chainID: bigint; address: Hex; decimals: number },
|
416
|
+
): NormalizedToken {
|
417
|
+
const original = value
|
418
|
+
const newDecimals = BASE_DECIMALS
|
419
|
+
//todo some conversion, assuming here 1-1
|
420
|
+
return {
|
421
|
+
...token,
|
422
|
+
balance: normalizeBalance({ balance: original, decimal: token.decimals }, newDecimals)
|
423
|
+
.balance,
|
424
|
+
decimals: newDecimals,
|
425
|
+
}
|
426
|
+
}
|
427
|
+
|
428
|
+
/**
|
429
|
+
* Deconverts and denormalizes the token form a standard reserve value for comparisons
|
430
|
+
* @param value the value to deconvert
|
431
|
+
* @param token the token to deconvert
|
432
|
+
* @returns
|
433
|
+
*/
|
434
|
+
deconvertNormalize(value: bigint, token: { chainID: bigint; address: Hex; decimals: number }) {
|
435
|
+
const original = value
|
436
|
+
//todo some conversion, assuming here 1-1
|
437
|
+
return {
|
438
|
+
...token,
|
439
|
+
balance: normalizeBalance({ balance: original, decimal: BASE_DECIMALS }, token.decimals)
|
440
|
+
.balance,
|
441
|
+
}
|
442
|
+
}
|
443
|
+
|
444
|
+
/**
|
445
|
+
* Returbs the default route destination solver, unless its a ethereum L1 (mainnet or sepolia).
|
446
|
+
* In which case it returns that one instead
|
447
|
+
*
|
448
|
+
* @param route The route of the quote intent
|
449
|
+
* @returns
|
450
|
+
*/
|
451
|
+
getAskRouteDestinationSolver(route: QuoteRouteDataInterface) {
|
452
|
+
//hardcode the destination to eth mainnet/sepolia if its part of the route
|
453
|
+
const destination =
|
454
|
+
route.destination === 1n || route.source === 1n
|
455
|
+
? 1n
|
456
|
+
: route.destination === 11155111n || route.source === 11155111n
|
457
|
+
? 11155111n
|
458
|
+
: route.destination
|
459
|
+
|
460
|
+
const solver = this.ecoConfigService.getSolver(destination)
|
461
|
+
if (!solver) {
|
462
|
+
//we shouldn't get here after validations are run so throw
|
463
|
+
throw QuoteError.NoSolverForDestination(destination)
|
464
|
+
}
|
465
|
+
return solver
|
466
|
+
}
|
467
|
+
}
|