@mixerx/oracles 0.5.4 → 2.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 (77) hide show
  1. package/README.md +76 -109
  2. package/dist/application/ports/ILogger.d.ts +0 -1
  3. package/dist/application/ports/ILogger.js.map +1 -1
  4. package/dist/application/ports/index.d.ts +0 -1
  5. package/dist/application/ports/index.js.map +1 -1
  6. package/dist/domain/config/TokenDefaults.d.ts +0 -32
  7. package/dist/domain/config/TokenDefaults.js +7 -74
  8. package/dist/domain/config/TokenDefaults.js.map +1 -1
  9. package/dist/domain/config/index.d.ts +1 -2
  10. package/dist/domain/config/index.js +1 -13
  11. package/dist/domain/config/index.js.map +1 -1
  12. package/dist/domain/exceptions.d.ts +8 -4
  13. package/dist/domain/exceptions.js +12 -3
  14. package/dist/domain/exceptions.js.map +1 -1
  15. package/dist/domain/repositories.d.ts +3 -1
  16. package/dist/domain/repositories.js +10 -0
  17. package/dist/domain/repositories.js.map +1 -1
  18. package/dist/factory.d.ts +10 -31
  19. package/dist/factory.js +14 -24
  20. package/dist/factory.js.map +1 -1
  21. package/dist/index.d.ts +5 -2
  22. package/dist/index.js +4 -4
  23. package/dist/index.js.map +1 -1
  24. package/dist/infrastructure/blockchain/staticProvider.d.ts +2 -3
  25. package/dist/infrastructure/blockchain/staticProvider.js +33 -8
  26. package/dist/infrastructure/blockchain/staticProvider.js.map +1 -1
  27. package/dist/infrastructure/config/ProtocolTokenConfig.d.ts +0 -1
  28. package/dist/infrastructure/config/ProtocolTokenConfig.js +2 -0
  29. package/dist/infrastructure/config/ProtocolTokenConfig.js.map +1 -1
  30. package/dist/infrastructure/config/gasOracleConfigs.d.ts +2 -13
  31. package/dist/infrastructure/config/gasOracleConfigs.js +7 -33
  32. package/dist/infrastructure/config/gasOracleConfigs.js.map +1 -1
  33. package/dist/infrastructure/config/index.d.ts +0 -1
  34. package/dist/infrastructure/config/index.js.map +1 -1
  35. package/dist/infrastructure/gas/GasPriceOracle.d.ts +12 -2
  36. package/dist/infrastructure/gas/GasPriceOracle.js +76 -37
  37. package/dist/infrastructure/gas/GasPriceOracle.js.map +1 -1
  38. package/dist/infrastructure/mixerx/MixerXFeeOracle.d.ts +36 -41
  39. package/dist/infrastructure/mixerx/MixerXFeeOracle.js +146 -151
  40. package/dist/infrastructure/mixerx/MixerXFeeOracle.js.map +1 -1
  41. package/dist/infrastructure/multicall/MulticallProvider.d.ts +4 -3
  42. package/dist/infrastructure/multicall/MulticallProvider.js +44 -9
  43. package/dist/infrastructure/multicall/MulticallProvider.js.map +1 -1
  44. package/dist/infrastructure/token-price/TokenPriceOracle.d.ts +11 -45
  45. package/dist/infrastructure/token-price/TokenPriceOracle.js +68 -100
  46. package/dist/infrastructure/token-price/TokenPriceOracle.js.map +1 -1
  47. package/dist/infrastructure/token-price/index.d.ts +2 -2
  48. package/dist/infrastructure/token-price/index.js.map +1 -1
  49. package/dist/types.d.ts +5 -1
  50. package/dist/types.js.map +1 -1
  51. package/package.json +56 -41
  52. package/dist/application/ports/ILogger.d.ts.map +0 -1
  53. package/dist/application/ports/index.d.ts.map +0 -1
  54. package/dist/application/services/TokenPriceService.d.ts +0 -60
  55. package/dist/application/services/TokenPriceService.d.ts.map +0 -1
  56. package/dist/application/services/TokenPriceService.js +0 -137
  57. package/dist/application/services/TokenPriceService.js.map +0 -1
  58. package/dist/domain/config/TokenDefaults.d.ts.map +0 -1
  59. package/dist/domain/config/index.d.ts.map +0 -1
  60. package/dist/domain/exceptions.d.ts.map +0 -1
  61. package/dist/domain/repositories.d.ts.map +0 -1
  62. package/dist/factory.d.ts.map +0 -1
  63. package/dist/index.d.ts.map +0 -1
  64. package/dist/infrastructure/blockchain/contracts.d.ts +0 -20
  65. package/dist/infrastructure/blockchain/contracts.d.ts.map +0 -1
  66. package/dist/infrastructure/blockchain/contracts.js +0 -10
  67. package/dist/infrastructure/blockchain/contracts.js.map +0 -1
  68. package/dist/infrastructure/blockchain/staticProvider.d.ts.map +0 -1
  69. package/dist/infrastructure/config/ProtocolTokenConfig.d.ts.map +0 -1
  70. package/dist/infrastructure/config/gasOracleConfigs.d.ts.map +0 -1
  71. package/dist/infrastructure/config/index.d.ts.map +0 -1
  72. package/dist/infrastructure/gas/GasPriceOracle.d.ts.map +0 -1
  73. package/dist/infrastructure/mixerx/MixerXFeeOracle.d.ts.map +0 -1
  74. package/dist/infrastructure/multicall/MulticallProvider.d.ts.map +0 -1
  75. package/dist/infrastructure/token-price/TokenPriceOracle.d.ts.map +0 -1
  76. package/dist/infrastructure/token-price/index.d.ts.map +0 -1
  77. package/dist/types.d.ts.map +0 -1
package/dist/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
1
  export * from './factory';
2
- export { MIXERX_GAS_LIMITS, getMixerXGasLimit } from './domain/config';
3
- //# 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 { FeeCalculationResult, MixerXGasPurpose, MixerXFeeCalculationResult, MixerXGasParametersForTxResult, MixerXTxType, WithdrawalFeeInput, } from './infrastructure/mixerx/MixerXFeeOracle';
package/dist/index.js CHANGED
@@ -14,9 +14,9 @@ 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.getMixerXGasLimit = exports.MIXERX_GAS_LIMITS = void 0;
17
+ exports.TokenPriceError = exports.OracleError = void 0;
18
18
  __exportStar(require("./factory"), exports);
