@mixerx/oracles 1.0.0 → 3.0.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.
Files changed (82) hide show
  1. package/README.md +90 -111
  2. package/dist/application/ports/ILogger.d.ts +0 -1
  3. package/dist/application/ports/ILogger.js +0 -1
  4. package/dist/domain/config/TokenDefaults.d.ts +0 -32
  5. package/dist/domain/config/TokenDefaults.js +7 -73
  6. package/dist/domain/exceptions.d.ts +8 -4
  7. package/dist/domain/exceptions.js +12 -4
  8. package/dist/domain/repositories.d.ts +3 -2
  9. package/dist/domain/repositories.js +10 -1
  10. package/dist/factory.d.ts +15 -46
  11. package/dist/factory.js +18 -28
  12. package/dist/index.d.ts +6 -1
  13. package/dist/index.js +4 -1
  14. package/dist/infrastructure/blockchain/staticProvider.d.ts +3 -0
  15. package/dist/infrastructure/blockchain/staticProvider.js +36 -0
  16. package/dist/infrastructure/config/ProtocolTokenConfig.d.ts +0 -1
  17. package/dist/infrastructure/config/ProtocolTokenConfig.js +7 -5
  18. package/dist/infrastructure/config/gasOracleConfigs.d.ts +2 -13
  19. package/dist/infrastructure/config/gasOracleConfigs.js +7 -34
  20. package/dist/infrastructure/gas/GasPriceOracle.d.ts +14 -17
  21. package/dist/infrastructure/gas/GasPriceOracle.js +89 -44
  22. package/dist/infrastructure/mixerx/MixerXFeeOracle.d.ts +99 -0
  23. package/dist/infrastructure/mixerx/MixerXFeeOracle.js +292 -0
  24. package/dist/infrastructure/multicall/MulticallProvider.d.ts +9 -3
  25. package/dist/infrastructure/multicall/MulticallProvider.js +48 -10
  26. package/dist/infrastructure/token-price/TokenPriceOracle.d.ts +10 -47
  27. package/dist/infrastructure/token-price/TokenPriceOracle.js +75 -107
  28. package/dist/types.d.ts +11 -2
  29. package/dist/types.js +0 -1
  30. package/package.json +37 -15
  31. package/dist/application/ports/ILogger.d.ts.map +0 -1
  32. package/dist/application/ports/ILogger.js.map +0 -1
  33. package/dist/application/ports/index.d.ts +0 -6
  34. package/dist/application/ports/index.d.ts.map +0 -1
  35. package/dist/application/ports/index.js +0 -18
  36. package/dist/application/ports/index.js.map +0 -1
  37. package/dist/application/services/TokenPriceService.d.ts +0 -61
  38. package/dist/application/services/TokenPriceService.d.ts.map +0 -1
  39. package/dist/application/services/TokenPriceService.js +0 -137
  40. package/dist/application/services/TokenPriceService.js.map +0 -1
  41. package/dist/domain/config/TokenDefaults.d.ts.map +0 -1
  42. package/dist/domain/config/TokenDefaults.js.map +0 -1
  43. package/dist/domain/config/index.d.ts +0 -2
  44. package/dist/domain/config/index.d.ts.map +0 -1
  45. package/dist/domain/config/index.js +0 -20
  46. package/dist/domain/config/index.js.map +0 -1
  47. package/dist/domain/exceptions.d.ts.map +0 -1
  48. package/dist/domain/exceptions.js.map +0 -1
  49. package/dist/domain/repositories.d.ts.map +0 -1
  50. package/dist/domain/repositories.js.map +0 -1
  51. package/dist/factory.d.ts.map +0 -1
  52. package/dist/factory.js.map +0 -1
  53. package/dist/index.d.ts.map +0 -1
  54. package/dist/index.js.map +0 -1
  55. package/dist/infrastructure/blockchain/contracts.d.ts +0 -20
  56. package/dist/infrastructure/blockchain/contracts.d.ts.map +0 -1
  57. package/dist/infrastructure/blockchain/contracts.js +0 -10
  58. package/dist/infrastructure/blockchain/contracts.js.map +0 -1
  59. package/dist/infrastructure/config/ProtocolTokenConfig.d.ts.map +0 -1
  60. package/dist/infrastructure/config/ProtocolTokenConfig.js.map +0 -1
  61. package/dist/infrastructure/config/gasOracleConfigs.d.ts.map +0 -1
  62. package/dist/infrastructure/config/gasOracleConfigs.js.map +0 -1
  63. package/dist/infrastructure/config/index.d.ts +0 -5
  64. package/dist/infrastructure/config/index.d.ts.map +0 -1
  65. package/dist/infrastructure/config/index.js +0 -11
  66. package/dist/infrastructure/config/index.js.map +0 -1
  67. package/dist/infrastructure/gas/GasPriceOracle.d.ts.map +0 -1
  68. package/dist/infrastructure/gas/GasPriceOracle.js.map +0 -1
  69. package/dist/infrastructure/multicall/MulticallProvider.d.ts.map +0 -1
  70. package/dist/infrastructure/multicall/MulticallProvider.js.map +0 -1
  71. package/dist/infrastructure/token-price/TokenPriceOracle.d.ts.map +0 -1
  72. package/dist/infrastructure/token-price/TokenPriceOracle.js.map +0 -1
  73. package/dist/infrastructure/token-price/index.d.ts +0 -3
  74. package/dist/infrastructure/token-price/index.d.ts.map +0 -1
  75. package/dist/infrastructure/token-price/index.js +0 -6
  76. package/dist/infrastructure/token-price/index.js.map +0 -1
  77. package/dist/infrastructure/tornado/TornadoFeeOracle.d.ts +0 -90
  78. package/dist/infrastructure/tornado/TornadoFeeOracle.d.ts.map +0 -1
  79. package/dist/infrastructure/tornado/TornadoFeeOracle.js +0 -230
  80. package/dist/infrastructure/tornado/TornadoFeeOracle.js.map +0 -1
  81. package/dist/types.d.ts.map +0 -1
  82. package/dist/types.js.map +0 -1
