@mixerx/oracles 2.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.
- package/README.md +31 -19
- package/dist/application/ports/ILogger.js +0 -1
- package/dist/domain/config/TokenDefaults.js +0 -1
- package/dist/domain/exceptions.d.ts +1 -1
- package/dist/domain/exceptions.js +0 -1
- package/dist/domain/repositories.d.ts +1 -2
- package/dist/domain/repositories.js +0 -1
- package/dist/factory.d.ts +5 -15
- package/dist/factory.js +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +0 -1
- package/dist/infrastructure/blockchain/staticProvider.js +0 -1
- package/dist/infrastructure/config/ProtocolTokenConfig.js +5 -5
- package/dist/infrastructure/config/gasOracleConfigs.js +0 -1
- package/dist/infrastructure/gas/GasPriceOracle.d.ts +5 -18
- package/dist/infrastructure/gas/GasPriceOracle.js +51 -45
- package/dist/infrastructure/mixerx/MixerXFeeOracle.d.ts +16 -5
- package/dist/infrastructure/mixerx/MixerXFeeOracle.js +110 -38
- package/dist/infrastructure/multicall/MulticallProvider.d.ts +5 -1
- package/dist/infrastructure/multicall/MulticallProvider.js +10 -7
- package/dist/infrastructure/token-price/TokenPriceOracle.d.ts +3 -5
- package/dist/infrastructure/token-price/TokenPriceOracle.js +11 -10
- package/dist/types.d.ts +6 -1
- package/dist/types.js +0 -1
- package/package.json +62 -56
- package/dist/application/ports/ILogger.js.map +0 -1
- package/dist/application/ports/index.d.ts +0 -5
- package/dist/application/ports/index.js +0 -18
- package/dist/application/ports/index.js.map +0 -1
- package/dist/domain/config/TokenDefaults.js.map +0 -1
- package/dist/domain/config/index.d.ts +0 -1
- package/dist/domain/config/index.js +0 -8
- package/dist/domain/config/index.js.map +0 -1
- package/dist/domain/exceptions.js.map +0 -1
- package/dist/domain/repositories.js.map +0 -1
- package/dist/factory.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/infrastructure/blockchain/staticProvider.js.map +0 -1
- package/dist/infrastructure/config/ProtocolTokenConfig.js.map +0 -1
- package/dist/infrastructure/config/gasOracleConfigs.js.map +0 -1
- package/dist/infrastructure/config/index.d.ts +0 -4
- package/dist/infrastructure/config/index.js +0 -11
- package/dist/infrastructure/config/index.js.map +0 -1
- package/dist/infrastructure/gas/GasPriceOracle.js.map +0 -1
- package/dist/infrastructure/mixerx/MixerXFeeOracle.js.map +0 -1
- package/dist/infrastructure/multicall/MulticallProvider.js.map +0 -1
- package/dist/infrastructure/token-price/TokenPriceOracle.js.map +0 -1
- package/dist/infrastructure/token-price/index.d.ts +0 -3
- package/dist/infrastructure/token-price/index.js +0 -6
- package/dist/infrastructure/token-price/index.js.map +0 -1
- package/dist/types.js.map +0 -1
package/dist/factory.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":";;AAmCA,8DAyCC;AA3ED,yDAAyD;AAEzD,wEAAqE;AACrE,6EAA0E;AAC1E,oFAAiF;AA8BjF,SAAgB,yBAAyB,CAAC,MAA2B;IACnE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,IAAI,oBAAU,EAAE,CAAC;IACjD,MAAM,cAAc,GAAG,IAAI,+BAAc,CAAC;QACxC,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,MAAM;QACN,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;QAC3C,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,eAAe,EAAE,MAAM,CAAC,eAAe;KACxC,CAAC,CAAC;IACH,MAAM,gBAAgB,GAAG,IAAI,mCAAgB,CAC3C,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,OAAO,EACd,MAAM,CAAC,qBAAqB,EAC5B,MAAM,CAAC,gBAAgB,EACvB,SAAS,EACT,MAAM,EACN,MAAM,CAAC,YAAY,CACpB,CAAC;IACF,MAAM,eAAe,GAAG,IAAI,iCAAe,CAAC;QAC1C,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;QAC3C,cAAc;QACd,gBAAgB;QAChB,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,MAAM;QACN,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,YAAY,EAAE,MAAM,CAAC,YAAY;KAClC,CAAC,CAAC;IAEH,OAAO;QACL,cAAc;QACd,gBAAgB;QAChB,eAAe;QACf,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,WAAW,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,WAAW,EAAE;QACjD,WAAW,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,WAAW,EAAE;KACjD,CAAC;AACJ,CAAC","sourcesContent":["import type { ILogger } from './application/ports/ILogger';\r\nimport { NullLogger } from './application/ports/ILogger';\r\nimport type { ITokenPriceRepository } from './domain/repositories';\r\nimport { GasPriceOracle } from './infrastructure/gas/GasPriceOracle';\r\nimport { MixerXFeeOracle } from './infrastructure/mixerx/MixerXFeeOracle';\r\nimport { TokenPriceOracle } from './infrastructure/token-price/TokenPriceOracle';\r\nimport type { ChainId, TokenPrices } from './types';\r\n\r\nexport interface MixerxOraclesConfig {\r\n chainId: ChainId;\r\n rpcUrl: string;\r\n relayerFeePercent: number;\r\n priceRepository?: ITokenPriceRepository;\r\n multicallAddress?: string;\r\n offchainOracleAddress?: string;\r\n logger?: ILogger;\r\n rpcTimeoutMs?: number;\r\n maxGasPriceGwei?: number;\r\n maxPriceAgeMs?: number;\r\n maxRefundWei?: bigint;\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 interface IMixerxOraclesModule {\r\n gasPriceOracle: GasPriceOracle;\r\n tokenPriceOracle: TokenPriceOracle;\r\n mixerXFeeOracle: MixerXFeeOracle;\r\n fetchPrices(): Promise<TokenPrices>;\r\n assertReady(): Promise<void>;\r\n chainId: ChainId;\r\n}\r\n\r\nexport function createMixerxOraclesModule(config: MixerxOraclesConfig): IMixerxOraclesModule {\r\n const logger = config.logger ?? new NullLogger();\r\n const gasPriceOracle = new GasPriceOracle({\r\n chainId: config.chainId,\r\n rpcUrl: config.rpcUrl,\r\n logger,\r\n fallbackGasPrices: config.fallbackGasPrices,\r\n rpcTimeoutMs: config.rpcTimeoutMs,\r\n maxGasPriceGwei: config.maxGasPriceGwei,\r\n });\r\n const tokenPriceOracle = new TokenPriceOracle(\r\n config.rpcUrl,\r\n config.chainId,\r\n config.offchainOracleAddress,\r\n config.multicallAddress,\r\n undefined,\r\n logger,\r\n config.rpcTimeoutMs,\r\n );\r\n const mixerXFeeOracle = new MixerXFeeOracle({\r\n chainId: config.chainId,\r\n rpcUrl: config.rpcUrl,\r\n relayerFeePercent: config.relayerFeePercent,\r\n gasPriceOracle,\r\n tokenPriceOracle,\r\n priceRepository: config.priceRepository,\r\n logger,\r\n rpcTimeoutMs: config.rpcTimeoutMs,\r\n maxGasPriceGwei: config.maxGasPriceGwei,\r\n maxPriceAgeMs: config.maxPriceAgeMs,\r\n maxRefundWei: config.maxRefundWei,\r\n });\r\n\r\n return {\r\n gasPriceOracle,\r\n tokenPriceOracle,\r\n mixerXFeeOracle,\r\n chainId: config.chainId,\r\n fetchPrices: () => tokenPriceOracle.fetchPrices(),\r\n assertReady: () => mixerXFeeOracle.assertReady(),\r\n };\r\n}\r\n"]}
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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 +0,0 @@
|
|
|
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 +0,0 @@
|
|
|
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 +0,0 @@
|
|
|
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"]}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Infrastructure configuration exports
|
|
4
|
-
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.resolveOffchainOracleAddress = exports.resolveDefaultTokens = void 0;
|
|
7
|
-
// Protocol-derived configuration
|
|
8
|
-
var ProtocolTokenConfig_1 = require("./ProtocolTokenConfig");
|
|
9
|
-
Object.defineProperty(exports, "resolveDefaultTokens", { enumerable: true, get: function () { return ProtocolTokenConfig_1.resolveDefaultTokens; } });
|
|
10
|
-
Object.defineProperty(exports, "resolveOffchainOracleAddress", { enumerable: true, get: function () { return ProtocolTokenConfig_1.resolveOffchainOracleAddress; } });
|
|
11
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
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"]}
|
|
@@ -1 +0,0 @@
|
|
|
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 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MixerXFeeOracle.js","sourceRoot":"","sources":["../../../src/infrastructure/mixerx/MixerXFeeOracle.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ILogger } from '../../application/ports/ILogger';\r\nimport { NullLogger } from '../../application/ports/ILogger';\r\nimport { getBumpPercent } from '../../domain/config/TokenDefaults';\r\nimport { OracleError } from '../../domain/exceptions';\r\nimport { toPriceQuote, type ITokenPriceRepository } from '../../domain/repositories';\r\nimport type { TokenPriceQuote } from '../../types';\r\nimport { GasPriceOracle, type GasPriceParams } from '../gas/GasPriceOracle';\r\nimport type { ChainId } from '../config/gasOracleConfigs';\r\nimport { TokenPriceOracle } from '../token-price/TokenPriceOracle';\r\n\r\nconst DEFAULT_MAX_PRICE_AGE_MS = 120_000;\r\nconst DEFAULT_MAX_REFUND_WEI = 1_000_000_000_000_000_000n;\r\nconst FEE_SCALE = 1_000_000n;\r\n\r\nexport type MixerXTxType = 'relayer_withdrawal' | 'user_withdrawal' | 'relayer_withdrawal_check_v4';\r\nexport type MixerXGasPurpose = 'fee' | 'send' | 'replacement';\r\nexport type LegacyFeePurpose = 'fee_quote' | 'fee_validation';\r\n\r\nexport interface MixerXFeeOracleOptions {\r\n chainId: ChainId;\r\n rpcUrl: string;\r\n relayerFeePercent: number;\r\n gasPriceOracle?: GasPriceOracle;\r\n tokenPriceOracle?: TokenPriceOracle;\r\n priceRepository?: ITokenPriceRepository;\r\n logger?: ILogger;\r\n multicallAddress?: string;\r\n offchainOracleAddress?: string;\r\n rpcTimeoutMs?: number;\r\n maxGasPriceGwei?: number;\r\n maxPriceAgeMs?: number;\r\n maxRefundWei?: bigint;\r\n}\r\n\r\nexport interface MixerXWithdrawalFeeInput {\r\n currency: string;\r\n amount: bigint;\r\n decimals: number;\r\n /** Gas limit obtained from a simulation or eth_estimateGas for this exact transaction. */\r\n gasLimit: number;\r\n refund?: string;\r\n txType?: MixerXTxType;\r\n purpose?: 'fee' | LegacyFeePurpose;\r\n}\r\n\r\nexport interface MixerXGasParametersForTxInput {\r\n txType?: MixerXTxType;\r\n purpose?: MixerXGasPurpose | LegacyFeePurpose;\r\n isLegacy?: boolean;\r\n}\r\n\r\nexport interface MixerXGasParametersForTxResult extends GasPriceParams {\r\n txType: MixerXTxType;\r\n purpose: MixerXGasPurpose;\r\n bumpPercent: number;\r\n}\r\n\r\nexport interface MixerXFeeCalculationResult {\r\n gasCost: bigint;\r\n relayerFee: bigint;\r\n refundAmount: bigint;\r\n totalFee: bigint;\r\n currency: string;\r\n gasLimit: number;\r\n gasPriceWei: bigint;\r\n baseFeePerGas?: string;\r\n maxFeePerGas?: string;\r\n maxPriorityFeePerGas?: string;\r\n gasPrice?: string;\r\n tokenPriceInEth?: bigint;\r\n priceSource: 'native' | 'repository';\r\n priceObservedAtMs?: number;\r\n bumpPercent: number;\r\n txType: MixerXTxType;\r\n gasPurpose: 'fee';\r\n}\r\n\r\nexport type WithdrawalFeeInput = MixerXWithdrawalFeeInput;\r\nexport type FeeCalculationResult = MixerXFeeCalculationResult;\r\n\r\nexport class MixerXFeeOracle {\r\n private readonly chainId: ChainId;\r\n private readonly relayerFeePartsPerMillion: bigint;\r\n private readonly gasPriceOracle: GasPriceOracle;\r\n private readonly tokenPriceOracle: TokenPriceOracle;\r\n private readonly priceRepository?: ITokenPriceRepository;\r\n private readonly logger: ILogger;\r\n private readonly maxPriceAgeMs: number;\r\n private readonly maxRefundWei: bigint;\r\n private readonly supportedCurrencies: ReadonlySet<string>;\r\n\r\n constructor(options: MixerXFeeOracleOptions) {\r\n if (!Number.isFinite(options.relayerFeePercent) || options.relayerFeePercent < 0 || options.relayerFeePercent > 0.1) {\r\n throw new OracleError('INVALID_CONFIG', 'relayerFeePercent must be between 0 and 0.1');\r\n }\r\n this.maxPriceAgeMs = options.maxPriceAgeMs ?? DEFAULT_MAX_PRICE_AGE_MS;\r\n if (!Number.isInteger(this.maxPriceAgeMs) || this.maxPriceAgeMs < 1_000 || this.maxPriceAgeMs > 3_600_000) {\r\n throw new OracleError('INVALID_CONFIG', 'maxPriceAgeMs must be between 1000 and 3600000');\r\n }\r\n this.maxRefundWei = options.maxRefundWei ?? DEFAULT_MAX_REFUND_WEI;\r\n if (this.maxRefundWei < 0n) {\r\n throw new OracleError('INVALID_CONFIG', 'maxRefundWei cannot be negative');\r\n }\r\n\r\n this.chainId = options.chainId;\r\n this.relayerFeePartsPerMillion = BigInt(Math.round(options.relayerFeePercent * Number(FEE_SCALE)));\r\n this.logger = options.logger ?? new NullLogger();\r\n this.gasPriceOracle = options.gasPriceOracle ?? new GasPriceOracle({\r\n chainId: options.chainId,\r\n rpcUrl: options.rpcUrl,\r\n logger: this.logger,\r\n rpcTimeoutMs: options.rpcTimeoutMs,\r\n maxGasPriceGwei: options.maxGasPriceGwei,\r\n });\r\n this.tokenPriceOracle = options.tokenPriceOracle ?? new TokenPriceOracle(\r\n options.rpcUrl,\r\n options.chainId,\r\n options.offchainOracleAddress,\r\n options.multicallAddress,\r\n undefined,\r\n this.logger,\r\n options.rpcTimeoutMs,\r\n );\r\n this.supportedCurrencies = new Set([\r\n 'eth',\r\n ...this.tokenPriceOracle.getSupportedTokens().map((token) => token.symbol),\r\n ]);\r\n this.priceRepository = options.priceRepository;\r\n }\r\n\r\n async calculateWithdrawalFee(input: MixerXWithdrawalFeeInput): Promise<MixerXFeeCalculationResult> {\r\n const currency = this.validateFeeInput(input);\r\n const txType = input.txType ?? 'relayer_withdrawal';\r\n const gasParams = await this.getGasParametersForTx({\r\n txType,\r\n purpose: this.normalizePurpose(input.purpose ?? 'fee'),\r\n isLegacy: false,\r\n });\r\n const gasLimit = input.gasLimit;\r\n const gasPriceWei = this.effectiveGasPrice(gasParams);\r\n const gasCostWei = gasPriceWei * BigInt(gasLimit);\r\n const relayerFee = (input.amount * this.relayerFeePartsPerMillion) / FEE_SCALE;\r\n\r\n if (currency === 'eth') {\r\n if (input.refund !== undefined && this.parseRefund(input.refund) !== 0n) {\r\n throw new OracleError('INVALID_INPUT', 'refund must be zero for native ETH withdrawals');\r\n }\r\n const totalFee = gasCostWei + relayerFee;\r\n this.assertTotalFee(totalFee, input.amount);\r\n return this.result({\r\n gasCost: gasCostWei,\r\n relayerFee,\r\n refundAmount: 0n,\r\n totalFee,\r\n currency,\r\n gasLimit,\r\n gasPriceWei,\r\n gasParams,\r\n txType,\r\n priceSource: 'native',\r\n });\r\n }\r\n\r\n const quote = await this.getFreshPrice(currency);\r\n const decimalsFactor = 10n ** BigInt(input.decimals);\r\n const gasCost = (gasCostWei * decimalsFactor) / quote.priceInEthWei;\r\n const refundWei = this.parseRefund(input.refund ?? '0');\r\n const refundAmount = (refundWei * decimalsFactor) / quote.priceInEthWei;\r\n const totalFee = gasCost + relayerFee + refundAmount;\r\n this.assertTotalFee(totalFee, input.amount);\r\n\r\n return this.result({\r\n gasCost,\r\n relayerFee,\r\n refundAmount,\r\n totalFee,\r\n currency,\r\n gasLimit,\r\n gasPriceWei,\r\n gasParams,\r\n txType,\r\n tokenPriceInEth: quote.priceInEthWei,\r\n priceSource: quote.source,\r\n priceObservedAtMs: quote.observedAtMs,\r\n });\r\n }\r\n\r\n async getGasParametersForTx(\r\n input: MixerXGasParametersForTxInput = {},\r\n ): Promise<MixerXGasParametersForTxResult> {\r\n const txType = input.txType ?? 'relayer_withdrawal';\r\n const purpose = this.normalizePurpose(input.purpose ?? 'send');\r\n const bumpPercent = purpose === 'send' ? 0 : getBumpPercent(txType);\r\n const gasParams = await this.gasPriceOracle.getTxGasParams({\r\n isLegacy: input.isLegacy ?? false,\r\n bumpPercent,\r\n legacySpeed: 'fast',\r\n });\r\n return { ...gasParams, txType, purpose, bumpPercent };\r\n }\r\n\r\n async assertReady(): Promise<void> {\r\n await this.tokenPriceOracle.fetchPrices();\r\n }\r\n\r\n async getGasParameters(isLegacy: boolean = false): Promise<GasPriceParams> {\r\n return this.gasPriceOracle.getTxGasParams({ isLegacy });\r\n }\r\n\r\n getChainId(): ChainId {\r\n return this.chainId;\r\n }\r\n\r\n private validateFeeInput(input: MixerXWithdrawalFeeInput): string {\r\n const currency = input.currency.trim().toLowerCase();\r\n if (!/^[a-z0-9]{2,16}$/.test(currency)) {\r\n throw new OracleError('INVALID_INPUT', 'currency must be a valid protocol token symbol');\r\n }\r\n if (!this.supportedCurrencies.has(currency)) {\r\n throw new OracleError('INVALID_INPUT', `Unsupported MixerX currency: ${currency}`);\r\n }\r\n if (input.amount <= 0n) {\r\n throw new OracleError('INVALID_INPUT', 'amount must be greater than zero');\r\n }\r\n if (!Number.isInteger(input.decimals) || input.decimals < 0 || input.decimals > 255) {\r\n throw new OracleError('INVALID_INPUT', 'decimals must be an integer between 0 and 255');\r\n }\r\n if (!Number.isInteger(input.gasLimit) || input.gasLimit < 21_000 || input.gasLimit > 5_000_000) {\r\n throw new OracleError('INVALID_INPUT', 'gasLimit must be an integer between 21000 and 5000000');\r\n }\r\n return currency;\r\n }\r\n\r\n private parseRefund(raw: string): bigint {\r\n if (!/^\\d+$/.test(raw)) {\r\n throw new OracleError('INVALID_INPUT', 'refund must be an unsigned base-10 integer string');\r\n }\r\n const refund = BigInt(raw);\r\n if (refund > this.maxRefundWei) {\r\n throw new OracleError('ECONOMIC_LIMIT_EXCEEDED', 'refund exceeds maxRefundWei');\r\n }\r\n return refund;\r\n }\r\n\r\n private async getFreshPrice(symbol: string): Promise<TokenPriceQuote> {\r\n if (!this.priceRepository) {\r\n throw new OracleError('TOKEN_PRICE_UNAVAILABLE', `No fresh price repository configured for ${symbol}`);\r\n }\r\n const [price, observedAtMs] = await Promise.all([\r\n this.priceRepository.getPrice(symbol),\r\n this.priceRepository.getLastUpdateTimestamp(symbol),\r\n ]);\r\n const quote = toPriceQuote(price, observedAtMs);\r\n if (!quote) {\r\n throw new OracleError('TOKEN_PRICE_UNAVAILABLE', `Price unavailable for ${symbol}`);\r\n }\r\n if (Date.now() - quote.observedAtMs > this.maxPriceAgeMs || quote.observedAtMs > Date.now() + 5_000) {\r\n throw new OracleError('TOKEN_PRICE_STALE', `Price is stale for ${symbol}`);\r\n }\r\n return quote;\r\n }\r\n\r\n private effectiveGasPrice(params: GasPriceParams): bigint {\r\n if (params.gasPrice) {\r\n return BigInt(params.gasPrice);\r\n }\r\n if (params.baseFeePerGas && params.maxFeePerGas && params.maxPriorityFeePerGas) {\r\n const maxFee = BigInt(params.maxFeePerGas);\r\n const likelyEffective = BigInt(params.baseFeePerGas) + BigInt(params.maxPriorityFeePerGas);\r\n return likelyEffective < maxFee ? likelyEffective : maxFee;\r\n }\r\n throw new OracleError('RPC_REQUEST_FAILED', 'Gas price response is incomplete');\r\n }\r\n\r\n private assertTotalFee(totalFee: bigint, amount: bigint): void {\r\n if (totalFee < 0n || totalFee > amount) {\r\n throw new OracleError('ECONOMIC_LIMIT_EXCEEDED', 'Total fee must be between zero and withdrawal amount');\r\n }\r\n }\r\n\r\n private normalizePurpose(purpose: MixerXGasPurpose | LegacyFeePurpose): MixerXGasPurpose {\r\n if (purpose === 'fee_quote' || purpose === 'fee_validation') {\r\n return 'fee';\r\n }\r\n return purpose;\r\n }\r\n\r\n private result(input: {\r\n gasCost: bigint;\r\n relayerFee: bigint;\r\n refundAmount: bigint;\r\n totalFee: bigint;\r\n currency: string;\r\n gasLimit: number;\r\n gasPriceWei: bigint;\r\n gasParams: MixerXGasParametersForTxResult;\r\n txType: MixerXTxType;\r\n tokenPriceInEth?: bigint;\r\n priceSource: 'native' | 'repository';\r\n priceObservedAtMs?: number;\r\n }): MixerXFeeCalculationResult {\r\n this.logger.debug('Withdrawal fee calculated', {\r\n priceSource: input.priceSource,\r\n gasPurpose: 'fee',\r\n });\r\n return {\r\n gasCost: input.gasCost,\r\n relayerFee: input.relayerFee,\r\n refundAmount: input.refundAmount,\r\n totalFee: input.totalFee,\r\n currency: input.currency,\r\n gasLimit: input.gasLimit,\r\n gasPriceWei: input.gasPriceWei,\r\n baseFeePerGas: input.gasParams.baseFeePerGas,\r\n maxFeePerGas: input.gasParams.maxFeePerGas,\r\n maxPriorityFeePerGas: input.gasParams.maxPriorityFeePerGas,\r\n gasPrice: input.gasParams.gasPrice,\r\n tokenPriceInEth: input.tokenPriceInEth,\r\n priceSource: input.priceSource,\r\n priceObservedAtMs: input.priceObservedAtMs,\r\n bumpPercent: input.gasParams.bumpPercent,\r\n txType: input.txType,\r\n gasPurpose: 'fee',\r\n };\r\n }\r\n}\r\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MulticallProvider.js","sourceRoot":"","sources":["../../../src/infrastructure/multicall/MulticallProvider.ts"],"names":[],"mappings":";;;AAAA,mCAAgC;AAChC,wDAAsD;AAEtD,iEAGsC;AACtC,iEAAkE;AAElE,MAAM,kBAAkB,GAAG,4CAA4C,CAAC;AACxE,MAAM,SAAS,GAAG,EAAE,CAAC;AACrB,MAAM,cAAc,GAAG;IACrB,4JAA4J;CACpJ,CAAC;AAYX,MAAa,iBAAiB;IACX,QAAQ,CAAyB;IACjC,SAAS,CAAkB;IAC3B,OAAO,CAAS;IAEjC,YACE,MAAc,EACd,mBAA2B,kBAAkB,EAC7C,OAAyB,EACzB,YAAqB;QAErB,IAAA,uCAAoB,EAAC,OAAO,CAAC,CAAC;QAC9B,IAAI,CAAC,eAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACxC,MAAM,IAAI,wBAAW,CAAC,gBAAgB,EAAE,8CAA8C,CAAC,CAAC;QAC1F,CAAC;QAED,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,QAAQ,GAAG,IAAA,8CAA6B,EAAC,MAAM,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;QACnG,IAAI,CAAC,SAAS,GAAG,IAAI,eAAM,CAAC,QAAQ,CAAC,gBAAgB,EAAE,cAAc,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxF,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,KAAsB;QACpC,IAAI,KAAK,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;YAC7B,MAAM,IAAI,wBAAW,CAAC,eAAe,EAAE,8BAA8B,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAClG,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,eAAM,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,eAAM,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACzE,MAAM,IAAI,wBAAW,CAAC,eAAe,EAAE,yDAAyD,CAAC,CAAC;YACpG,CAAC;QACH,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAA,sCAAqB,EAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YACzD,MAAM,UAAU,GAAG,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,UAAU,CAC5D,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,CACvD,CAAwD,CAAC;YAE1D,IAAI,UAAU,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;gBACvC,MAAM,IAAI,wBAAW,CAAC,oBAAoB,EAAE,oDAAoD,CAAC,CAAC;YACpG,CAAC;YAED,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;gBACjC,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,UAAU,EAAE,MAAM,CAAC,UAAU;aAC9B,CAAC,CAAC,CAAC;QACN,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,wBAAW,EAAE,CAAC;gBACjC,MAAM,KAAK,CAAC;YACd,CAAC;YACD,MAAM,IAAI,wBAAW,CAAC,oBAAoB,EAAE,8BAA8B,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;CACF;AAxDD,8CAwDC","sourcesContent":["import { ethers } from 'ethers';\r\nimport { OracleError } from '../../domain/exceptions';\r\nimport type { ChainId } from '../../types';\r\nimport {\r\n assertProviderNetwork,\r\n createVerifiedJsonRpcProvider,\r\n} from '../blockchain/staticProvider';\r\nimport { assertSepoliaChainId } from '../config/gasOracleConfigs';\r\n\r\nconst MULTICALL3_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11';\r\nconst MAX_CALLS = 64;\r\nconst MULTICALL3_ABI = [\r\n 'function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)',\r\n] as const;\r\n\r\nexport interface Call {\r\n target: string;\r\n callData: string;\r\n}\r\n\r\nexport interface CallResult {\r\n success: boolean;\r\n returnData: string;\r\n}\r\n\r\nexport class MulticallProvider {\r\n private readonly provider: ethers.JsonRpcProvider;\r\n private readonly multicall: ethers.Contract;\r\n private readonly chainId: number;\r\n\r\n constructor(\r\n rpcUrl: string,\r\n multicallAddress: string = MULTICALL3_ADDRESS,\r\n chainId: ChainId | number,\r\n rpcTimeoutMs?: number,\r\n ) {\r\n assertSepoliaChainId(chainId);\r\n if (!ethers.isAddress(multicallAddress)) {\r\n throw new OracleError('INVALID_CONFIG', 'multicallAddress must be a valid EVM address');\r\n }\r\n\r\n this.chainId = chainId;\r\n this.provider = createVerifiedJsonRpcProvider(rpcUrl, chainId, { batchMaxCount: 1 }, rpcTimeoutMs);\r\n this.multicall = new ethers.Contract(multicallAddress, MULTICALL3_ABI, this.provider);\r\n }\r\n\r\n async aggregate(calls: readonly Call[]): Promise<CallResult[]> {\r\n if (calls.length > MAX_CALLS) {\r\n throw new OracleError('INVALID_INPUT', `Multicall supports at most ${String(MAX_CALLS)} calls`);\r\n }\r\n if (calls.length === 0) {\r\n return [];\r\n }\r\n\r\n for (const call of calls) {\r\n if (!ethers.isAddress(call.target) || !ethers.isHexString(call.callData)) {\r\n throw new OracleError('INVALID_INPUT', 'Every multicall target and calldata value must be valid');\r\n }\r\n }\r\n\r\n try {\r\n await assertProviderNetwork(this.provider, this.chainId);\r\n const rawResults = (await this.multicall.aggregate3.staticCall(\r\n calls.map((call) => ({ ...call, allowFailure: true })),\r\n )) as readonly { success: boolean; returnData: string }[];\r\n\r\n if (rawResults.length !== calls.length) {\r\n throw new OracleError('RPC_REQUEST_FAILED', 'Multicall returned an unexpected number of results');\r\n }\r\n\r\n return rawResults.map((result) => ({\r\n success: result.success,\r\n returnData: result.returnData,\r\n }));\r\n } catch (error) {\r\n if (error instanceof OracleError) {\r\n throw error;\r\n }\r\n throw new OracleError('RPC_REQUEST_FAILED', 'Multicall RPC request failed');\r\n }\r\n }\r\n}\r\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TokenPriceOracle.js","sourceRoot":"","sources":["../../../src/infrastructure/token-price/TokenPriceOracle.ts"],"names":[],"mappings":"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","sourcesContent":["import { ethers } from 'ethers';\r\nimport type { ILogger } from '../../application/ports/ILogger';\r\nimport { NullLogger } from '../../application/ports/ILogger';\r\nimport { OracleError, TokenPriceError } from '../../domain/exceptions';\r\nimport type { ChainId, Token, TokenPrices } from '../../types';\r\nimport { resolveDefaultTokens, resolveOffchainOracleAddress } from '../config/ProtocolTokenConfig';\r\nimport { MulticallProvider, type Call } from '../multicall/MulticallProvider';\r\n\r\nconst MAX_TOKENS = 32;\r\nconst OFFCHAIN_ORACLE_ABI = [\r\n 'function getRateToEth(address srcToken, bool useSrcWrappers) external view returns (uint256 weightedRate)',\r\n] as const;\r\n\r\nexport class TokenPriceOracle {\r\n private readonly multicall: MulticallProvider;\r\n private readonly offchainOracleAddress: string;\r\n private readonly interface = new ethers.Interface(OFFCHAIN_ORACLE_ABI);\r\n private readonly logger: ILogger;\r\n private defaultTokens: Token[];\r\n\r\n constructor(\r\n rpcUrl: string,\r\n chainId: ChainId | number,\r\n offchainOracleAddress?: string,\r\n multicallAddress?: string,\r\n tokens?: Token[],\r\n logger?: ILogger,\r\n rpcTimeoutMs?: number,\r\n ) {\r\n this.offchainOracleAddress = offchainOracleAddress ?? resolveOffchainOracleAddress(chainId);\r\n if (!ethers.isAddress(this.offchainOracleAddress)) {\r\n throw new OracleError('INVALID_CONFIG', 'offchainOracleAddress must be a valid EVM address');\r\n }\r\n\r\n this.defaultTokens = this.normalizeTokens(tokens ?? resolveDefaultTokens(chainId));\r\n this.multicall = new MulticallProvider(rpcUrl, multicallAddress, chainId, rpcTimeoutMs);\r\n this.logger = logger ?? new NullLogger();\r\n }\r\n\r\n async fetchPrices(tokens: readonly Token[] = this.defaultTokens): Promise<TokenPrices> {\r\n const normalizedTokens = this.normalizeTokens(tokens);\r\n const calls: Call[] = normalizedTokens.map((token) => ({\r\n target: this.offchainOracleAddress,\r\n callData: this.interface.encodeFunctionData('getRateToEth', [token.address, true]),\r\n }));\r\n const results = await this.multicall.aggregate(calls);\r\n const prices = Object.create(null) as TokenPrices;\r\n const failedSymbols: string[] = [];\r\n\r\n for (const [index, result] of results.entries()) {\r\n const token = normalizedTokens[index];\r\n if (!result.success) {\r\n failedSymbols.push(token.symbol);\r\n continue;\r\n }\r\n\r\n try {\r\n const rateWei = this.interface.decodeFunctionResult('getRateToEth', result.returnData)[0] as bigint;\r\n const price = (rateWei * 10n ** BigInt(token.decimals)) / 10n ** 18n;\r\n if (price <= 0n) {\r\n failedSymbols.push(token.symbol);\r\n continue;\r\n }\r\n prices[token.symbol] = price.toString();\r\n } catch {\r\n failedSymbols.push(token.symbol);\r\n }\r\n }\r\n\r\n if (failedSymbols.length > 0) {\r\n this.logger.warn('Token price batch rejected', { failedSymbols: [...failedSymbols] });\r\n throw new TokenPriceError('One or more on-chain token prices are unavailable', failedSymbols);\r\n }\r\n\r\n return { ...prices };\r\n }\r\n\r\n async fetchPrice(token: Token): Promise<string> {\r\n const prices = await this.fetchPrices([token]);\r\n return prices[token.symbol.trim().toLowerCase()];\r\n }\r\n\r\n getSupportedTokens(): Token[] {\r\n return this.defaultTokens.map((token) => ({ ...token }));\r\n }\r\n\r\n addTokens(tokens: readonly Token[]): void {\r\n this.defaultTokens = this.normalizeTokens([...this.defaultTokens, ...tokens]);\r\n }\r\n\r\n private normalizeTokens(tokens: readonly Token[]): Token[] {\r\n if (tokens.length === 0) {\r\n throw new OracleError('INVALID_CONFIG', `Token list must contain between 1 and ${String(MAX_TOKENS)} items`);\r\n }\r\n\r\n const byAddress = new Map<string, Token>();\r\n const symbols = new Set<string>();\r\n for (const token of tokens) {\r\n const address = token.address.trim().toLowerCase();\r\n const symbol = token.symbol.trim().toLowerCase();\r\n if (!ethers.isAddress(address) || !/^[a-z0-9]{2,16}$/.test(symbol)) {\r\n throw new OracleError('INVALID_CONFIG', 'Token address or symbol is invalid');\r\n }\r\n if (!Number.isInteger(token.decimals) || token.decimals < 0 || token.decimals > 255) {\r\n throw new OracleError('INVALID_CONFIG', `Invalid decimals for token ${symbol}`);\r\n }\r\n if (symbols.has(symbol) && byAddress.get(address)?.symbol !== symbol) {\r\n throw new OracleError('INVALID_CONFIG', `Duplicate token symbol: ${symbol}`);\r\n }\r\n\r\n symbols.add(symbol);\r\n byAddress.set(address, { address, symbol, decimals: token.decimals });\r\n }\r\n\r\n if (byAddress.size > MAX_TOKENS) {\r\n throw new OracleError('INVALID_CONFIG', `Token list exceeds ${String(MAX_TOKENS)} unique addresses`);\r\n }\r\n return [...byAddress.values()];\r\n }\r\n}\r\n"]}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TokenPriceOracle = void 0;
|
|
4
|
-
var TokenPriceOracle_1 = require("./TokenPriceOracle");
|
|
5
|
-
Object.defineProperty(exports, "TokenPriceOracle", { enumerable: true, get: function () { return TokenPriceOracle_1.TokenPriceOracle; } });
|
|
6
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/infrastructure/token-price/index.ts"],"names":[],"mappings":";;;AAAA,uDAAsD;AAA7C,oHAAA,gBAAgB,OAAA","sourcesContent":["export { TokenPriceOracle } from './TokenPriceOracle';\r\nexport type { TokenPrices } from '../../types';\r\nexport type { Token } from '../../types';\r\n"]}
|
package/dist/types.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["export type ChainId = number;\r\n\r\nexport interface Token {\r\n address: string;\r\n symbol: string;\r\n decimals: number;\r\n}\r\n\r\nexport interface TokenPrices {\r\n [symbol: string]: string;\r\n}\r\n\r\nexport interface TokenPriceQuote {\r\n priceInEthWei: bigint;\r\n observedAtMs: number;\r\n source: 'repository';\r\n}\r\n"]}
|