19
- var config_1 = require("./domain/config");
20
- Object.defineProperty(exports, "MIXERX_GAS_LIMITS", { enumerable: true, get: function () { return config_1.MIXERX_GAS_LIMITS; } });
21
- Object.defineProperty(exports, "getMixerXGasLimit", { enumerable: true, get: function () { return config_1.getMixerXGasLimit; } });
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; } });
22
22
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,4CAA0B;AAC1B,0CAAuE;AAA9D,2GAAA,iBAAiB,OAAA;AAAE,2GAAA,iBAAiB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,4CAA0B;AAC1B,kDAAmE;AAA1D,yGAAA,WAAW,OAAA;AAAE,6GAAA,eAAe,OAAA","sourcesContent":["export * from './factory';\r\nexport { OracleError, TokenPriceError } from './domain/exceptions';\r\nexport type { OracleErrorCode } from './domain/exceptions';\r\nexport type { ILogger } from './application/ports/ILogger';\r\nexport type { ITokenPriceRepository } from './domain/repositories';\r\nexport type {\r\n FeeCalculationResult,\r\n MixerXGasPurpose,\r\n MixerXFeeCalculationResult,\r\n MixerXGasParametersForTxResult,\r\n MixerXTxType,\r\n WithdrawalFeeInput,\r\n} from './infrastructure/mixerx/MixerXFeeOracle';\r\n"]}
@@ -1,4 +1,3 @@
1
1
  import { ethers, type JsonRpcApiProviderOptions } from 'ethers';
2
- import type { ChainId } from '../../types';
3
- export declare const createStaticJsonRpcProvider: (rpcUrl: string, chainId: ChainId | number, options?: JsonRpcApiProviderOptions) => ethers.JsonRpcProvider;
4
- //# sourceMappingURL=staticProvider.d.ts.map
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>;
@@ -1,12 +1,37 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createStaticJsonRpcProvider = void 0;
3
+ exports.createVerifiedJsonRpcProvider = createVerifiedJsonRpcProvider;
4
+ exports.assertProviderNetwork = assertProviderNetwork;
4
5
  const ethers_1 = require("ethers");
5
- const createStaticJsonRpcProvider = (rpcUrl, chainId, options = {}) => {
6
- return new ethers_1.ethers.JsonRpcProvider(rpcUrl, Number(chainId), {
7
- ...options,
8
- staticNetwork: true,
9
- });
10
- };
11
- exports.createStaticJsonRpcProvider = createStaticJsonRpcProvider;
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
+ }
12
37
  //# sourceMappingURL=staticProvider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"staticProvider.js","sourceRoot":"","sources":["../../../src/infrastructure/blockchain/staticProvider.ts"],"names":[],"mappings":";;;AAAA,mCAAgE;AAGzD,MAAM,2BAA2B,GAAG,CACzC,MAAc,EACd,OAAyB,EACzB,UAAqC,EAAE,EACf,EAAE;IAC1B,OAAO,IAAI,eAAM,CAAC,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE;QACzD,GAAG,OAAO;QACV,aAAa,EAAE,IAAI;KACpB,CAAC,CAAC;AACL,CAAC,CAAC;AATW,QAAA,2BAA2B,+BAStC"}
1
+ {"version":3,"file":"staticProvider.js","sourceRoot":"","sources":["../../../src/infrastructure/blockchain/staticProvider.ts"],"names":[],"mappings":";;AAMA,sEAeC;AAED,sDAWC;AAlCD,mCAA8E;AAC9E,wDAAsD;AACtD,iEAAkE;AAElE,MAAM,sBAAsB,GAAG,MAAM,CAAC;AAEtC,SAAgB,6BAA6B,CAC3C,MAAc,EACd,OAAe,EACf,OAAmC,EACnC,YAAoB,sBAAsB;IAE1C,IAAA,uCAAoB,EAAC,OAAO,CAAC,CAAC;IAC9B,cAAc,CAAC,MAAM,CAAC,CAAC;IACvB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,KAAK,IAAI,SAAS,GAAG,MAAM,EAAE,CAAC;QAC5E,MAAM,IAAI,wBAAW,CAAC,gBAAgB,EAAE,wDAAwD,CAAC,CAAC;IACpG,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,qBAAY,CAAC,MAAM,CAAC,CAAC;IACzC,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAC5B,OAAO,IAAI,eAAM,CAAC,eAAe,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AACjE,CAAC;AAEM,KAAK,UAAU,qBAAqB,CACzC,QAAgC,EAChC,eAAuB;IAEvB,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,CAAC;IAC5C,IAAI,OAAO,CAAC,OAAO,KAAK,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,wBAAW,CACnB,kBAAkB,EAClB,kCAAkC,MAAM,CAAC,eAAe,CAAC,cAAc,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CACpG,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,MAAc;IACpC,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,wBAAW,CAAC,gBAAgB,EAAE,kCAAkC,CAAC,CAAC;IAC9E,CAAC;IAED,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;QAC9D,MAAM,IAAI,wBAAW,CAAC,gBAAgB,EAAE,kEAAkE,CAAC,CAAC;IAC9G,CAAC;AACH,CAAC","sourcesContent":["import { ethers, FetchRequest, type JsonRpcApiProviderOptions } from 'ethers';\r\nimport { OracleError } from '../../domain/exceptions';\r\nimport { assertSepoliaChainId } from '../config/gasOracleConfigs';\r\n\r\nconst DEFAULT_RPC_TIMEOUT_MS = 10_000;\r\n\r\nexport function createVerifiedJsonRpcProvider(\r\n rpcUrl: string,\r\n chainId: number,\r\n options?: JsonRpcApiProviderOptions,\r\n timeoutMs: number = DEFAULT_RPC_TIMEOUT_MS,\r\n): ethers.JsonRpcProvider {\r\n assertSepoliaChainId(chainId);\r\n validateRpcUrl(rpcUrl);\r\n if (!Number.isInteger(timeoutMs) || timeoutMs < 1_000 || timeoutMs > 60_000) {\r\n throw new OracleError('INVALID_CONFIG', 'rpcTimeoutMs must be an integer between 1000 and 60000');\r\n }\r\n\r\n const request = new FetchRequest(rpcUrl);\r\n request.timeout = timeoutMs;\r\n return new ethers.JsonRpcProvider(request, undefined, options);\r\n}\r\n\r\nexport async function assertProviderNetwork(\r\n provider: ethers.JsonRpcProvider,\r\n expectedChainId: number,\r\n): Promise<void> {\r\n const network = await provider.getNetwork();\r\n if (network.chainId !== BigInt(expectedChainId)) {\r\n throw new OracleError(\r\n 'NETWORK_MISMATCH',\r\n `RPC network mismatch: expected ${String(expectedChainId)}, received ${network.chainId.toString()}`,\r\n );\r\n }\r\n}\r\n\r\nfunction validateRpcUrl(rawUrl: string): void {\r\n let url: URL;\r\n try {\r\n url = new URL(rawUrl);\r\n } catch {\r\n throw new OracleError('INVALID_CONFIG', 'rpcUrl must be a valid HTTPS URL');\r\n }\r\n\r\n if (url.protocol !== 'https:' || url.username || url.password) {\r\n throw new OracleError('INVALID_CONFIG', 'rpcUrl must use HTTPS and must not contain user-info credentials');\r\n }\r\n}\r\n"]}
@@ -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
@@ -3,8 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.resolveOffchainOracleAddress = resolveOffchainOracleAddress;
4
4
  exports.resolveDefaultTokens = resolveDefaultTokens;