package/README.md CHANGED
@@ -1,139 +1,118 @@
1
1
  # MixerX Oracles
2
2
 
3
- Oracle library for MixerX Relayer - fee calculation and token pricing for Tornado Cash operations.
4
-
5
- ## Requirements
6
-
7
- - Node.js >= 24.12.0
8
- - Yarn 4.12.0 (nodeLinker: node-modules)
9
-
10
- Create `./.yarnrc.yml` (repository-root relative path, works on Linux/macOS/Windows):
11
-
12
- ```yaml
13
- nodeLinker: node-modules
14
- ```
3
+ Fail-closed Sepolia pricing and gas policy for MixerX. Version 3.0.0 rejects a
4
+ withdrawal quote when its gas or token price is missing, stale or economically
5
+ unsafe.
6
+
7
+ ## Security contract
8
+
9
+ - Sepolia (`11155111`) is the only accepted chain.
10
+ - RPC endpoints must use HTTPS, are time-bounded and are checked against the
11
+ real `eth_chainId` response.
12
+ - Token calls use Multicall3 atomically at one block. Any failed token rejects
13
+ the batch; no historical constant is substituted.
14
+ - Non-native withdrawal fees require a repository price plus its observation
15
+ timestamp. The default maximum age is two minutes.
16
+ - Gas data, service fee, refund, token decimals and total fee are bounded before
17
+ a result is returned. The default total-fee ceiling matches the contracts at
18
+ 10% of the withdrawal amount.
19
+ - Configurable token-price sanity bands can reject corrupted but fresh cache
20
+ values.
21
+ - The exact withdrawal gas limit is mandatory input and must come from
22
+ simulation or `eth_estimateGas`; the package contains no token-specific
23
+ limits inherited from another protocol.
24
+ - Logging is disabled by default. Inject an `ILogger` that follows the host
25
+ service's privacy policy if operational events are required.
26
+
27
+ The Sepolia OffchainOracle deployment must contain valid rates and must be
28
+ protected by the protocol's TWAP/deviation policy. The library cannot create or
29
+ initialize that on-chain state. Call `assertReady()` during application startup;
30
+ do not accept traffic if it fails.
15
31
 
16
32
  ## Installation
17
33
 
18
34
  ```bash
19
- yarn add @mixerx/oracles
35
+ yarn add @mixerx/oracles@3.0.0
20
36
  ```
21
37
 
22
- ## Quick Start
38
+ Node.js 24.12.0 or newer is required.
39
+
40
+ ## Usage
41
+
42
+ ```ts
43
+ import {
44
+ createMixerxOraclesModule,
45
+ type ITokenPriceRepository,
46
+ } from '@mixerx/oracles';
23
47
 
24
- ```typescript
25
- import { createMixerxOraclesModule } from '@mixerx/oracles';
48
+ const priceRepository: ITokenPriceRepository = {
49
+ getQuote: async (symbol) => readCachedQuote(symbol),
50
+ };
26
51
 
