Skip to content

Commit 1196d13

Browse files
authored
Merge pull request #796 from LIT-Protocol/LIT-4207-refresh-node-prices
LIT-4207 - Implement node price re-fetching when running lit node commands
2 parents fbf0ccd + 15091e7 commit 1196d13

File tree

7 files changed

+179
-117
lines changed

7 files changed

+179
-117
lines changed

packages/constants/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ export * from './lib/version';
55
export * from './lib/constants/constants';
66
export * from './lib/constants/mappers';
77
export * from './lib/constants/endpoints';
8-
export * from './lib/constants/mappers';
98
export * from './lib/constants/curves';
109

1110
// ----------- Interfaces -----------
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
1+
import { getPriceFeedInfo, getNodePrices } from './lib/price-feed-info-manager';
2+
13
export * from './lib/contracts-sdk';
24
export * from './lib/utils';
5+
6+
export { getPriceFeedInfo, getNodePrices };

packages/contracts-sdk/src/lib/contracts-sdk.ts

Lines changed: 7 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// import * as util from 'node:util'; // For inspecting bigInt payloads for pricing data
2-
31
import {
42
Abi,
53
AbiFunction,
@@ -14,21 +12,20 @@ import {
1412
AUTH_METHOD_SCOPE_VALUES,
1513
AUTH_METHOD_TYPE_VALUES,
1614
HTTP,
17-
HTTPS,
1815
HTTP_BY_NETWORK,
16+
HTTPS,
1917
InitError,
2018
InvalidArgumentException,
2119
LIT_NETWORK,
2220
LIT_NETWORK_VALUES,
2321
METAMASK_CHAIN_INFO_BY_NETWORK,
2422
NETWORK_CONTEXT_BY_NETWORK,
25-
PRODUCT_IDS,
2623
ParamsMissingError,
2724
RPC_URL_BY_NETWORK,
2825
TransactionError,
2926
WrongNetworkException,
3027
} from '@lit-protocol/constants';
31-
import { LogManager, Logger } from '@lit-protocol/logger';
28+
import { Logger, LogManager } from '@lit-protocol/logger';
3229
import { derivedAddresses, isBrowser, isNode } from '@lit-protocol/misc';
3330
import {
3431
ContractName,
@@ -47,13 +44,12 @@ import {
4744
import { getAuthIdByAuthMethod, stringToArrayify } from './auth-utils';
4845
import {
4946
CIDParser,
50-
IPFSHash,
5147
getBytes32FromMultihash,
48+
IPFSHash,
5249
} from './helpers/getBytes32FromMultihash';
5350
import { decToHex, hexToDec, intToIP } from './hex2dec';
54-
import { ValidatorStruct, type ValidatorWithPrices } from './types';
55-
56-
const PRODUCT_IDS_ARRAY = Object.values(PRODUCT_IDS);
51+
import { getPriceFeedInfo } from './price-feed-info-manager';
52+
import { ValidatorStruct } from './types';
5753

5854
// CHANGE: this should be dynamically set, but we only have 1 net atm.
5955
const REALM_ID = 1;
@@ -924,81 +920,14 @@ export class LitContracts {
924920
};
925921
};
926922

927-
public static getPriceFeedInfo = async ({
928-
realmId,
929-
litNetwork,
930-
networkContext,
931-
rpcUrl,
932-
nodeProtocol,
933-
}: {
923+
public static getPriceFeedInfo = async (params: {
934924
realmId: number;
935925
litNetwork: LIT_NETWORKS_KEYS;
936926
networkContext?: LitContractContext | LitContractResolverContext;
937927
rpcUrl?: string;
938928
nodeProtocol?: typeof HTTP | typeof HTTPS | null;
939929
}) => {
940-
const priceFeedContract = await LitContracts.getPriceFeedContract(
941-
litNetwork,
942-
networkContext,
943-
rpcUrl
944-
);
945-
946-
const nodesForRequest = await priceFeedContract['getNodesForRequest'](
947-
realmId,
948-
PRODUCT_IDS_ARRAY
949-
);
950-
951-
const epochId: number[] = nodesForRequest[0].toNumber();
952-
const minNodeCount: number[] = nodesForRequest[1].toNumber();
953-
const nodesAndPrices: ValidatorWithPrices[] = nodesForRequest[2];
954-
955-
const networkUrls = LitContracts.generateValidatorURLs({
956-
activeValidatorStructs: nodesAndPrices.map(({ validator }) => validator),
957-
litNetwork,
958-
nodeProtocol,
959-
});
960-
961-
const prices = networkUrls
962-
.reduce<{ url: string; prices: bigint[] }[]>((acc, network, index) => {
963-
acc.push({
964-
url: network,
965-
prices: nodesAndPrices[index].prices.map((ethersPrice) =>
966-
ethersPrice.toBigInt()
967-
),
968-
});
969-
return acc;
970-
}, [])
971-
.sort(({ prices: pricesA }, { prices: pricesB }) => {
972-
// Sort by any price since the cheapest for _any_ product will be the cheapest for _all_ products
973-
const diff = pricesA[0] - pricesB[0];
974-
if (diff > 0n) {
975-
return 1;
976-
} else if (diff < 0n) {
977-
return -1;
978-
} else {
979-
return 0;
980-
}
981-
});
982-
983-
// console.log(
984-
// 'getPriceFeedInfo()',
985-
// util.inspect(
986-
// {
987-
// epochId,
988-
// minNodeCount,
989-
// networkPrices: {
990-
// mapByAddress: networkPriceMap,
991-
// },
992-
// },
993-
// { depth: 4 }
994-
// )
995-
// );
996-
997-
return {
998-
epochId,
999-
minNodeCount,
1000-
networkPrices: prices,
1001-
};
930+
return getPriceFeedInfo(params);
1002931
};
1003932

1004933
private static _resolveContractContext(
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// import * as util from 'node:util'; // For inspecting bigInt payloads for pricing data
2+
3+
import { HTTP, HTTPS, PRODUCT_IDS } from '@lit-protocol/constants';
4+
import {
5+
LIT_NETWORKS_KEYS,
6+
LitContractContext,
7+
LitContractResolverContext,
8+
} from '@lit-protocol/types';
9+
10+
import { LitContracts } from './contracts-sdk';
11+
12+
import type { ValidatorWithPrices } from './types';
13+
14+
type GetPriceFeedInfoArgs = Parameters<typeof LitContracts.getPriceFeedInfo>;
15+
type PriceFeedInfo = Awaited<ReturnType<typeof fetchPriceFeedInfo>>;
16+
17+
const STALE_PRICES_SECONDS = 3 * 1000; // Update prices if > X seconds old
18+
const PRODUCT_IDS_ARRAY = Object.values(PRODUCT_IDS);
19+
20+
let priceFeedInfo: PriceFeedInfo | null = null;
21+
let fetchingPriceFeedInfo: null | Promise<PriceFeedInfo> = null;
22+
let lastUpdatedTimestamp = 0;
23+
24+
async function fetchPriceFeedInfo({
25+
realmId,
26+
litNetwork,
27+
networkContext,
28+
rpcUrl,
29+
nodeProtocol,
30+
}: {
31+
realmId: number;
32+
litNetwork: LIT_NETWORKS_KEYS;
33+
networkContext?: LitContractContext | LitContractResolverContext;
34+
rpcUrl?: string;
35+
nodeProtocol?: typeof HTTP | typeof HTTPS | null;
36+
}) {
37+
const priceFeedContract = await LitContracts.getPriceFeedContract(
38+
litNetwork,
39+
networkContext,
40+
rpcUrl
41+
);
42+
43+
const nodesForRequest = await priceFeedContract['getNodesForRequest'](
44+
realmId,
45+
PRODUCT_IDS_ARRAY
46+
);
47+
48+
const epochId: number[] = nodesForRequest[0].toNumber();
49+
const minNodeCount: number[] = nodesForRequest[1].toNumber();
50+
const nodesAndPrices: ValidatorWithPrices[] = nodesForRequest[2];
51+
52+
const networkUrls = LitContracts.generateValidatorURLs({
53+
activeValidatorStructs: nodesAndPrices.map(({ validator }) => validator),
54+
litNetwork,
55+
nodeProtocol,
56+
});
57+
58+
const prices = networkUrls
59+
.reduce<{ url: string; prices: bigint[] }[]>((acc, network, index) => {
60+
acc.push({
61+
url: network,
62+
prices: nodesAndPrices[index].prices.map((ethersPrice) =>
63+
ethersPrice.toBigInt()
64+
),
65+
});
66+
return acc;
67+
}, [])
68+
.sort(({ prices: pricesA }, { prices: pricesB }) => {
69+
// Sort by any price since the cheapest for _any_ product will be the cheapest for _all_ products
70+
const diff = pricesA[0] - pricesB[0];
71+
if (diff > 0n) {
72+
return 1;
73+
} else if (diff < 0n) {
74+
return -1;
75+
} else {
76+
return 0;
77+
}
78+
});
79+
80+
// console.log(
81+
// 'getPriceFeedInfo()',
82+
// util.inspect(
83+
// {
84+
// epochId,
85+
// minNodeCount,
86+
// networkPrices: {
87+
// mapByAddress: networkPriceMap,
88+
// },
89+
// },
90+
// { depth: 4 }
91+
// )
92+
// );
93+
94+
return {
95+
epochId,
96+
minNodeCount,
97+
networkPrices: prices,
98+
};
99+
}
100+
101+
async function fetchPriceFeedInfoWithLocalPromise(
102+
...params: GetPriceFeedInfoArgs
103+
): Promise<PriceFeedInfo> {
104+
try {
105+
fetchingPriceFeedInfo = fetchPriceFeedInfo(...params);
106+
107+
priceFeedInfo = await fetchingPriceFeedInfo;
108+
lastUpdatedTimestamp = Date.now();
109+
110+
return priceFeedInfo;
111+
} finally {
112+
fetchingPriceFeedInfo = null;
113+
}
114+
}
115+
116+
export async function getPriceFeedInfo(...params: GetPriceFeedInfoArgs) {
117+
// If there's a local promise, an update is in progress; wait for that
118+
if (fetchingPriceFeedInfo) {
119+
return fetchingPriceFeedInfo;
120+
}
121+
122+
// If we have updated prices in the last 2 seconds, return our current prices
123+
if (
124+
priceFeedInfo &&
125+
Date.now() - lastUpdatedTimestamp < STALE_PRICES_SECONDS
126+
) {
127+
return priceFeedInfo;
128+
}
129+
130+
// If we get here, we've got prices that are at least 2 seconds out-of-date.
131+
// Fetch the new ones, update local cache values, and return them
132+
return fetchPriceFeedInfoWithLocalPromise(...params);
133+
}
134+
135+
export async function getNodePrices(
136+
...params: GetPriceFeedInfoArgs
137+
): Promise<PriceFeedInfo['networkPrices']> {
138+
const priceInfo = await getPriceFeedInfo(...params);
139+
140+
return priceInfo.networkPrices;
141+
}

packages/core/src/lib/lit-core.ts

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,5 @@
11
import { ethers } from 'ethers';
22

3-
import {
4-
canonicalAccessControlConditionFormatter,
5-
canonicalEVMContractConditionFormatter,
6-
canonicalSolRpcConditionFormatter,
7-
canonicalUnifiedAccessControlConditionFormatter,
8-
hashAccessControlConditions,
9-
hashEVMContractConditions,
10-
hashSolRpcConditions,
11-
hashUnifiedAccessControlConditions,
12-
validateAccessControlConditionsSchema,
13-
validateEVMContractConditionsSchema,
14-
validateSolRpcConditionsSchema,
15-
validateUnifiedAccessControlConditionsSchema,
16-
} from '@lit-protocol/access-control-conditions';
173
import {
184
CENTRALISATION_BY_NETWORK,
195
HTTP,
@@ -59,15 +45,12 @@ import {
5945
CustomNetwork,
6046
EpochInfo,
6147
EthBlockhashInfo,
62-
FormattedMultipleAccs,
6348
JsonHandshakeResponse,
6449
LitNodeClientConfig,
65-
MultipleAccessControlConditions,
6650
NodeSet,
6751
RejectedNodePromises,
6852
SessionSigsMap,
6953
SuccessNodePromises,
70-
SupportedJsonRequests,
7154
} from '@lit-protocol/types';
7255

7356
import { composeLitUrl } from './endpoint-version';
@@ -132,8 +115,6 @@ export type LitNodeClientConfigWithDefaults = Required<
132115
bootstrapUrls: string[];
133116
} & {
134117
nodeProtocol?: typeof HTTP | typeof HTTPS | null;
135-
} & {
136-
nodePrices: { url: string; prices: bigint[] }[]; // eg. <nodeAddress, price[]>
137118
};
138119

139120
export class LitCore {
@@ -146,7 +127,6 @@ export class LitCore {
146127
minNodeCount: 2, // Default value, should be replaced
147128
bootstrapUrls: [], // Default value, should be replaced
148129
nodeProtocol: null,
149-
nodePrices: [],
150130
};
151131
connectedNodes = new Set<string>();
152132
serverKeys: Record<string, JsonHandshakeResponse> = {};
@@ -521,7 +501,6 @@ export class LitCore {
521501
this._stakingContract = validatorData.stakingContract;
522502
this.config.minNodeCount = validatorData.minNodeCount;
523503
this.config.bootstrapUrls = validatorData.bootstrapUrls;
524-
this.config.nodePrices = validatorData.nodePrices;
525504

526505
this._epochState = await this._fetchCurrentEpochState(
527506
validatorData.epochInfo

0 commit comments

Comments
 (0)