5
5
  const config_1 = require("@mixerx/config");
6
+ const gasOracleConfigs_1 = require("./gasOracleConfigs");
6
7
  function toSupportedChainId(chainId) {
7
8
  const numericChainId = Number(chainId);
9
+ (0, gasOracleConfigs_1.assertSepoliaChainId)(numericChainId);
8
10
  if (!(0, config_1.isSupportedNetId)(numericChainId)) {
9
11
  throw new Error(`Unsupported chainId ${chainId} for @mixerx/config`);
10
12
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ProtocolTokenConfig.js","sourceRoot":"","sources":["../../../src/infrastructure/config/ProtocolTokenConfig.ts"],"names":[],"mappings":";;AAcA,oEAQC;AAED,oDAoCC;AA5DD,2CAA+E;AAK/E,SAAS,kBAAkB,CAAC,OAAyB;IACnD,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACvC,IAAI,CAAC,IAAA,yBAAgB,EAAC,cAAc,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,uBAAuB,OAAO,qBAAqB,CAAC,CAAC;IACvE,CAAC;IAED,OAAO,cAAc,CAAC;AACxB,CAAC;AAED,SAAgB,4BAA4B,CAAC,OAAyB;IACpE,MAAM,gBAAgB,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,IAAA,yBAAgB,EAAC,gBAAgB,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,OAAO,EAAE,SAAS,CAAC,qBAAqB,CAAC;IACzD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,+DAA+D,OAAO,EAAE,CAAC,CAAC;IAC5F,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAgB,oBAAoB,CAAC,OAAyB;IAC5D,MAAM,KAAK,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,gBAAgB,GAAG,kBAAS,CAAC,KAAK,CAA+C,CAAC;IACxF,MAAM,aAAa,GAAG,IAAA,yBAAgB,EAAC,KAAK,CAAC,CAAC;IAE9C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAiB,CAAC;IAE1C,IAAI,aAAa,EAAE,MAAM,CAAC,kBAAkB,EAAE,CAAC;QAC7C,MAAM,kBAAkB,GAAG,aAAa,CAAC,MAAM,CAAC,kBAAkB,CAAC,WAAW,EAAE,CAAC;QACjF,QAAQ,CAAC,GAAG,CAAC,kBAAkB,EAAE;YAC/B,OAAO,EAAE,kBAAkB;YAC3B,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,iBAAiB,CAAC,WAAW,EAAE;YAC5D,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,mBAAmB;SACnD,CAAC,CAAC;IACL,CAAC;IAED,IAAI,gBAAgB,EAAE,CAAC;QACrB,KAAK,MAAM,YAAY,IAAI,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAC3D,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,CAAC;gBAC/B,SAAS;YACX,CAAC;YACD,MAAM,YAAY,GAAG,YAAY,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC;YAC7D,QAAQ,CAAC,GAAG,CAAC,YAAY,EAAE;gBACzB,OAAO,EAAE,YAAY;gBACrB,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE;gBACzC,QAAQ,EAAE,YAAY,CAAC,QAAQ;aAChC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,8DAA8D,OAAO,EAAE,CAAC,CAAC;IAC3F,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"ProtocolTokenConfig.js","sourceRoot":"","sources":["../../../src/infrastructure/config/ProtocolTokenConfig.ts"],"names":[],"mappings":";;AAgBA,oEAQC;AAED,oDAoCC;AA9DD,2CAA+E;AAI/E,yDAA0D;AAE1D,SAAS,kBAAkB,CAAC,OAAyB;IACnD,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACvC,IAAA,uCAAoB,EAAC,cAAc,CAAC,CAAC;IACrC,IAAI,CAAC,IAAA,yBAAgB,EAAC,cAAc,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,uBAAuB,OAAO,qBAAqB,CAAC,CAAC;IACvE,CAAC;IAED,OAAO,cAAc,CAAC;AACxB,CAAC;AAED,SAAgB,4BAA4B,CAAC,OAAyB;IACpE,MAAM,gBAAgB,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,IAAA,yBAAgB,EAAC,gBAAgB,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,OAAO,EAAE,SAAS,CAAC,qBAAqB,CAAC;IACzD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,+DAA+D,OAAO,EAAE,CAAC,CAAC;IAC5F,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAgB,oBAAoB,CAAC,OAAyB;IAC5D,MAAM,KAAK,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,gBAAgB,GAAG,kBAAS,CAAC,KAAK,CAA+C,CAAC;IACxF,MAAM,aAAa,GAAG,IAAA,yBAAgB,EAAC,KAAK,CAAC,CAAC;IAE9C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAiB,CAAC;IAE1C,IAAI,aAAa,EAAE,MAAM,CAAC,kBAAkB,EAAE,CAAC;QAC7C,MAAM,kBAAkB,GAAG,aAAa,CAAC,MAAM,CAAC,kBAAkB,CAAC,WAAW,EAAE,CAAC;QACjF,QAAQ,CAAC,GAAG,CAAC,kBAAkB,EAAE;YAC/B,OAAO,EAAE,kBAAkB;YAC3B,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,iBAAiB,CAAC,WAAW,EAAE;YAC5D,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,mBAAmB;SACnD,CAAC,CAAC;IACL,CAAC;IAED,IAAI,gBAAgB,EAAE,CAAC;QACrB,KAAK,MAAM,YAAY,IAAI,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAC3D,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,CAAC;gBAC/B,SAAS;YACX,CAAC;YACD,MAAM,YAAY,GAAG,YAAY,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC;YAC7D,QAAQ,CAAC,GAAG,CAAC,YAAY,EAAE;gBACzB,OAAO,EAAE,YAAY;gBACrB,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE;gBACzC,QAAQ,EAAE,YAAY,CAAC,QAAQ;aAChC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,8DAA8D,OAAO,EAAE,CAAC,CAAC;IAC3F,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { getNetworkConfig, instances, isSupportedNetId } from '@mixerx/config';\r\nimport type { availableIds } from '@mixerx/config';\r\nimport type { CurrencyConfig } from '@mixerx/config';\r\nimport type { ChainId, Token } from '../../types';\r\nimport { assertSepoliaChainId } from './gasOracleConfigs';\r\n\r\nfunction toSupportedChainId(chainId: ChainId | number): availableIds {\r\n const numericChainId = Number(chainId);\r\n assertSepoliaChainId(numericChainId);\r\n if (!isSupportedNetId(numericChainId)) {\r\n throw new Error(`Unsupported chainId ${chainId} for @mixerx/config`);\r\n }\r\n\r\n return numericChainId;\r\n}\r\n\r\nexport function resolveOffchainOracleAddress(chainId: ChainId | number): string {\r\n const validatedChainId = toSupportedChainId(chainId);\r\n const network = getNetworkConfig(validatedChainId);\r\n const address = network?.contracts.offchainOracleAddress;\r\n if (!address) {\r\n throw new Error(`Missing offchainOracleAddress in @mixerx/config for chainId ${chainId}`);\r\n }\r\n return address;\r\n}\r\n\r\nexport function resolveDefaultTokens(chainId: ChainId | number): Token[] {\r\n const netId = toSupportedChainId(chainId);\r\n const networkInstances = instances[netId] as Record<string, CurrencyConfig> | undefined;\r\n const networkConfig = getNetworkConfig(netId);\r\n\r\n const tokenMap = new Map<string, Token>();\r\n\r\n if (networkConfig?.tokens.mixerXTokenAddress) {\r\n const mixerXTokenAddress = networkConfig.tokens.mixerXTokenAddress.toLowerCase();\r\n tokenMap.set(mixerXTokenAddress, {\r\n address: mixerXTokenAddress,\r\n symbol: networkConfig.tokens.mixerXTokenSymbol.toLowerCase(),\r\n decimals: networkConfig.tokens.mixerXTokenDecimals,\r\n });\r\n }\r\n\r\n if (networkInstances) {\r\n for (const currencyData of Object.values(networkInstances)) {\r\n if (!currencyData.tokenAddress) {\r\n continue;\r\n }\r\n const tokenAddress = currencyData.tokenAddress.toLowerCase();\r\n tokenMap.set(tokenAddress, {\r\n address: tokenAddress,\r\n symbol: currencyData.symbol.toLowerCase(),\r\n decimals: currencyData.decimals,\r\n });\r\n }\r\n }\r\n\r\n const tokens = Array.from(tokenMap.values());\r\n if (tokens.length === 0) {\r\n throw new Error(`No default tokens resolved from @mixerx/config for chainId ${chainId}`);\r\n }\r\n\r\n return tokens;\r\n}\r\n"]}
@@ -1,14 +1,3 @@
1
- import { getNetworkConfig as getProtocolNetworkConfig } from '@mixerx/config';
2
1
  export type ChainId = number;