27
- // Create module
28
52
  const oracles = createMixerxOraclesModule({
29
- chainId: 1, // Mainnet
30
- rpcUrl: 'https://mainnet.infura.io/v3/YOUR_KEY',
31
- relayerFeePercent: 0.004, // 0.4%
32
- priceRepository: yourRedisRepository, // Implementation of ITokenPriceRepository
33
- multicallAddress: '0xeefBa1e63905eF1D7ACbA5a8513c70307C1cE441', // optional
34
- // offchainOracleAddress is optional:
35
- // if omitted, it is resolved from @mixerx/config by chainId
36
- fallbackGasPrices: {
37
- legacy: { instant: 25, fast: 20, standard: 18, low: 15 },
38
- eip1559: { baseFee: 10, maxFeePerGas: 20, maxPriorityFeePerGas: 2 },
39
- },
53
+ chainId: 11155111,
54
+ rpcUrl: process.env.SEPOLIA_RPC_URL!,
55
+ relayerFeePercent: 0.004,
56
+ priceRepository,
57
+ maxGasPriceGwei: 100,
58
+ maxPriceAgeMs: 120_000,
59
+ maxTotalFeeBps: 1_000,
60
+ priceBounds: {
61
+ fds: {
62
+ minimumWeiPerToken: minimumAcceptedFdsPrice,
63
+ maximumWeiPerToken: maximumAcceptedFdsPrice,
64
+ },
65
+ },
40
66
  });
41
67
 
42
- // Fetch token prices
43
- const prices = await oracles.fetchPrices();
44
- console.log(prices); // { dai: '603108348359886', usdc: '601311723569085', ... }
68
+ await oracles.assertReady();
45
69
 
