@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.
- package/README.md +76 -109
- package/dist/application/ports/ILogger.d.ts +0 -1
- package/dist/application/ports/ILogger.js.map +1 -1
- package/dist/application/ports/index.d.ts +0 -1
- package/dist/application/ports/index.js.map +1 -1
- package/dist/domain/config/TokenDefaults.d.ts +0 -32
- package/dist/domain/config/TokenDefaults.js +7 -74
- package/dist/domain/config/TokenDefaults.js.map +1 -1
- package/dist/domain/config/index.d.ts +1 -2
- package/dist/domain/config/index.js +1 -13
- package/dist/domain/config/index.js.map +1 -1
- package/dist/domain/exceptions.d.ts +8 -4
- package/dist/domain/exceptions.js +12 -3
- package/dist/domain/exceptions.js.map +1 -1
- package/dist/domain/repositories.d.ts +3 -1
- package/dist/domain/repositories.js +10 -0
- package/dist/domain/repositories.js.map +1 -1
- package/dist/factory.d.ts +10 -31
- package/dist/factory.js +14 -24
- package/dist/factory.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/blockchain/staticProvider.d.ts +2 -3
- package/dist/infrastructure/blockchain/staticProvider.js +33 -8
- package/dist/infrastructure/blockchain/staticProvider.js.map +1 -1
- package/dist/infrastructure/config/ProtocolTokenConfig.d.ts +0 -1
- package/dist/infrastructure/config/ProtocolTokenConfig.js +2 -0
- package/dist/infrastructure/config/ProtocolTokenConfig.js.map +1 -1
- package/dist/infrastructure/config/gasOracleConfigs.d.ts +2 -13
- package/dist/infrastructure/config/gasOracleConfigs.js +7 -33
- package/dist/infrastructure/config/gasOracleConfigs.js.map +1 -1
- package/dist/infrastructure/config/index.d.ts +0 -1
- package/dist/infrastructure/config/index.js.map +1 -1
- package/dist/infrastructure/gas/GasPriceOracle.d.ts +12 -2
- package/dist/infrastructure/gas/GasPriceOracle.js +76 -37
- package/dist/infrastructure/gas/GasPriceOracle.js.map +1 -1
- package/dist/infrastructure/mixerx/MixerXFeeOracle.d.ts +36 -41
- package/dist/infrastructure/mixerx/MixerXFeeOracle.js +146 -151
- package/dist/infrastructure/mixerx/MixerXFeeOracle.js.map +1 -1
- package/dist/infrastructure/multicall/MulticallProvider.d.ts +4 -3
- package/dist/infrastructure/multicall/MulticallProvider.js +44 -9
- package/dist/infrastructure/multicall/MulticallProvider.js.map +1 -1
- package/dist/infrastructure/token-price/TokenPriceOracle.d.ts +11 -45
- package/dist/infrastructure/token-price/TokenPriceOracle.js +68 -100
- package/dist/infrastructure/token-price/TokenPriceOracle.js.map +1 -1
- package/dist/infrastructure/token-price/index.d.ts +2 -2
- package/dist/infrastructure/token-price/index.js.map +1 -1
- package/dist/types.d.ts +5 -1
- package/dist/types.js.map +1 -1
- package/package.json +56 -41
- package/dist/application/ports/ILogger.d.ts.map +0 -1
- package/dist/application/ports/index.d.ts.map +0 -1
- package/dist/application/services/TokenPriceService.d.ts +0 -60
- package/dist/application/services/TokenPriceService.d.ts.map +0 -1
- package/dist/application/services/TokenPriceService.js +0 -137
- package/dist/application/services/TokenPriceService.js.map +0 -1
- package/dist/domain/config/TokenDefaults.d.ts.map +0 -1
- package/dist/domain/config/index.d.ts.map +0 -1
- package/dist/domain/exceptions.d.ts.map +0 -1
- package/dist/domain/repositories.d.ts.map +0 -1
- package/dist/factory.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/infrastructure/blockchain/contracts.d.ts +0 -20
- package/dist/infrastructure/blockchain/contracts.d.ts.map +0 -1
- package/dist/infrastructure/blockchain/contracts.js +0 -10
- package/dist/infrastructure/blockchain/contracts.js.map +0 -1
- package/dist/infrastructure/blockchain/staticProvider.d.ts.map +0 -1
- package/dist/infrastructure/config/ProtocolTokenConfig.d.ts.map +0 -1
- package/dist/infrastructure/config/gasOracleConfigs.d.ts.map +0 -1
- package/dist/infrastructure/config/index.d.ts.map +0 -1
- package/dist/infrastructure/gas/GasPriceOracle.d.ts.map +0 -1
- package/dist/infrastructure/mixerx/MixerXFeeOracle.d.ts.map +0 -1
- package/dist/infrastructure/multicall/MulticallProvider.d.ts.map +0 -1
- package/dist/infrastructure/token-price/TokenPriceOracle.d.ts.map +0 -1
- package/dist/infrastructure/token-price/index.d.ts.map +0 -1
- package/dist/types.d.ts.map +0 -1
|
@@ -2,130 +2,98 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TokenPriceOracle = void 0;
|
|
4
4
|
const ethers_1 = require("ethers");
|
|
5
|
-
const MulticallProvider_1 = require("../multicall/MulticallProvider");
|
|
6
|
-
const TokenDefaults_1 = require("../../domain/config/TokenDefaults");
|
|
7
5
|
const ILogger_1 = require("../../application/ports/ILogger");
|
|
6
|
+
const exceptions_1 = require("../../domain/exceptions");
|
|
8
7
|
const ProtocolTokenConfig_1 = require("../config/ProtocolTokenConfig");
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* Returns price rate to ETH in wei (how much ETH 1 token = X wei)
|
|
15
|
-
*
|
|
16
|
-
* NOTE: Uses centralized TokenDefaults to avoid duplication
|
|
17
|
-
*/
|
|
8
|
+
const MulticallProvider_1 = require("../multicall/MulticallProvider");
|
|
9
|
+
const MAX_TOKENS = 32;
|
|
10
|
+
const OFFCHAIN_ORACLE_ABI = [
|
|
11
|
+
'function getRateToEth(address srcToken, bool useSrcWrappers) external view returns (uint256 weightedRate)',
|
|
12
|
+
];
|
|
18
13
|
class TokenPriceOracle {
|
|
19
14
|
multicall;
|
|
20
15
|
offchainOracleAddress;
|
|
21
|
-
|
|
22
|
-
'function getRateToEth(address srcToken, bool useSrcWrappers) external view returns (uint256 weightedRate)',
|
|
23
|
-
];
|
|
24
|
-
// Default tokens from @mixerx/config by chain
|
|
25
|
-
defaultTokens;
|
|
26
|
-
// Default prices as fallback (in wei) - from TokenDefaults
|
|
27
|
-
defaultPrices = TokenDefaults_1.DEFAULT_TOKEN_PRICES;
|
|
16
|
+
interface = new ethers_1.ethers.Interface(OFFCHAIN_ORACLE_ABI);
|
|
28
17
|
logger;
|
|
29
|
-
|
|
30
|
-
|
|
18
|
+
defaultTokens;
|
|
19
|
+
constructor(rpcUrl, chainId, offchainOracleAddress, multicallAddress, tokens, logger, rpcTimeoutMs) {
|
|
31
20
|
this.offchainOracleAddress = offchainOracleAddress ?? (0, ProtocolTokenConfig_1.resolveOffchainOracleAddress)(chainId);
|
|
32
|
-
|
|
21
|
+
if (!ethers_1.ethers.isAddress(this.offchainOracleAddress)) {
|
|
22
|
+
throw new exceptions_1.OracleError('INVALID_CONFIG', 'offchainOracleAddress must be a valid EVM address');
|
|
23
|
+
}
|
|
24
|
+
this.defaultTokens = this.normalizeTokens(tokens ?? (0, ProtocolTokenConfig_1.resolveDefaultTokens)(chainId));
|
|
25
|
+
this.multicall = new MulticallProvider_1.MulticallProvider(rpcUrl, multicallAddress, chainId, rpcTimeoutMs);
|
|
33
26
|
this.logger = logger ?? new ILogger_1.NullLogger();
|
|
34
27
|
}
|
|
35
|
-
/**
|
|
36
|
-
* Fetch prices for tokens
|
|
37
|
-
* Uses multicall to batch requests
|
|
38
|
-
*/
|
|
39
28
|
async fetchPrices(tokens = this.defaultTokens) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
29
|
+
const normalizedTokens = this.normalizeTokens(tokens);
|
|
30
|
+
const calls = normalizedTokens.map((token) => ({
|
|
31
|
+
target: this.offchainOracleAddress,
|
|
32
|
+
callData: this.interface.encodeFunctionData('getRateToEth', [token.address, true]),
|
|
33
|
+
}));
|
|
34
|
+
const results = await this.multicall.aggregate(calls);
|
|
35
|
+
const prices = Object.create(null);
|
|
36
|
+
const failedSymbols = [];
|
|
37
|
+
for (const [index, result] of results.entries()) {
|
|
38
|
+
const token = normalizedTokens[index];
|
|
39
|
+
if (!result.success) {
|
|
40
|
+
failedSymbols.push(token.symbol);
|
|
41
|
+
continue;
|
|
43
42
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
// Execute multicall
|
|
50
|
-
const results = await this.multicall.aggregate(calls);
|
|
51
|
-
const prices = {};
|
|
52
|
-
for (let i = 0; i < results.length; i++) {
|
|
53
|
-
const token = tokens[i];
|
|
54
|
-
const result = results[i];
|
|
55
|
-
if (!result.success) {
|
|
56
|
-
// Use default price if available
|
|
57
|
-
const defaultPrice = this.defaultPrices[token.symbol.toLowerCase()];
|
|
58
|
-
if (defaultPrice) {
|
|
59
|
-
prices[token.symbol.toLowerCase()] = defaultPrice;
|
|
60
|
-
this.logger.warn('TokenPriceOracle default price used (call failed)', {
|
|
61
|
-
symbol: token.symbol,
|
|
62
|
-
address: token.address,
|
|
63
|
-
});
|
|
64
|
-
}
|
|
43
|
+
try {
|
|
44
|
+
const rateWei = this.interface.decodeFunctionResult('getRateToEth', result.returnData)[0];
|
|
45
|
+
const price = (rateWei * 10n ** BigInt(token.decimals)) / 10n ** 18n;
|
|
46
|
+
if (price <= 0n) {
|
|
47
|
+
failedSymbols.push(token.symbol);
|
|
65
48
|
continue;
|
|
66
49
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
// Formula: (rate * 10^tokenDecimals) / 10^18
|
|
72
|
-
const tokenDecimals = 10n ** BigInt(token.decimals);
|
|
73
|
-
const ethDecimals = 10n ** 18n;
|
|
74
|
-
const price = (rateWei * tokenDecimals) / ethDecimals;
|
|
75
|
-
prices[token.symbol.toLowerCase()] = price.toString();
|
|
76
|
-
}
|
|
77
|
-
catch (decodeError) {
|
|
78
|
-
// Use default if decoding fails
|
|
79
|
-
const defaultPrice = this.defaultPrices[token.symbol.toLowerCase()];
|
|
80
|
-
if (defaultPrice) {
|
|
81
|
-
prices[token.symbol.toLowerCase()] = defaultPrice;
|
|
82
|
-
this.logger.warn('TokenPriceOracle default price used (decode failed)', {
|
|
83
|
-
symbol: token.symbol,
|
|
84
|
-
address: token.address,
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
}
|
|
50
|
+
prices[token.symbol] = price.toString();
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
failedSymbols.push(token.symbol);
|
|
88
54
|
}
|
|
89
|
-
return prices;
|
|
90
55
|
}
|
|
91
|
-
|
|
92
|
-
this.logger.
|
|
93
|
-
|
|
94
|
-
this.logger.warn('TokenPriceOracle using default prices (fetch failed)');
|
|
95
|
-
return this.defaultPrices;
|
|
56
|
+
if (failedSymbols.length > 0) {
|
|
57
|
+
this.logger.warn('Token price batch rejected', { failedSymbols: [...failedSymbols] });
|
|
58
|
+
throw new exceptions_1.TokenPriceError('One or more on-chain token prices are unavailable', failedSymbols);
|
|
96
59
|
}
|
|
60
|
+
return { ...prices };
|
|
97
61
|
}
|
|
98
|
-
/**
|
|
99
|
-
* Fetch price for single token
|
|
100
|
-
*/
|
|
101
62
|
async fetchPrice(token) {
|
|
102
63
|
const prices = await this.fetchPrices([token]);
|
|
103
|
-
return prices[token.symbol.toLowerCase()]
|
|
64
|
+
return prices[token.symbol.trim().toLowerCase()];
|
|
104
65
|
}
|
|
105
|
-
/**
|
|
106
|
-
* Get default prices
|
|
107
|
-
*/
|
|
108
|
-
getDefaultPrices() {
|
|
109
|
-
return { ...this.defaultPrices };
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Get supported tokens
|
|
113
|
-
*/
|
|
114
66
|
getSupportedTokens() {
|
|
115
|
-
return
|
|
67
|
+
return this.defaultTokens.map((token) => ({ ...token }));
|
|
116
68
|
}
|
|
117
|
-
/**
|
|
118
|
-
* Add custom tokens
|
|
119
|
-
*/
|
|
120
69
|
addTokens(tokens) {
|
|
121
|
-
this.defaultTokens = [...this.defaultTokens, ...tokens];
|
|
70
|
+
this.defaultTokens = this.normalizeTokens([...this.defaultTokens, ...tokens]);
|
|
122
71
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
72
|
+
normalizeTokens(tokens) {
|
|
73
|
+
if (tokens.length === 0) {
|
|
74
|
+
throw new exceptions_1.OracleError('INVALID_CONFIG', `Token list must contain between 1 and ${String(MAX_TOKENS)} items`);
|
|
75
|
+
}
|
|
76
|
+
const byAddress = new Map();
|
|
77
|
+
const symbols = new Set();
|
|
78
|
+
for (const token of tokens) {
|
|
79
|
+
const address = token.address.trim().toLowerCase();
|
|
80
|
+
const symbol = token.symbol.trim().toLowerCase();
|
|
81
|
+
if (!ethers_1.ethers.isAddress(address) || !/^[a-z0-9]{2,16}$/.test(symbol)) {
|
|
82
|
+
throw new exceptions_1.OracleError('INVALID_CONFIG', 'Token address or symbol is invalid');
|
|
83
|
+
}
|
|
84
|
+
if (!Number.isInteger(token.decimals) || token.decimals < 0 || token.decimals > 255) {
|
|
85
|
+
throw new exceptions_1.OracleError('INVALID_CONFIG', `Invalid decimals for token ${symbol}`);
|
|
86
|
+
}
|
|
87
|
+
if (symbols.has(symbol) && byAddress.get(address)?.symbol !== symbol) {
|
|
88
|
+
throw new exceptions_1.OracleError('INVALID_CONFIG', `Duplicate token symbol: ${symbol}`);
|
|
89
|
+
}
|
|
90
|
+
symbols.add(symbol);
|
|
91
|
+
byAddress.set(address, { address, symbol, decimals: token.decimals });
|
|
92
|
+
}
|
|
93
|
+
if (byAddress.size > MAX_TOKENS) {
|
|
94
|
+
throw new exceptions_1.OracleError('INVALID_CONFIG', `Token list exceeds ${String(MAX_TOKENS)} unique addresses`);
|
|
95
|
+
}
|
|
96
|
+
return [...byAddress.values()];
|
|
129
97
|
}
|
|
130
98
|
}
|
|
131
99
|
exports.TokenPriceOracle = TokenPriceOracle;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenPriceOracle.js","sourceRoot":"","sources":["../../../src/infrastructure/token-price/TokenPriceOracle.ts"],"names":[],"mappings":";;;AAAA,mCAAgC;
|
|
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/infrastructure/token-price/index.ts"],"names":[],"mappings":";;;AAAA,
|
|
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.d.ts
CHANGED
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
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"]}
|
package/package.json
CHANGED
|
@@ -1,41 +1,56 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@mixerx/oracles",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
5
|
-
"main": "./dist/index.js",
|
|
6
|
-
"types": "./dist/index.d.ts",
|
|
7
|
-
"packageManager": "yarn@4.12.0",
|
|
8
|
-
"files": [
|
|
9
|
-
"dist/**/*"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"test
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
},
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
}
|
|
41
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@mixerx/oracles",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Fail-closed Sepolia gas, token-price and withdrawal-fee policies for MixerX",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"packageManager": "yarn@4.12.0",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist/**/*"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "yarn clean && tsc",
|
|
13
|
+
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
14
|
+
"typecheck": "tsc --noEmit",
|
|
15
|
+
"lint": "eslint src tests",
|
|
16
|
+
"test": "yarn build && node --test \"tests/**/*.test.cjs\"",
|
|
17
|
+
"validate": "yarn typecheck && yarn lint && yarn test",
|
|
18
|
+
"prepack": "yarn build",
|
|
19
|
+
"prepublish": "yarn validate"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"mixerx",
|
|
23
|
+
"ethereum",
|
|
24
|
+
"gas",
|
|
25
|
+
"oracle",
|
|
26
|
+
"fees"
|
|
27
|
+
],
|
|
28
|
+
"author": "MixerX Contributors",
|
|
29
|
+
"license": "GPL-3.0-only",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/zkusd-mixerx/mixerx-oracles.git"
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/zkusd-mixerx/mixerx-oracles/issues"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/zkusd-mixerx/mixerx-oracles#readme",
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@mixerx/config": "0.5.4",
|
|
43
|
+
"ethers": "6.17.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@eslint/js": "9.39.1",
|
|
47
|
+
"@types/node": "24.10.1",
|
|
48
|
+
"eslint": "9.39.1",
|
|
49
|
+
"globals": "16.5.0",
|
|
50
|
+
"typescript": "5.9.3",
|
|
51
|
+
"typescript-eslint": "8.48.1"
|
|
52
|
+
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=22.12.0"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ILogger.d.ts","sourceRoot":"","sources":["../../../src/application/ports/ILogger.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,OAAO;IACtB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC9C;AAED,qBAAa,UAAW,YAAW,OAAO;IACxC,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,IAAI;IAC9C,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,IAAI;IAC7C,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,IAAI;CAC/C;AAED,qBAAa,aAAc,YAAW,OAAO;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,SAAmB;IAErD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAI5C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAI3C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;CAG7C"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/application/ports/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CACrD;AAED,cAAc,WAAW,CAAC"}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { ITokenPriceFetcher } from '../ports';
|
|
2
|
-
import { Token, TokenPrices } from '../../types';
|
|
3
|
-
import { ChainId } from '../../types';
|
|
4
|
-
/**
|
|
5
|
-
* Configuration for TokenPriceService
|
|
6
|
-
*/
|
|
7
|
-
export interface TokenPriceServiceConfig {
|
|
8
|
-
/** Chain ID used to resolve default tokens from @mixerx/config */
|
|
9
|
-
chainId: ChainId | number;
|
|
10
|
-
/** RPC URL for Ethereum Mainnet */
|
|
11
|
-
rpcUrl: string;
|
|
12
|
-
/** Multicall contract address */
|
|
13
|
-
multicallAddress: string;
|
|
14
|
-
/** Offchain Oracle contract address */
|
|
15
|
-
offchainOracleAddress: string;
|
|
16
|
-
/** Default token prices (fallback) */
|
|
17
|
-
defaultTokenPrices?: TokenPrices;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Token Price Service
|
|
21
|
-
* Fetches token prices from on-chain oracles using multicall
|
|
22
|
-
*/
|
|
23
|
-
export declare class TokenPriceService implements ITokenPriceFetcher {
|
|
24
|
-
private readonly config;
|
|
25
|
-
private provider;
|
|
26
|
-
private oracle;
|
|
27
|
-
private multicall;
|
|
28
|
-
private supportedTokens;
|
|
29
|
-
constructor(config: TokenPriceServiceConfig);
|
|
30
|
-
/**
|
|
31
|
-
* Fetch prices for tokens
|
|
32
|
-
* Uses multicall to batch requests
|
|
33
|
-
*/
|
|
34
|
-
fetchPrices(tokens?: Token[]): Promise<TokenPrices>;
|
|
35
|
-
/**
|
|
36
|
-
* Fetch price for a single token
|
|
37
|
-
*/
|
|
38
|
-
fetchSinglePrice(token: Token): Promise<string | null>;
|
|
39
|
-
/**
|
|
40
|
-
* Get default prices (fallback)
|
|
41
|
-
*/
|
|
42
|
-
getDefaultPrices(): TokenPrices;
|
|
43
|
-
/**
|
|
44
|
-
* Get supported tokens
|
|
45
|
-
*/
|
|
46
|
-
getSupportedTokens(): Token[];
|
|
47
|
-
/**
|
|
48
|
-
* Add custom tokens
|
|
49
|
-
*/
|
|
50
|
-
addTokens(tokens: Token[]): void;
|
|
51
|
-
/**
|
|
52
|
-
* ABI for Offchain Oracle (simplified)
|
|
53
|
-
*/
|
|
54
|
-
private getOracleAbi;
|
|
55
|
-
/**
|
|
56
|
-
* ABI for Multicall (simplified)
|
|
57
|
-
*/
|
|
58
|
-
private getMulticallAbi;
|
|
59
|
-
}
|
|
60
|
-
//# sourceMappingURL=TokenPriceService.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TokenPriceService.d.ts","sourceRoot":"","sources":["../../../src/application/services/TokenPriceService.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,kBAAkB,EACnB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,KAAK,EACL,WAAW,EACZ,MAAM,aAAa,CAAC;AAMrB,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAGtC;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,kEAAkE;IAClE,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC;IAE1B,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAC;IAEf,iCAAiC;IACjC,gBAAgB,EAAE,MAAM,CAAC;IAEzB,uCAAuC;IACvC,qBAAqB,EAAE,MAAM,CAAC;IAE9B,sCAAsC;IACtC,kBAAkB,CAAC,EAAE,WAAW,CAAC;CAClC;AAED;;;GAGG;AACH,qBAAa,iBAAkB,YAAW,kBAAkB;IAM9C,OAAO,CAAC,QAAQ,CAAC,MAAM;IALnC,OAAO,CAAC,QAAQ,CAAyB;IACzC,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,SAAS,CAAe;IAChC,OAAO,CAAC,eAAe,CAAU;gBAEJ,MAAM,EAAE,uBAAuB;IAiB5D;;;OAGG;IACG,WAAW,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAkEzD;;OAEG;IACG,gBAAgB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAS5D;;OAEG;IACH,gBAAgB,IAAI,WAAW;IAY/B;;OAEG;IACH,kBAAkB,IAAI,KAAK,EAAE;IAI7B;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI;IAIhC;;OAEG;IACH,OAAO,CAAC,YAAY;IAMpB;;OAEG;IACH,OAAO,CAAC,eAAe;CAKxB"}
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TokenPriceService = void 0;
|
|
4
|
-
const ethers_1 = require("ethers");
|
|
5
|
-
const TokenDefaults_1 = require("../../domain/config/TokenDefaults");
|
|
6
|
-
const exceptions_1 = require("../../domain/exceptions");
|
|
7
|
-
const ProtocolTokenConfig_1 = require("../../infrastructure/config/ProtocolTokenConfig");
|
|
8
|
-
const staticProvider_1 = require("../../infrastructure/blockchain/staticProvider");
|
|
9
|
-
/**
|
|
10
|
-
* Token Price Service
|
|
11
|
-
* Fetches token prices from on-chain oracles using multicall
|
|
12
|
-
*/
|
|
13
|
-
class TokenPriceService {
|
|
14
|
-
config;
|
|
15
|
-
provider;
|
|
16
|
-
oracle;
|
|
17
|
-
multicall;
|
|
18
|
-
supportedTokens;
|
|
19
|
-
constructor(config) {
|
|
20
|
-
this.config = config;
|
|
21
|
-
this.provider = (0, staticProvider_1.createStaticJsonRpcProvider)(config.rpcUrl, config.chainId);
|
|
22
|
-
this.oracle = new ethers_1.ethers.Contract(config.offchainOracleAddress, this.getOracleAbi(), this.provider);
|
|
23
|
-
this.multicall = new ethers_1.ethers.Contract(config.multicallAddress, this.getMulticallAbi(), this.provider);
|
|
24
|
-
this.supportedTokens = (0, ProtocolTokenConfig_1.resolveDefaultTokens)(config.chainId);
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Fetch prices for tokens
|
|
28
|
-
* Uses multicall to batch requests
|
|
29
|
-
*/
|
|
30
|
-
async fetchPrices(tokens) {
|
|
31
|
-
const tokensToFetch = tokens || this.supportedTokens;
|
|
32
|
-
try {
|
|
33
|
-
if (!tokensToFetch?.length) {
|
|
34
|
-
return {};
|
|
35
|
-
}
|
|
36
|
-
// Prepare call data for multicall
|
|
37
|
-
const callData = tokensToFetch.map((token) => ({
|
|
38
|
-
to: this.oracle.target,
|
|
39
|
-
data: this.oracle.interface.encodeFunctionData('getRateToEth', [
|
|
40
|
-
token.address,
|
|
41
|
-
true // use wrappers
|
|
42
|
-
]),
|
|
43
|
-
}));
|
|
44
|
-
// Execute multicall
|
|
45
|
-
const { results, success } = await this.multicall.multicall(callData);
|
|
46
|
-
const prices = {};
|
|
47
|
-
// Decode results
|
|
48
|
-
for (let i = 0; i < results.length; i++) {
|
|
49
|
-
const tokenSymbol = tokensToFetch[i].symbol.toLowerCase();
|
|
50
|
-
if (!success[i]) {
|
|
51
|
-
// Use default if call failed
|
|
52
|
-
const defaultPrice = this.getDefaultPrices()[tokenSymbol];
|
|
53
|
-
if (defaultPrice) {
|
|
54
|
-
prices[tokenSymbol] = defaultPrice;
|
|
55
|
-
}
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
try {
|
|
59
|
-
// Decode the rate
|
|
60
|
-
const decodedRate = ethers_1.ethers.AbiCoder.defaultAbiCoder().decode(['uint256'], results[i])[0];
|
|
61
|
-
// Calculate price with decimals adjustment
|
|
62
|
-
// Formula: (rate * 10^tokenDecimals) / 10^18
|
|
63
|
-
const tokenDecimals = 10n ** BigInt(tokensToFetch[i].decimals);
|
|
64
|
-
const ethDecimals = 10n ** 18n;
|
|
65
|
-
const price = (decodedRate * tokenDecimals) / ethDecimals;
|
|
66
|
-
prices[tokenSymbol] = price.toString();
|
|
67
|
-
}
|
|
68
|
-
catch (decodeError) {
|
|
69
|
-
// Use default if decoding failed
|
|
70
|
-
const defaultPrice = this.getDefaultPrices()[tokenSymbol];
|
|
71
|
-
if (defaultPrice) {
|
|
72
|
-
prices[tokenSymbol] = defaultPrice;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return prices;
|
|
77
|
-
}
|
|
78
|
-
catch (error) {
|
|
79
|
-
throw new exceptions_1.TokenPriceError(`Failed to fetch prices: ${error instanceof Error ? error.message : String(error)}`, tokensToFetch.map(t => t.symbol));
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Fetch price for a single token
|
|
84
|
-
*/
|
|
85
|
-
async fetchSinglePrice(token) {
|
|
86
|
-
try {
|
|
87
|
-
const prices = await this.fetchPrices([token]);
|
|
88
|
-
return prices[token.symbol.toLowerCase()] || null;
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Get default prices (fallback)
|
|
96
|
-
*/
|
|
97
|
-
getDefaultPrices() {
|
|
98
|
-
if (!this.config.defaultTokenPrices) {
|
|
99
|
-
return TokenDefaults_1.DEFAULT_TOKEN_PRICES;
|
|
100
|
-
}
|
|
101
|
-
const normalized = {};
|
|
102
|
-
for (const [symbol, value] of Object.entries(this.config.defaultTokenPrices)) {
|
|
103
|
-
normalized[symbol.toLowerCase()] = value;
|
|
104
|
-
}
|
|
105
|
-
return normalized;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Get supported tokens
|
|
109
|
-
*/
|
|
110
|
-
getSupportedTokens() {
|
|
111
|
-
return this.supportedTokens;
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Add custom tokens
|
|
115
|
-
*/
|
|
116
|
-
addTokens(tokens) {
|
|
117
|
-
this.supportedTokens = [...this.supportedTokens, ...tokens];
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* ABI for Offchain Oracle (simplified)
|
|
121
|
-
*/
|
|
122
|
-
getOracleAbi() {
|
|
123
|
-
return [
|
|
124
|
-
'function getRateToEth(address srcToken, bool useSrcWrappers) external view returns (uint256 weightedRate)',
|
|
125
|
-
];
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* ABI for Multicall (simplified)
|
|
129
|
-
*/
|
|
130
|
-
getMulticallAbi() {
|
|
131
|
-
return [
|
|
132
|
-
'function multicall(tuple(address to, bytes data)[] calls) external view returns (bytes[] results, bool[] success)',
|
|
133
|
-
];
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
exports.TokenPriceService = TokenPriceService;
|
|
137
|
-
//# sourceMappingURL=TokenPriceService.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TokenPriceService.js","sourceRoot":"","sources":["../../../src/application/services/TokenPriceService.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TokenDefaults.d.ts","sourceRoot":"","sources":["../../../src/domain/config/TokenDefaults.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAQvD,CAAC;AAGF,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAQpD,CAAC;AAGF,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGpD,CAAC;AAGF,eAAO,MAAM,2BAA2B,QAAQ,CAAC;AAGjD,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAKnD,CAAC;AAEF;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEvE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,QAAQ,GAAG,UAAU,GAAG,MAAM,CAE1E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAErD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/domain/config/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,oBAAoB,EAGpB,iBAAiB,EACjB,iBAAiB,EAGjB,2BAA2B,EAC3B,gBAAgB,EAGhB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,GACf,MAAM,iBAAiB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"exceptions.d.ts","sourceRoot":"","sources":["../../src/domain/exceptions.ts"],"names":[],"mappings":"AAAA,qBAAa,eAAgB,SAAQ,KAAK;aACK,OAAO,EAAE,MAAM,EAAE;gBAAlD,OAAO,EAAE,MAAM,EAAkB,OAAO,GAAE,MAAM,EAAO;CAIpE"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"repositories.d.ts","sourceRoot":"","sources":["../../src/domain/repositories.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;CAC9D"}
|
package/dist/factory.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,0CAA0C,CAAC;AAEnE,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,eAAe;IACf,OAAO,EAAE,OAAO,CAAC;IAEjB,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;IAEf,oDAAoD;IACpD,iBAAiB,EAAE,MAAM,CAAC;IAE1B,8EAA8E;IAC9E,eAAe,CAAC,EAAE,qBAAqB,CAAC;IAExC,0DAA0D;IAC1D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,yFAAyF;IACzF,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B,qCAAqC;IACrC,iBAAiB,CAAC,EAAE;QAClB,MAAM,CAAC,EAAE;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC;QAC1E,OAAO,CAAC,EAAE;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,YAAY,EAAE,MAAM,CAAC;YAAC,oBAAoB,EAAE,MAAM,CAAA;SAAE,CAAC;KACnF,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,2CAA2C;IAC3C,cAAc,EAAE,cAAc,CAAC;IAE/B,sDAAsD;IACtD,gBAAgB,EAAE,gBAAgB,CAAC;IAEnC,8EAA8E;IAC9E,eAAe,EAAE,eAAe,CAAC;IAEjC,+CAA+C;IAC/C,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;IAEpC,eAAe;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,mBAAmB,GAC1B,oBAAoB,CA6CtB"}
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { InterfaceAbi } from 'ethers';
|
|
2
|
-
export type OffchainOracleAbi = {
|
|
3
|
-
interface: {
|
|
4
|
-
encodeFunctionData(functionName: string, values?: readonly unknown[]): string;
|
|
5
|
-
};
|
|
6
|
-
target: string | object;
|
|
7
|
-
};
|
|
8
|
-
export type MulticallInput = {
|
|
9
|
-
to: string;
|
|
10
|
-
data: string;
|
|
11
|
-
};
|
|
12
|
-
export type MulticallAbi = {
|
|
13
|
-
multicall(calls: MulticallInput[]): Promise<{
|
|
14
|
-
results: string[];
|
|
15
|
-
success: boolean[];
|
|
16
|
-
}>;
|
|
17
|
-
};
|
|
18
|
-
export declare const OFFCHAIN_ORACLE_ABI: InterfaceAbi;
|
|
19
|
-
export declare const MULTICALL_ABI: InterfaceAbi;
|
|
20
|
-
//# sourceMappingURL=contracts.d.ts.map
|