3
- export interface NetworkConfig {
4
- chainId: number;
5
- rpcUrl?: string;
6
- }
7
- export declare const NETWORKS: Record<number, NetworkConfig>;
8
- export declare function getNetworkConfig(chainId: ChainId): ReturnType<typeof getProtocolNetworkConfig>;
9
- export declare function isSupportedChain(chainId: number): boolean;
10
- export declare function getSupportedChainIds(): number[];
11
- export declare function getDefaultGasPrice(): number;
12
- export declare function getMaxGasPrice(): number;
13
- export declare function getRpcUrl(chainId: number): string | undefined;
14
- //# sourceMappingURL=gasOracleConfigs.d.ts.map
2
+ export declare const SEPOLIA_CHAIN_ID = 11155111;
3
+ export declare function assertSepoliaChainId(chainId: number): void;
@@ -1,38 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NETWORKS = void 0;
4
- exports.getNetworkConfig = getNetworkConfig;
5
- exports.isSupportedChain = isSupportedChain;
6
- exports.getSupportedChainIds = getSupportedChainIds;
7
- exports.getDefaultGasPrice = getDefaultGasPrice;
8
- exports.getMaxGasPrice = getMaxGasPrice;
9
- exports.getRpcUrl = getRpcUrl;
10
- const config_1 = require("@mixerx/config");
11
- const SUPPORTED_CHAIN_IDS = [...config_1.SUPPORTED_NET_IDS];
12
- exports.NETWORKS = Object.fromEntries(SUPPORTED_CHAIN_IDS.map((chainId) => [chainId, { chainId }]));
13
- function getNetworkConfig(chainId) {
14
- if (!(0, config_1.isSupportedNetId)(chainId)) {
15
- return undefined;
3
+ exports.SEPOLIA_CHAIN_ID = void 0;
4
+ exports.assertSepoliaChainId = assertSepoliaChainId;
5
+ const exceptions_1 = require("../../domain/exceptions");
6
+ exports.SEPOLIA_CHAIN_ID = 11_155_111;
7
+ function assertSepoliaChainId(chainId) {
8
+ if (chainId !== exports.SEPOLIA_CHAIN_ID) {
9
+ throw new exceptions_1.OracleError('INVALID_CONFIG', `Unsupported chainId ${String(chainId)}; @mixerx/oracles is Sepolia-only`);
16
10
  }
17
- return (0, config_1.getNetworkConfig)(chainId);
18
- }
19
- function isSupportedChain(chainId) {
20
- return (0, config_1.isSupportedNetId)(chainId);
21
- }
22
- function getSupportedChainIds() {
23
- return [...SUPPORTED_CHAIN_IDS];
24
- }
25
- function getDefaultGasPrice() {
26
- return 20;
27
- }
28
- function getMaxGasPrice() {
29
- return 500;
30
- }
31
- function getRpcUrl(chainId) {
32
- const network = getNetworkConfig(chainId);
33
- if (!network || !('rpcUrl' in network) || typeof network.rpcUrl !== 'string') {
34
- return undefined;
35
- }
36
- return network.rpcUrl;
37
11
  }
38
12
  //# sourceMappingURL=gasOracleConfigs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gasOracleConfigs.js","sourceRoot":"","sources":["../../../src/infrastructure/config/gasOracleConfigs.ts"],"names":[],"mappings":";;;AAoBA,4CAMC;AAED,4CAEC;AAED,oDAEC;AAED,gDAEC;AAED,wCAEC;AAED,8BAOC;AAnDD,2CAIwB;AAUxB,MAAM,mBAAmB,GAAG,CAAC,GAAG,0BAAiB,CAAa,CAAC;AAElD,QAAA,QAAQ,GAAkC,MAAM,CAAC,WAAW,CACvE,mBAAmB,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,CAC7D,CAAC;AAEF,SAAgB,gBAAgB,CAAC,OAAgB;IAC/C,IAAI,CAAC,IAAA,yBAAgB,EAAC,OAAO,CAAC,EAAE,CAAC;QAC/B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,IAAA,yBAAwB,EAAC,OAAuB,CAAC,CAAC;AAC3D,CAAC;AAED,SAAgB,gBAAgB,CAAC,OAAe;IAC9C,OAAO,IAAA,yBAAgB,EAAC,OAAO,CAAC,CAAC;AACnC,CAAC;AAED,SAAgB,oBAAoB;IAClC,OAAO,CAAC,GAAG,mBAAmB,CAAC,CAAC;AAClC,CAAC;AAED,SAAgB,kBAAkB;IAChC,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAgB,cAAc;IAC5B,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAgB,SAAS,CAAC,OAAe;IACvC,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC1C,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC7E,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,OAAO,CAAC,MAAM,CAAC;AACxB,CAAC"}
1
+ {"version":3,"file":"gasOracleConfigs.js","sourceRoot":"","sources":["../../../src/infrastructure/config/gasOracleConfigs.ts"],"names":[],"mappings":";;;AAKA,oDAOC;AAZD,wDAAsD;AAGzC,QAAA,gBAAgB,GAAG,UAAU,CAAC;AAE3C,SAAgB,oBAAoB,CAAC,OAAe;IAClD,IAAI,OAAO,KAAK,wBAAgB,EAAE,CAAC;QACjC,MAAM,IAAI,wBAAW,CACnB,gBAAgB,EAChB,uBAAuB,MAAM,CAAC,OAAO,CAAC,mCAAmC,CAC1E,CAAC;IACJ,CAAC;AACH,CAAC","sourcesContent":["import { OracleError } from '../../domain/exceptions';\r\n\r\nexport type ChainId = number;\r\nexport const SEPOLIA_CHAIN_ID = 11_155_111;\r\n\r\nexport function assertSepoliaChainId(chainId: number): void {\r\n if (chainId !== SEPOLIA_CHAIN_ID) {\r\n throw new OracleError(\r\n 'INVALID_CONFIG',\r\n `Unsupported chainId ${String(chainId)}; @mixerx/oracles is Sepolia-only`,\r\n );\r\n }\r\n}\r\n"]}
@@ -2,4 +2,3 @@
2
2
  * Infrastructure configuration exports
3
3
  */
4
4
  export { resolveDefaultTokens, resolveOffchainOracleAddress } from './ProtocolTokenConfig';
5
- //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/infrastructure/config/index.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,iCAAiC;AACjC,6DAA2F;AAAlF,2HAAA,oBAAoB,OAAA;AAAE,mIAAA,4BAA4B,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/infrastructure/config/index.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,iCAAiC;AACjC,6DAA2F;AAAlF,2HAAA,oBAAoB,OAAA;AAAE,mIAAA,4BAA4B,OAAA","sourcesContent":["/**\r\n * Infrastructure configuration exports\r\n */\r\n\r\n// Protocol-derived configuration\r\nexport { resolveDefaultTokens, resolveOffchainOracleAddress } from './ProtocolTokenConfig';\r\n"]}
@@ -2,6 +2,7 @@ import type { ILogger } from '../../application/ports/ILogger';
2
2
  import type { ChainId } from '../config/gasOracleConfigs';
3
3
  export interface GasPriceParams {
4
4
  gasPrice?: string;
5
+ baseFeePerGas?: string;
5
6
  maxFeePerGas?: string;
6
7
  maxPriorityFeePerGas?: string;
7
8
  }
@@ -14,6 +15,8 @@ export interface GasPriceOracleOptions {
14
15
  chainId: ChainId;
15
16
  rpcUrl: string;
16
17
  logger?: ILogger;
18
+ rpcTimeoutMs?: number;
19
+ maxGasPriceGwei?: number;
17
20
  fallbackGasPrices?: {
18
21
  legacy?: {
19
22
  instant: number;
@@ -31,11 +34,18 @@ export interface GasPriceOracleOptions {
31
34
  export declare class GasPriceOracle {
32
35
  private readonly provider;
33
36
  private readonly logger;
37
+ private readonly chainId;
38
+ private readonly maxGasPriceWei;
34
39
  private readonly fallback;
35
40
  constructor(options: GasPriceOracleOptions);
36
41
  getTxGasParams(input?: GetTxGasParamsInput): Promise<GasPriceParams>;
42
+ private fallbackParams;
43
+ private validateFallback;
44
+ private validateGwei;
45
+ private validateBump;
37
46
  private bump;
38
- private toWeiGwei;
39
47
  private bumpGwei;
48
+ private toWeiGwei;
49
+ private cap;
50
+ private min;
40
51
  }
41
- //# sourceMappingURL=GasPriceOracle.d.ts.map
@@ -2,75 +2,114 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GasPriceOracle = void 0;
4
4
  const ILogger_1 = require("../../application/ports/ILogger");
5
+ const exceptions_1 = require("../../domain/exceptions");
5
6
  const staticProvider_1 = require("../blockchain/staticProvider");
7
+ const DEFAULT_MAX_GAS_PRICE_GWEI = 500;
6
8
  class GasPriceOracle {
7
9
  provider;
8
10
  logger;
11
+ chainId;
12
+ maxGasPriceWei;
9
13
  fallback;
10
14
  constructor(options) {
11
- this.provider = (0, staticProvider_1.createStaticJsonRpcProvider)(options.rpcUrl, options.chainId, { batchMaxCount: 1 });
15
+ this.chainId = options.chainId;
16
+ this.provider = (0, staticProvider_1.createVerifiedJsonRpcProvider)(options.rpcUrl, options.chainId, { batchMaxCount: 1 }, options.rpcTimeoutMs);
12
17
  this.logger = options.logger ?? new ILogger_1.NullLogger();
18
+ const maxGasPriceGwei = options.maxGasPriceGwei ?? DEFAULT_MAX_GAS_PRICE_GWEI;
19
+ const validatedMaxGasPriceGwei = this.validateGwei(maxGasPriceGwei, 'maxGasPriceGwei');
20
+ if (validatedMaxGasPriceGwei === 0) {
21
+ throw new exceptions_1.OracleError('INVALID_CONFIG', 'maxGasPriceGwei must be greater than 0');
22
+ }
23
+ this.maxGasPriceWei = this.toWeiGwei(validatedMaxGasPriceGwei);
13
24
  this.fallback = {
14
25
  legacy: options.fallbackGasPrices?.legacy ?? { instant: 35, fast: 25, standard: 20, low: 15 },
15
- eip1559: options.fallbackGasPrices?.eip1559 ?? { baseFee: 15, maxFeePerGas: 30, maxPriorityFeePerGas: 2 },
26
+ eip1559: options.fallbackGasPrices?.eip1559 ?? {
27
+ baseFee: 15,
28
+ maxFeePerGas: 30,
29
+ maxPriorityFeePerGas: 2,
30
+ },
16
31
  };
32
+ this.validateFallback();
17
33
  }
18
34
  async getTxGasParams(input = {}) {
19
- const rawBumpPercent = Number(input.bumpPercent);
20
- const bumpPercent = Number.isFinite(rawBumpPercent)
21
- ? Math.max(0, Math.floor(rawBumpPercent))
22
- : 0;
35
+ const bumpPercent = this.validateBump(input.bumpPercent ?? 0);
23
36
  try {
24
- const feeData = await this.provider.getFeeData();
25
- if (!input.isLegacy && feeData.maxFeePerGas && feeData.maxPriorityFeePerGas) {
26
- const maxFeePerGas = this.bump(feeData.maxFeePerGas, bumpPercent);
27
- const maxPriorityFeePerGas = this.bump(feeData.maxPriorityFeePerGas, bumpPercent);
37
+ await (0, staticProvider_1.assertProviderNetwork)(this.provider, this.chainId);
38
+ const [feeData, block] = await Promise.all([
39
+ this.provider.getFeeData(),
40
+ this.provider.getBlock('latest'),
41
+ ]);
42
+ if (!input.isLegacy && feeData.maxFeePerGas && feeData.maxPriorityFeePerGas && block?.baseFeePerGas) {
43
+ const baseFeePerGas = this.cap(block.baseFeePerGas);
44
+ const maxPriorityFeePerGas = this.cap(this.bump(feeData.maxPriorityFeePerGas, bumpPercent));
45
+ const maxFeePerGas = this.cap(this.bump(feeData.maxFeePerGas, bumpPercent));
28
46
  return {
47
+ baseFeePerGas: baseFeePerGas.toString(),
29
48
  maxFeePerGas: maxFeePerGas.toString(),
30
- maxPriorityFeePerGas: maxPriorityFeePerGas.toString(),
49
+ maxPriorityFeePerGas: this.min(maxPriorityFeePerGas, maxFeePerGas).toString(),
31
50
  };
32
51
  }
33
52
  if (feeData.gasPrice) {
34
- return {
35
- gasPrice: this.bump(feeData.gasPrice, bumpPercent).toString(),
36
- };
53
+ return { gasPrice: this.cap(this.bump(feeData.gasPrice, bumpPercent)).toString() };
37
54
  }
38
55
  }
39
56
  catch (error) {
40
- this.logger.warn('GasPriceOracle failed to fetch gas price; using fallback', error);
57
+ if (error instanceof exceptions_1.OracleError && error.code === 'NETWORK_MISMATCH') {
58
+ throw error;
59
+ }
60
+ this.logger.warn('Gas RPC request failed; bounded fallback selected', { code: 'RPC_REQUEST_FAILED' });
41
61
  }
62
+ return this.fallbackParams(input, bumpPercent);
63
+ }
64
+ fallbackParams(input, bumpPercent) {
42
65
  if (input.isLegacy) {
43
- const requestedSpeed = input.legacySpeed;
44
- const speed = requestedSpeed && Object.prototype.hasOwnProperty.call(this.fallback.legacy, requestedSpeed)
45
- ? requestedSpeed
46
- : 'fast';
47
- const bumpedLegacyGwei = this.bumpGwei(this.fallback.legacy[speed], bumpPercent);
48
- return { gasPrice: this.toWeiGwei(bumpedLegacyGwei).toString() };
66
+ const speed = input.legacySpeed ?? 'fast';
67
+ const gasPrice = this.cap(this.toWeiGwei(this.bumpGwei(this.fallback.legacy[speed], bumpPercent)));
68
+ return { gasPrice: gasPrice.toString() };
49
69
  }
50
- const bumpedMaxFeePerGasGwei = this.bumpGwei(this.fallback.eip1559.maxFeePerGas, bumpPercent);
51
- const bumpedMaxPriorityFeePerGasGwei = this.bumpGwei(this.fallback.eip1559.maxPriorityFeePerGas, bumpPercent);
70
+ const baseFeePerGas = this.cap(this.toWeiGwei(this.fallback.eip1559.baseFee));
71
+ const maxFeePerGas = this.cap(this.toWeiGwei(this.bumpGwei(this.fallback.eip1559.maxFeePerGas, bumpPercent)));
72
+ const priority = this.cap(this.toWeiGwei(this.bumpGwei(this.fallback.eip1559.maxPriorityFeePerGas, bumpPercent)));
52
73
  return {
53
- maxFeePerGas: this.toWeiGwei(bumpedMaxFeePerGasGwei).toString(),
54
- maxPriorityFeePerGas: this.toWeiGwei(bumpedMaxPriorityFeePerGasGwei).toString(),
74
+ baseFeePerGas: baseFeePerGas.toString(),
75
+ maxFeePerGas: maxFeePerGas.toString(),
76
+ maxPriorityFeePerGas: this.min(priority, maxFeePerGas).toString(),
55
77
  };
56
78
  }
57
- bump(value, percent) {
58
- if (!percent) {
59
- return value;
79
+ validateFallback() {
80
+ for (const [name, value] of Object.entries(this.fallback.legacy)) {
81
+ this.validateGwei(value, `fallbackGasPrices.legacy.${name}`);
82
+ }
83
+ for (const [name, value] of Object.entries(this.fallback.eip1559)) {
84
+ this.validateGwei(value, `fallbackGasPrices.eip1559.${name}`);
60
85
  }
61
- return (value * BigInt(100 + percent)) / 100n;
62
86
  }
63
- toWeiGwei(gwei) {
64
- if (!Number.isFinite(gwei) || gwei < 0) {
65
- throw new Error(`Invalid fallbackGasPrices gwei value: ${String(gwei)}. Expected a finite non-negative number.`);
87
+ validateGwei(value, name) {
88
+ if (!Number.isFinite(value) || value < 0 || value > DEFAULT_MAX_GAS_PRICE_GWEI) {
89
+ throw new exceptions_1.OracleError('INVALID_CONFIG', `${name} must be a finite number between 0 and ${String(DEFAULT_MAX_GAS_PRICE_GWEI)} gwei`);
66
90
  }
67
- return BigInt(Math.round(gwei * 1e9));
91
+ return value;
68
92
  }
69
- bumpGwei(gwei, percent) {
70
- if (!percent) {
71
- return gwei;
93
+ validateBump(value) {
94
+ if (!Number.isFinite(value) || value < 0 || value > 100) {
95
+ throw new exceptions_1.OracleError('INVALID_INPUT', 'bumpPercent must be a finite number between 0 and 100');
72
96
  }
73
- return (gwei * (100 + percent)) / 100;
97
+ return Math.floor(value);
98
+ }
99
+ bump(value, percent) {
100
+ return percent === 0 ? value : (value * BigInt(100 + percent)) / 100n;
101
+ }
102
+ bumpGwei(gwei, percent) {
103
+ return percent === 0 ? gwei : (gwei * (100 + percent)) / 100;
104
+ }
105
+ toWeiGwei(gwei) {
106
+ return BigInt(Math.round(gwei * 1e9));
107
+ }
108
+ cap(value) {
109
+ return this.min(value, this.maxGasPriceWei);
110
+ }
111
+ min(left, right) {
112
+ return left < right ? left : right;
74
113
  }
75
114
  }
76
115
  exports.GasPriceOracle = GasPriceOracle;
@@ -1 +1 @@
1
- {"version":3,"file":"GasPriceOracle.js","sourceRoot":"","sources":["../../../src/infrastructure/gas/GasPriceOracle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"GasPriceOracle.js","sourceRoot":"","sources":["../../../src/infrastructure/gas/GasPriceOracle.ts"],"names":[],"mappings":"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","sourcesContent":["import type { JsonRpcProvider } from 'ethers';\r\nimport type { ILogger } from '../../application/ports/ILogger';\r\nimport { NullLogger } from '../../application/ports/ILogger';\r\nimport { OracleError } from '../../domain/exceptions';\r\nimport { assertProviderNetwork, createVerifiedJsonRpcProvider } from '../blockchain/staticProvider';\r\nimport type { ChainId } from '../config/gasOracleConfigs';\r\n\r\nconst DEFAULT_MAX_GAS_PRICE_GWEI = 500;\r\n\r\nexport interface GasPriceParams {\r\n gasPrice?: string;\r\n baseFeePerGas?: string;\r\n maxFeePerGas?: string;\r\n maxPriorityFeePerGas?: string;\r\n}\r\n\r\nexport interface GetTxGasParamsInput {\r\n isLegacy?: boolean;\r\n bumpPercent?: number;\r\n legacySpeed?: 'instant' | 'fast' | 'standard' | 'low';\r\n}\r\n\r\nexport interface GasPriceOracleOptions {\r\n chainId: ChainId;\r\n rpcUrl: string;\r\n logger?: ILogger;\r\n rpcTimeoutMs?: number;\r\n maxGasPriceGwei?: number;\r\n fallbackGasPrices?: {\r\n legacy?: { instant: number; fast: number; standard: number; low: number };\r\n eip1559?: { baseFee: number; maxFeePerGas: number; maxPriorityFeePerGas: number };\r\n };\r\n}\r\n\r\nexport class GasPriceOracle {\r\n private readonly provider: JsonRpcProvider;\r\n private readonly logger: ILogger;\r\n private readonly chainId: number;\r\n private readonly maxGasPriceWei: bigint;\r\n private readonly fallback: Required<NonNullable<GasPriceOracleOptions['fallbackGasPrices']>>;\r\n\r\n constructor(options: GasPriceOracleOptions) {\r\n this.chainId = options.chainId;\r\n this.provider = createVerifiedJsonRpcProvider(\r\n options.rpcUrl,\r\n options.chainId,\r\n { batchMaxCount: 1 },\r\n options.rpcTimeoutMs,\r\n );\r\n this.logger = options.logger ?? new NullLogger();\r\n const maxGasPriceGwei = options.maxGasPriceGwei ?? DEFAULT_MAX_GAS_PRICE_GWEI;\r\n const validatedMaxGasPriceGwei = this.validateGwei(maxGasPriceGwei, 'maxGasPriceGwei');\r\n if (validatedMaxGasPriceGwei === 0) {\r\n throw new OracleError('INVALID_CONFIG', 'maxGasPriceGwei must be greater than 0');\r\n }\r\n this.maxGasPriceWei = this.toWeiGwei(validatedMaxGasPriceGwei);\r\n this.fallback = {\r\n legacy: options.fallbackGasPrices?.legacy ?? { instant: 35, fast: 25, standard: 20, low: 15 },\r\n eip1559: options.fallbackGasPrices?.eip1559 ?? {\r\n baseFee: 15,\r\n maxFeePerGas: 30,\r\n maxPriorityFeePerGas: 2,\r\n },\r\n };\r\n this.validateFallback();\r\n }\r\n\r\n async getTxGasParams(input: GetTxGasParamsInput = {}): Promise<GasPriceParams> {\r\n const bumpPercent = this.validateBump(input.bumpPercent ?? 0);\r\n\r\n try {\r\n await assertProviderNetwork(this.provider, this.chainId);\r\n const [feeData, block] = await Promise.all([\r\n this.provider.getFeeData(),\r\n this.provider.getBlock('latest'),\r\n ]);\r\n\r\n if (!input.isLegacy && feeData.maxFeePerGas && feeData.maxPriorityFeePerGas && block?.baseFeePerGas) {\r\n const baseFeePerGas = this.cap(block.baseFeePerGas);\r\n const maxPriorityFeePerGas = this.cap(this.bump(feeData.maxPriorityFeePerGas, bumpPercent));\r\n const maxFeePerGas = this.cap(this.bump(feeData.maxFeePerGas, bumpPercent));\r\n return {\r\n baseFeePerGas: baseFeePerGas.toString(),\r\n maxFeePerGas: maxFeePerGas.toString(),\r\n maxPriorityFeePerGas: this.min(maxPriorityFeePerGas, maxFeePerGas).toString(),\r\n };\r\n }\r\n\r\n if (feeData.gasPrice) {\r\n return { gasPrice: this.cap(this.bump(feeData.gasPrice, bumpPercent)).toString() };\r\n }\r\n } catch (error) {\r\n if (error instanceof OracleError && error.code === 'NETWORK_MISMATCH') {\r\n throw error;\r\n }\r\n this.logger.warn('Gas RPC request failed; bounded fallback selected', { code: 'RPC_REQUEST_FAILED' });\r\n }\r\n\r\n return this.fallbackParams(input, bumpPercent);\r\n }\r\n\r\n private fallbackParams(input: GetTxGasParamsInput, bumpPercent: number): GasPriceParams {\r\n if (input.isLegacy) {\r\n const speed = input.legacySpeed ?? 'fast';\r\n const gasPrice = this.cap(this.toWeiGwei(this.bumpGwei(this.fallback.legacy[speed], bumpPercent)));\r\n return { gasPrice: gasPrice.toString() };\r\n }\r\n\r\n const baseFeePerGas = this.cap(this.toWeiGwei(this.fallback.eip1559.baseFee));\r\n const maxFeePerGas = this.cap(\r\n this.toWeiGwei(this.bumpGwei(this.fallback.eip1559.maxFeePerGas, bumpPercent)),\r\n );\r\n const priority = this.cap(\r\n this.toWeiGwei(this.bumpGwei(this.fallback.eip1559.maxPriorityFeePerGas, bumpPercent)),\r\n );\r\n return {\r\n baseFeePerGas: baseFeePerGas.toString(),\r\n maxFeePerGas: maxFeePerGas.toString(),\r\n maxPriorityFeePerGas: this.min(priority, maxFeePerGas).toString(),\r\n };\r\n }\r\n\r\n private validateFallback(): void {\r\n for (const [name, value] of Object.entries(this.fallback.legacy)) {\r\n this.validateGwei(value, `fallbackGasPrices.legacy.${name}`);\r\n }\r\n for (const [name, value] of Object.entries(this.fallback.eip1559)) {\r\n this.validateGwei(value, `fallbackGasPrices.eip1559.${name}`);\r\n }\r\n }\r\n\r\n private validateGwei(value: number, name: string): number {\r\n if (!Number.isFinite(value) || value < 0 || value > DEFAULT_MAX_GAS_PRICE_GWEI) {\r\n throw new OracleError(\r\n 'INVALID_CONFIG',\r\n `${name} must be a finite number between 0 and ${String(DEFAULT_MAX_GAS_PRICE_GWEI)} gwei`,\r\n );\r\n }\r\n return value;\r\n }\r\n\r\n private validateBump(value: number): number {\r\n if (!Number.isFinite(value) || value < 0 || value > 100) {\r\n throw new OracleError('INVALID_INPUT', 'bumpPercent must be a finite number between 0 and 100');\r\n }\r\n return Math.floor(value);\r\n }\r\n\r\n private bump(value: bigint, percent: number): bigint {\r\n return percent === 0 ? value : (value * BigInt(100 + percent)) / 100n;\r\n }\r\n\r\n private bumpGwei(gwei: number, percent: number): number {\r\n return percent === 0 ? gwei : (gwei * (100 + percent)) / 100;\r\n }\r\n\r\n private toWeiGwei(gwei: number): bigint {\r\n return BigInt(Math.round(gwei * 1e9));\r\n }\r\n\r\n private cap(value: bigint): bigint {\r\n return this.min(value, this.maxGasPriceWei);\r\n }\r\n\r\n private min(left: bigint, right: bigint): bigint {\r\n return left < right ? left : right;\r\n }\r\n}\r\n"]}
@@ -1,10 +1,11 @@
1
- import { GasPriceOracle, GasPriceParams } from '../gas/GasPriceOracle';
1
+ import type { ILogger } from '../../application/ports/ILogger';
2
+ import { type ITokenPriceRepository } from '../../domain/repositories';
3
+ import { GasPriceOracle, type GasPriceParams } from '../gas/GasPriceOracle';
4
+ import type { ChainId } from '../config/gasOracleConfigs';
2
5
  import { TokenPriceOracle } from '../token-price/TokenPriceOracle';
3
- import { ITokenPriceRepository } from '../../domain/repositories';
4
- import { ChainId } from '../config/gasOracleConfigs';
5
- import { ILogger } from '../../application/ports/ILogger';
6
6
  export type MixerXTxType = 'relayer_withdrawal' | 'user_withdrawal' | 'relayer_withdrawal_check_v4';
7
- export type MixerXGasPurpose = 'fee_quote' | 'fee_validation' | 'send' | 'replacement';
7
+ export type MixerXGasPurpose = 'fee' | 'send' | 'replacement';
8
+ export type LegacyFeePurpose = 'fee_quote' | 'fee_validation';
8
9
  export interface MixerXFeeOracleOptions {
9
10
  chainId: ChainId;
10
11
  rpcUrl: string;
@@ -15,18 +16,24 @@ export interface MixerXFeeOracleOptions {
15
16
  logger?: ILogger;
16
17
  multicallAddress?: string;
17
18
  offchainOracleAddress?: string;
19
+ rpcTimeoutMs?: number;
20
+ maxGasPriceGwei?: number;
21
+ maxPriceAgeMs?: number;
22
+ maxRefundWei?: bigint;
18
23
  }
19
24
  export interface MixerXWithdrawalFeeInput {
20
25
  currency: string;
21
26
  amount: bigint;
22
27
  decimals: number;
28
+ /** Gas limit obtained from a simulation or eth_estimateGas for this exact transaction. */
29
+ gasLimit: number;
23
30
  refund?: string;
24
31
  txType?: MixerXTxType;
25
- purpose?: Extract<MixerXGasPurpose, 'fee_quote' | 'fee_validation'>;
32
+ purpose?: 'fee' | LegacyFeePurpose;
26
33
  }
27
34
  export interface MixerXGasParametersForTxInput {
28
35
  txType?: MixerXTxType;
29
- purpose?: MixerXGasPurpose;
36
+ purpose?: MixerXGasPurpose | LegacyFeePurpose;
30
37
  isLegacy?: boolean;
31
38
  }
32
39
  export interface MixerXGasParametersForTxResult extends GasPriceParams {
@@ -42,52 +49,40 @@ export interface MixerXFeeCalculationResult {
42
49
  currency: string;
43
50
  gasLimit: number;
44
51
  gasPriceWei: bigint;
52
+ baseFeePerGas?: string;
45
53
  maxFeePerGas?: string;
46
54
  maxPriorityFeePerGas?: string;
47
55
  gasPrice?: string;
56
+ tokenPriceInEth?: bigint;
57
+ priceSource: 'native' | 'repository';
58
+ priceObservedAtMs?: number;
48
59
  bumpPercent: number;
49
60
  txType: MixerXTxType;
50
- gasPurpose: Extract<MixerXGasPurpose, 'fee_quote' | 'fee_validation'>;
61
+ gasPurpose: 'fee';
51
62
  }
52
63
  export type WithdrawalFeeInput = MixerXWithdrawalFeeInput;
53
64
  export type FeeCalculationResult = MixerXFeeCalculationResult;
54
- /**
55
- * MixerXFeeOracle
56
- * Calculates protocol withdrawal fees and transaction gas parameters.
57
- *
58
- * Features:
59
- * - Explicit gas policy by purpose (quote/validation/send/replacement)
60
- * - Token price conversion (ETH <-> Token)
61
- * - Refund calculation for non-ETH tokens
62
- * - EIP-1559 and legacy support
63
- */
64
65
  export declare class MixerXFeeOracle {
65
- private chainId;
66
- private relayerFeePercent;
67
- private gasPriceOracle;
68
- private tokenPriceOracle?;
69
- private priceRepository?;
70
- private logger;
66
+ private readonly chainId;
67
+ private readonly relayerFeePartsPerMillion;
68
+ private readonly gasPriceOracle;
69
+ private readonly tokenPriceOracle;
70
+ private readonly priceRepository?;
71
+ private readonly logger;
72
+ private readonly maxPriceAgeMs;
73
+ private readonly maxRefundWei;
74
+ private readonly supportedCurrencies;
71
75
  constructor(options: MixerXFeeOracleOptions);
72
- /**
73
- * Calculate withdrawal fee using the fee quote/validation gas policy.
74
- */
75
76
  calculateWithdrawalFee(input: MixerXWithdrawalFeeInput): Promise<MixerXFeeCalculationResult>;
76
- /**
77
- * Get gas parameters for a protocol transaction and purpose.
78
- */
79
77
  getGasParametersForTx(input?: MixerXGasParametersForTxInput): Promise<MixerXGasParametersForTxResult>;
80
- private calculateGasCost;
81
- private calculateRelayerFee;
82
- private getGasLimit;
83
- private getBumpPercent;
84
- private extractGasPriceWei;
85
- private getTokenPrice;
86
- private withGasDetails;
87
- /**
88
- * Get gas parameters without protocol policy. Prefer getGasParametersForTx for relayer operations.
89
- */
78
+ assertReady(): Promise<void>;
90
79
  getGasParameters(isLegacy?: boolean): Promise<GasPriceParams>;
91
80
  getChainId(): ChainId;
81
+ private validateFeeInput;
82
+ private parseRefund;
83
+ private getFreshPrice;
84
+ private effectiveGasPrice;
85
+ private assertTotalFee;
86
+ private normalizePurpose;
87
+ private result;
92
88
  }
93
- //# sourceMappingURL=MixerXFeeOracle.d.ts.map