46
- // Calculate withdrawal fee
47
- const fee = await oracles.tornadoFeeOracle.calculateWithdrawalFee({
48
- currency: 'dai',
49
- amount: BigInt('100000000000000000000'), // 100 DAI
70
+ const fee = await oracles.mixerXFeeOracle.calculateWithdrawalFee({
71
+ currency: 'fds',
72
+ amount: 1_000_000_000_000_000_000n,
50
73
  decimals: 18,
51
- refund: '0x0',
52
- txType: 'relayer_withdrawal',
74
+ gasLimit: estimatedWithdrawalGas,
75
+ refund: '0',
76
+ purpose: 'fee',
53
77
  });
54
78
 
55
- console.log(`Total fee: ${fee.totalFee.toString()}`);
79
+ console.log({
80
+ totalFee: fee.totalFee,
81
+ tokenPriceInEth: fee.tokenPriceInEth,
82
+ priceSource: fee.priceSource,
83
+ priceObservedAtMs: fee.priceObservedAtMs,
84
+ baseFeePerGas: fee.baseFeePerGas,
85
+ effectiveGasPriceWei: fee.gasPriceWei,
86
+ gasSource: fee.gasSource,
87
+ });
56
88
  ```
57
89
 
58
- ## Architecture
59
-
60
- This library follows Domain-Driven Design (DDD) principles:
61
-
62
- - **Domain**: Value objects and entities for fees, gas, and tokens
63
- - **Application**: Services and use cases for fee calculation and price fetching
64
- - **Infrastructure**: Adapters for blockchain interaction (ethers)
65
-
66
- ## Features
67
-
68
- ### Fee Calculation (V5)
69
-
70
- - Smart gas limit bumping (10% for relayer, 30% for user)
71
- - On-chain gas estimation with fallback to defaults
72
- - Support for ETH and ERC-20 tokens
73
- - Automatic conversion between ETH and token units
74
- - EIP-1559 and Legacy gas price support
75
-
76
- ### Token Price Fetching
90
+ `fee_quote` and `fee_validation` remain accepted as input aliases for migration,
91
+ but both normalize to the single `fee` policy in returned data.
77
92
 
78
- - Fetches prices from on-chain OffchainOracle
79
- - Uses Multicall for efficient batch requests
80
- - Fallback to default prices if fetching fails
81
- - Token list is resolved from `@mixerx/config` by `chainId`
93
+ ## Price-feed operation
82
94
 
83
- ### Mining Fee Support
95
+ `fetchPrices()` reads all configured Sepolia tokens through Multicall3 at one
96
+ explicit block. It returns the complete price map together with `blockNumber`
97
+ and `observedAtMs`, and only succeeds when every token succeeds. The host price
98
+ feed must persist each price and its observation metadata atomically. Fee
99
+ calculation never falls back from that repository to a spot value.
84
100
 
85
- - Anonymity Mining reward calculations
86
- - Anonymity Mining withdrawal calculations
87
- - Automatic conversion between points and TORN
101
+ The default Multicall3 address is
102
+ `0xcA11bde05977b3631167028862bE2a173976CA11`; an explicit verified address can
103
+ be supplied with `multicallAddress`.
88
104
 
89
- ## API Reference
105
+ ## Development
90
106
 
91
- ### `createMixerxOraclesModule(config)`
92
-
93
- Factory function to create the oracles module.
94
-
95
- **Config:**
96
- - `chainId`: Chain ID (1 for Mainnet)
97
- - `rpcUrl`: RPC endpoint URL
98
- - `relayerFeePercent`: Fee percentage (e.g., 0.004 for 0.4%)
99
- - `priceRepository`: Implementation of `ITokenPriceRepository`
100
- - `multicallAddress?`: Multicall contract address
101
- - `offchainOracleAddress?`: OffchainOracle contract address (fallback to `@mixerx/config` by `chainId`)
102
- - `fallbackGasPrices?`: Optional fallback gas prices
103
-
104
- ### Use Cases
105
-
106
- #### `tornadoFeeOracle.calculateWithdrawalFee(input)`
107
-
108
- Calculate and validate fees for Tornado Cash withdrawals.
109
-
110
- **Input:**
111
- - `currency`: Token symbol (eth, dai, usdc, etc.)
112
- - `amount`: Withdrawal amount in base units (`bigint`)
113
- - `decimals`: Token decimals
114
- - `refund?`: Optional refund amount (hex string)
115
- - `txType?`: `'relayer_withdrawal' | 'user_withdrawal' | 'relayer_withdrawal_check_v4'`
116
-
117
- **Output:**
118
- - `gasCost`: bigint
119
- - `relayerFee`: bigint
120
- - `refundAmount`: bigint
121
- - `totalFee`: bigint
122
- - `currency`: string
123
-
124
- #### `fetchTokenPrices.execute(input?)`
125
-
126
- Fetch current token prices from on-chain oracle.
127
-
128
- **Input (optional):**
129
- - `tokens?`: Specific tokens to fetch
130
- - `useDefaults?`: Use default prices as fallback
107
+ ```bash
108
+ yarn install --immutable
109
+ yarn validate
110
+ yarn npm audit --all --recursive --severity high
111
+ ```
131
112
 
132
- **Output:**
133
- - `prices`: TokenPrices map
134
- - `failedTokens`: Tokens that failed to fetch
135
- - `usedDefaults`: Whether defaults were used
113
+ CI executes those gates for every pull request. Published packages pin the
114
+ protocol configuration dependency exactly.
136
115
 
137
116
  ## License
138
117
 
139
- MIT
118
+ GPL-3.0-only. See `LICENSE`.
@@ -15,4 +15,3 @@ export declare class ConsoleLogger implements ILogger {
15
15
  warn(message: string, meta?: unknown): void;
16
16
  error(message: string, meta?: unknown): void;
17
17
  }
18
- //# sourceMappingURL=ILogger.d.ts.map
@@ -23,4 +23,3 @@ class ConsoleLogger {
23
23
  }
24
24
  }
25
25
  exports.ConsoleLogger = ConsoleLogger;
26
- //# sourceMappingURL=ILogger.js.map
@@ -1,33 +1 @@
1
- /**
2
- * Token Defaults
3
- * Centralized configuration for Tornado Cash tokens
4
- *
5
- * This eliminates duplication between:
6
- * - TokenPriceOracle
7
- * - TokenPriceService
8
- * - FeeCalculator
9
- *
10
- * All token addresses, decimals, and default prices in one place
11
- */
12
- export declare const DEFAULT_TOKEN_PRICES: Record<string, string>;
13
- export declare const TORNADO_GAS_LIMITS: Record<string, number>;
14
- export declare const MINING_GAS_LIMITS: Record<string, number>;
15
- export declare const DEFAULT_RELAYER_FEE_PERCENT = 0.004;
16
- export declare const BUMP_PERCENTAGES: Record<string, number>;
17
- /**
18
- * Get default price for token
19
- */
20
- export declare function getDefaultTokenPrice(symbol: string): string | undefined;
21
- /**
22
- * Get gas limit for Tornado operation
23
- */
24
- export declare function getTornadoGasLimit(currency: string): number;
25
- /**
26
- * Get mining gas limit
27
- */
28
- export declare function getMiningGasLimit(operation: 'reward' | 'withdraw'): number;
29
- /**
30
- * Get bump percent for transaction type
31
- */
32
1
  export declare function getBumpPercent(txType: string): number;
33
- //# sourceMappingURL=TokenDefaults.d.ts.map
@@ -1,78 +1,12 @@
1
1
  "use strict";
2
- /**
3
- * Token Defaults
4
- * Centralized configuration for Tornado Cash tokens
5
- *
6
- * This eliminates duplication between:
7
- * - TokenPriceOracle
8
- * - TokenPriceService
9
- * - FeeCalculator
10
- *
11
- * All token addresses, decimals, and default prices in one place
12
- */
13
2
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.BUMP_PERCENTAGES = exports.DEFAULT_RELAYER_FEE_PERCENT = exports.MINING_GAS_LIMITS = exports.TORNADO_GAS_LIMITS = exports.DEFAULT_TOKEN_PRICES = void 0;
15
- exports.getDefaultTokenPrice = getDefaultTokenPrice;
16
- exports.getTornadoGasLimit = getTornadoGasLimit;
17
- exports.getMiningGasLimit = getMiningGasLimit;
18
3
  exports.getBumpPercent = getBumpPercent;
19
- // Token contract addresses and metadata are sourced from @mixerx/config
20
- // via infrastructure/config/ProtocolTokenConfig.ts.
21
- // Default prices as fallback (in wei per token unit, relative to ETH)
22
- // These represent the rate: 1 token = X wei of ETH
23
- exports.DEFAULT_TOKEN_PRICES = {
24
- torn: '1689423546359032',
25
- dai: '598416104472725',
26
- cdai: '13384388487019',
27
- usdc: '599013776676721',
28
- usdt: '599323410893614',
29
- wbtc: '15659889148334216720',
30
- };
31
- // Gas limits for Tornado operations by token (in gas units)
32
- exports.TORNADO_GAS_LIMITS = {
33
- eth: 390000,
34
- dai: 550000,
35
- cdai: 425000,
36
- usdc: 80000,
37
- usdt: 100000,
38
- wbtc: 85000,
39
- };
40
- // Mining operation gas limits
41
- exports.MINING_GAS_LIMITS = {
42
- reward: 455000,
43
- withdraw: 400000,
44
- };
45
- // Relayer fee percentage (0.4% default)
46
- exports.DEFAULT_RELAYER_FEE_PERCENT = 0.004;
47
- // Bump percentages by transaction type
48
- exports.BUMP_PERCENTAGES = {
49
- relayer_withdrawal: 10,
50
- user_withdrawal: 30,
51
- relayer_withdrawal_check_v4: 0,
52
- default: 10,
53
- };
54
- /**
55
- * Get default price for token
56
- */
57
- function getDefaultTokenPrice(symbol) {
58
- return exports.DEFAULT_TOKEN_PRICES[symbol.toLowerCase()];
59
- }
60
- /**
61
- * Get gas limit for Tornado operation
62
- */
63
- function getTornadoGasLimit(currency) {
64
- return exports.TORNADO_GAS_LIMITS[currency.toLowerCase()] ?? exports.TORNADO_GAS_LIMITS.eth;
65
- }
66
- /**
67
- * Get mining gas limit
68
- */
69
- function getMiningGasLimit(operation) {
70
- return exports.MINING_GAS_LIMITS[operation] ?? 400000;
71
- }
72
- /**
73
- * Get bump percent for transaction type
74
- */
4
+ const BUMP_PERCENTAGES = new Map([
5
+ ['relayer_withdrawal', 10],
6
+ ['user_withdrawal', 30],
7
+ ['relayer_withdrawal_check_v4', 0],
8
+ ]);
75
9
  function getBumpPercent(txType) {
76
- return exports.BUMP_PERCENTAGES[txType] ?? exports.BUMP_PERCENTAGES.default;
10
+ const key = txType.trim().toLowerCase();
11
+ return BUMP_PERCENTAGES.get(key) ?? 10;
77
12
  }
78
- //# sourceMappingURL=TokenDefaults.js.map
@@ -1,5 +1,9 @@
1
- export declare class TokenPriceError extends Error {
2
- readonly symbols: string[];
3
- constructor(message: string, symbols?: string[]);
1
+ export type OracleErrorCode = 'INVALID_CONFIG' | 'INVALID_INPUT' | 'NETWORK_MISMATCH' | 'RPC_REQUEST_FAILED' | 'TOKEN_PRICE_UNAVAILABLE' | 'TOKEN_PRICE_STALE' | 'TOKEN_PRICE_OUT_OF_RANGE' | 'ECONOMIC_LIMIT_EXCEEDED';
2
+ export declare class OracleError extends Error {
3
+ readonly code: OracleErrorCode;
4
+ constructor(code: OracleErrorCode, message: string);
5
+ }
6
+ export declare class TokenPriceError extends OracleError {
7
+ readonly symbols: readonly string[];
8
+ constructor(message: string, symbols?: readonly string[]);
4
9
  }
5
- //# sourceMappingURL=exceptions.d.ts.map
@@ -1,13 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TokenPriceError = void 0;
4
- class TokenPriceError extends Error {
3
+ exports.TokenPriceError = exports.OracleError = void 0;
4
+ class OracleError extends Error {
5
+ code;
6
+ constructor(code, message) {
7
+ super(message);
8
+ this.code = code;
9
+ this.name = 'OracleError';
10
+ }
11
+ }
12
+ exports.OracleError = OracleError;
13
+ class TokenPriceError extends OracleError {
5
14
  symbols;
6
15
  constructor(message, symbols = []) {
7
- super(message);
16
+ super('TOKEN_PRICE_UNAVAILABLE', message);
8
17
  this.symbols = symbols;
9
18
  this.name = 'TokenPriceError';
10
19
  }
11
20
  }
12
21
  exports.TokenPriceError = TokenPriceError;
13
- //# sourceMappingURL=exceptions.js.map
@@ -1,4 +1,5 @@
1
+ import type { TokenPriceQuote } from '../types';
1
2
  export interface ITokenPriceRepository {
2
- getPrice(symbol: string): Promise<bigint | null | undefined>;
3
+ getQuote(symbol: string): Promise<TokenPriceQuote | null | undefined>;
3
4
  }
4
- //# sourceMappingURL=repositories.d.ts.map
5
+ export declare function toPriceQuote(price: bigint | null | undefined, observedAtMs: number | null | undefined): TokenPriceQuote | null;
@@ -1,3 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=repositories.js.map
3
+ exports.toPriceQuote = toPriceQuote;
4
+ function toPriceQuote(price, observedAtMs) {
5
+ if (price === null || price === undefined || price <= 0n) {
6
+ return null;
7
+ }
8
+ if (!Number.isSafeInteger(observedAtMs) || (observedAtMs ?? -1) < 0) {
9
+ return null;
10
+ }
11
+ return { priceInEthWei: price, observedAtMs: observedAtMs, source: 'repository' };
12
+ }
package/dist/factory.d.ts CHANGED
@@ -1,62 +1,31 @@
1
+ import type { ILogger } from './application/ports/ILogger';
2
+ import type { ITokenPriceRepository } from './domain/repositories';
1
3
  import { GasPriceOracle } from './infrastructure/gas/GasPriceOracle';
4
+ import { MixerXFeeOracle } from './infrastructure/mixerx/MixerXFeeOracle';
5
+ import type { TokenPriceBounds } from './infrastructure/mixerx/MixerXFeeOracle';
2
6
  import { TokenPriceOracle } from './infrastructure/token-price/TokenPriceOracle';
3
- import { TornadoFeeOracle } from './infrastructure/tornado/TornadoFeeOracle';
4
- import { ITokenPriceRepository } from './domain/repositories';
5
- import { ChainId } from './infrastructure/config/gasOracleConfigs';
6
- import { TokenPrices } from './types';
7
- /**
8
- * Simplified Configuration for mixerx-oracles module
9
- */
7
+ import type { ChainId, TokenPriceSnapshot } from './types';
10
8
  export interface MixerxOraclesConfig {
11
- /** Chain ID */
12
9
  chainId: ChainId;
13
- /** RPC URL for blockchain connection */
14
10
  rpcUrl: string;
15
- /** Relayer fee percentage (e.g., 0.004 for 0.4%) */
16
11
  relayerFeePercent: number;
17
- /** Token price repository (for reading prices from Redis/cache) - optional */
18
12
  priceRepository?: ITokenPriceRepository;
19
- /** Multicall contract address (optional, uses default) */
20
13
  multicallAddress?: string;
21
- /** Offchain oracle contract address (optional, falls back to @mixerx/config by chain) */
22
14
  offchainOracleAddress?: string;
23
- /** Fallback gas prices (optional) */
24
- fallbackGasPrices?: {
25
- legacy?: {
26
- instant: number;
27
- fast: number;
28
- standard: number;
29
- low: number;
30
- };
31
- eip1559?: {
32
- baseFee: number;
33
- maxFeePerGas: number;
34
- maxPriorityFeePerGas: number;
35
- };
36
- };
15
+ logger?: ILogger;
16
+ rpcTimeoutMs?: number;
17
+ maxGasPriceGwei?: number;
18
+ maxPriceAgeMs?: number;
19
+ maxRefundWei?: bigint;
20
+ maxTotalFeeBps?: number;
21
+ priceBounds?: Readonly<Record<string, TokenPriceBounds>>;
37
22
  }
38
- /**
39
- * Mixerx Oracles Module Interface
40
- */
41
23
  export interface IMixerxOraclesModule {
42
- /** Gas price oracle (EIP-1559 + Legacy) */
43
24
  gasPriceOracle: GasPriceOracle;
44
- /** Token price oracle (Multicall + OffchainOracle) */
45
25
  tokenPriceOracle: TokenPriceOracle;
46
- /** Tornado fee oracle (V5 - calculates withdrawal fees) */
47
- tornadoFeeOracle: TornadoFeeOracle;
48
- /** Convenience method to fetch token prices */
49
- fetchPrices(): Promise<TokenPrices>;
50
- /** Chain ID */
26
+ mixerXFeeOracle: MixerXFeeOracle;
27
+ fetchPrices(): Promise<TokenPriceSnapshot>;
28
+ assertReady(): Promise<void>;
51
29
  chainId: ChainId;
52
30
  }
53
- /**
54
- * Factory function to create mixerx-oracles module
55
- *
56
- * This creates a complete oracle module with:
57
- * - GasPriceOracle: Fetches EIP-1559 and Legacy gas prices from multiple sources
58
- * - TokenPriceOracle: Fetches token prices using Multicall to OffchainOracle
59
- * - TornadoFeeOracle: Calculates Tornado Cash withdrawal fees
60
- */
61
31
  export declare function createMixerxOraclesModule(config: MixerxOraclesConfig): IMixerxOraclesModule;
62
- //# sourceMappingURL=factory.d.ts.map
package/dist/factory.js CHANGED
@@ -1,51 +1,41 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createMixerxOraclesModule = createMixerxOraclesModule;
4
+ const ILogger_1 = require("./application/ports/ILogger");
4
5
  const GasPriceOracle_1 = require("./infrastructure/gas/GasPriceOracle");
6
+ const MixerXFeeOracle_1 = require("./infrastructure/mixerx/MixerXFeeOracle");
5
7
  const TokenPriceOracle_1 = require("./infrastructure/token-price/TokenPriceOracle");
6
- const TornadoFeeOracle_1 = require("./infrastructure/tornado/TornadoFeeOracle");
7
- const ProtocolTokenConfig_1 = require("./infrastructure/config/ProtocolTokenConfig");
8
- /**
9
- * Factory function to create mixerx-oracles module
10
- *
11
- * This creates a complete oracle module with:
12
- * - GasPriceOracle: Fetches EIP-1559 and Legacy gas prices from multiple sources
13
- * - TokenPriceOracle: Fetches token prices using Multicall to OffchainOracle
14
- * - TornadoFeeOracle: Calculates Tornado Cash withdrawal fees
15
- */
16
8
  function createMixerxOraclesModule(config) {
17
- // Multicall remains explicit/hardcoded-compatible by design
18
- const multicallAddress = config.multicallAddress || '0xeefBa1e63905eF1D7ACbA5a8513c70307C1cE441';
19
- const offchainOracleAddress = config.offchainOracleAddress || (0, ProtocolTokenConfig_1.resolveOffchainOracleAddress)(config.chainId);
20
- const defaultTokens = (0, ProtocolTokenConfig_1.resolveDefaultTokens)(config.chainId);
21
- // Create gas price oracle
9
+ const logger = config.logger ?? new ILogger_1.NullLogger();
22
10
  const gasPriceOracle = new GasPriceOracle_1.GasPriceOracle({
23
11
  chainId: config.chainId,
24
12
  rpcUrl: config.rpcUrl,
25
- fallbackGasPrices: config.fallbackGasPrices,
13
+ logger,
14
+ rpcTimeoutMs: config.rpcTimeoutMs,
15
+ maxGasPriceGwei: config.maxGasPriceGwei,
26
16
  });
27
- // Create token price oracle
28
- const tokenPriceOracle = new TokenPriceOracle_1.TokenPriceOracle(config.rpcUrl, config.chainId, offchainOracleAddress, multicallAddress, defaultTokens);
29
- // Create tornado fee oracle
30
- const tornadoFeeOracle = new TornadoFeeOracle_1.TornadoFeeOracle({
17
+ const tokenPriceOracle = new TokenPriceOracle_1.TokenPriceOracle(config.rpcUrl, config.chainId, config.offchainOracleAddress, config.multicallAddress, undefined, logger, config.rpcTimeoutMs);
18
+ const mixerXFeeOracle = new MixerXFeeOracle_1.MixerXFeeOracle({
31
19
  chainId: config.chainId,
32
20
  rpcUrl: config.rpcUrl,
33
21
  relayerFeePercent: config.relayerFeePercent,
34
22
  gasPriceOracle,
35
23
  tokenPriceOracle,
36
24
  priceRepository: config.priceRepository,
37
- multicallAddress,
38
- offchainOracleAddress,
25
+ logger,
26
+ rpcTimeoutMs: config.rpcTimeoutMs,
27
+ maxGasPriceGwei: config.maxGasPriceGwei,
28
+ maxPriceAgeMs: config.maxPriceAgeMs,
29
+ maxRefundWei: config.maxRefundWei,
30
+ maxTotalFeeBps: config.maxTotalFeeBps,
31
+ priceBounds: config.priceBounds,
39
32
  });
40
33
  return {
41
34
  gasPriceOracle,
42
35
  tokenPriceOracle,
43
- tornadoFeeOracle,
36
+ mixerXFeeOracle,
44
37
  chainId: config.chainId,
45
- // Convenience method
46
- async fetchPrices() {
47
- return tokenPriceOracle.fetchPrices();
48
- },
38
+ fetchPrices: () => tokenPriceOracle.fetchPrices(),
39
+ assertReady: () => mixerXFeeOracle.assertReady(),
49
40
  };
50
41
  }
51
- //# sourceMappingURL=factory.js.map
package/dist/index.d.ts CHANGED
@@ -1,2 +1,7 @@
1
1
  export * from './factory';
2
- //# sourceMappingURL=index.d.ts.map
2
+ export { OracleError, TokenPriceError } from './domain/exceptions';
3
+ export type { OracleErrorCode } from './domain/exceptions';
4
+ export type { ILogger } from './application/ports/ILogger';
5
+ export type { ITokenPriceRepository } from './domain/repositories';
6
+ export type { TokenPriceQuote, TokenPriceSnapshot } from './types';
7
+ export type { FeeCalculationResult, MixerXGasPurpose, MixerXFeeCalculationResult, MixerXGasParametersForTxResult, MixerXTxType, WithdrawalFeeInput, } from './infrastructure/mixerx/MixerXFeeOracle';
package/dist/index.js CHANGED
@@ -14,5 +14,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.TokenPriceError = exports.OracleError = void 0;
17
18
  __exportStar(require("./factory"), exports);
18
- //# sourceMappingURL=index.js.map
19
+ var exceptions_1 = require("./domain/exceptions");
20
+ Object.defineProperty(exports, "OracleError", { enumerable: true, get: function () { return exceptions_1.OracleError; } });
21
+ Object.defineProperty(exports, "TokenPriceError", { enumerable: true, get: function () { return exceptions_1.TokenPriceError; } });
@@ -0,0 +1,3 @@
1
+ import { ethers, type JsonRpcApiProviderOptions } from 'ethers';
2
+ export declare function createVerifiedJsonRpcProvider(rpcUrl: string, chainId: number, options?: JsonRpcApiProviderOptions, timeoutMs?: number): ethers.JsonRpcProvider;
3
+ export declare function assertProviderNetwork(provider: ethers.JsonRpcProvider, expectedChainId: number): Promise<void>;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createVerifiedJsonRpcProvider = createVerifiedJsonRpcProvider;
4
+ exports.assertProviderNetwork = assertProviderNetwork;
5
+ const ethers_1 = require("ethers");
6
+ const exceptions_1 = require("../../domain/exceptions");
7
+ const gasOracleConfigs_1 = require("../config/gasOracleConfigs");
8
+ const DEFAULT_RPC_TIMEOUT_MS = 10_000;
9
+ function createVerifiedJsonRpcProvider(rpcUrl, chainId, options, timeoutMs = DEFAULT_RPC_TIMEOUT_MS) {
10
+ (0, gasOracleConfigs_1.assertSepoliaChainId)(chainId);
11
+ validateRpcUrl(rpcUrl);
12
+ if (!Number.isInteger(timeoutMs) || timeoutMs < 1_000 || timeoutMs > 60_000) {
13
+ throw new exceptions_1.OracleError('INVALID_CONFIG', 'rpcTimeoutMs must be an integer between 1000 and 60000');
14
+ }
15
+ const request = new ethers_1.FetchRequest(rpcUrl);
16
+ request.timeout = timeoutMs;
17
+ return new ethers_1.ethers.JsonRpcProvider(request, undefined, options);
18
+ }
19
+ async function assertProviderNetwork(provider, expectedChainId) {
20
+ const network = await provider.getNetwork();
21
+ if (network.chainId !== BigInt(expectedChainId)) {
22
+ throw new exceptions_1.OracleError('NETWORK_MISMATCH', `RPC network mismatch: expected ${String(expectedChainId)}, received ${network.chainId.toString()}`);
23
+ }
24
+ }
25
+ function validateRpcUrl(rawUrl) {
26
+ let url;
27
+ try {
28
+ url = new URL(rawUrl);
29
+ }
30
+ catch {
31
+ throw new exceptions_1.OracleError('INVALID_CONFIG', 'rpcUrl must be a valid HTTPS URL');
32
+ }
33
+ if (url.protocol !== 'https:' || url.username || url.password) {
34
+ throw new exceptions_1.OracleError('INVALID_CONFIG', 'rpcUrl must use HTTPS and must not contain user-info credentials');
35
+ }
36
+ }
@@ -1,4 +1,3 @@
1
1
  import type { ChainId, Token } from '../../types';
2
2
  export declare function resolveOffchainOracleAddress(chainId: ChainId | number): string;
3
3
  export declare function resolveDefaultTokens(chainId: ChainId | number): Token[];
4
- //# sourceMappingURL=ProtocolTokenConfig.d.ts.map