11/* eslint-disable import/order */
2- import { isBrowser , isNode } from '@lit-protocol/misc' ;
2+ import { isBrowser , isNode , log } from '@lit-protocol/misc' ;
33import {
44 ContractName ,
55 CreateCustomAuthMethodRequest ,
@@ -620,18 +620,18 @@ export class LitContracts {
620620 } ;
621621
622622 /**
623- * Retrieves the PriceFeed contract instance based on the provided network, context, and RPC URL.
624- * If a context is provided, it determines if a contract resolver is used for bootstrapping contracts.
625- * If a resolver address is present in the context, it retrieves the PriceFeed contract from the contract resolver instance.
626- * Otherwise, it retrieves the PriceFeed contract using the contract address and ABI.
627- * Throws an error if required contract data is missing or if the PriceFeed contract cannot be obtained.
628- *
629- * @param network - The network key.
630- * @param context - The contract context or contract resolver context.
631- * @param rpcUrl - The RPC URL.
632- * @returns The PriceFeed contract instance.
633- * @throws Error if required contract data is missing or if the PriceFeed contract cannot be obtained.
634- */
623+ * Retrieves the PriceFeed contract instance based on the provided network, context, and RPC URL.
624+ * If a context is provided, it determines if a contract resolver is used for bootstrapping contracts.
625+ * If a resolver address is present in the context, it retrieves the PriceFeed contract from the contract resolver instance.
626+ * Otherwise, it retrieves the PriceFeed contract using the contract address and ABI.
627+ * Throws an error if required contract data is missing or if the PriceFeed contract cannot be obtained.
628+ *
629+ * @param network - The network key.
630+ * @param context - The contract context or contract resolver context.
631+ * @param rpcUrl - The RPC URL.
632+ * @returns The PriceFeed contract instance.
633+ * @throws Error if required contract data is missing or if the PriceFeed contract cannot be obtained.
634+ */
635635 public static async getPriceFeedContract (
636636 network : LIT_NETWORKS_KEYS ,
637637 context ?: LitContractContext | LitContractResolverContext ,
@@ -938,7 +938,7 @@ export class LitContracts {
938938 const names = contractNames ?? LitContracts . contractNames ;
939939
940940 const contractContext : LitContractContext = { } as LitContractContext ;
941- // Ah, Bluebird.props(), we miss you 🫗
941+ // Ah, Bluebird.props(), we miss you ����
942942 await Promise . all (
943943 names . map ( async ( contractName ) => {
944944 const contracts = context ?. contractContext ;
@@ -1150,7 +1150,6 @@ export class LitContracts {
11501150 } ) ;
11511151
11521152 const networks = activeValidatorStructs . map ( ( item : ValidatorStruct ) => {
1153- const centralisation = CENTRALISATION_BY_NETWORK [ network ] ;
11541153
11551154 // Convert the integer IP to a string format
11561155 const ip = intToIP ( item . ip ) ;
@@ -1175,6 +1174,62 @@ export class LitContracts {
11751174 return networks ;
11761175 } ;
11771176
1177+
1178+ /**
1179+ * Generates an array of validator URLs based on the given validator structs and network configurations.
1180+ *
1181+ * @param {ValidatorStruct[] } activeValidatorStructs - Array of validator structures containing IP and port information.
1182+ * @param {string | undefined } nodeProtocol - Optional node protocol to override the default protocol selection logic.
1183+ * @param {string } litNetwork - The name of the network used to determine HTTP/HTTPS settings.
1184+ * @returns {string[] } Array of constructed validator URLs.
1185+ *
1186+ * @example
1187+ * // Example input
1188+ * const activeValidatorStructs = [
1189+ * { ip: 3232235777, port: 443 }, // IP: 192.168.1.1
1190+ * { ip: 3232235778, port: 80 }, // IP: 192.168.1.2
1191+ * ];
1192+ * const nodeProtocol = undefined;
1193+ * const litNetwork = "mainnet";
1194+ *
1195+ * // Example output
1196+ * const urls = generateValidatorURLs(activeValidatorStructs, nodeProtocol, litNetwork);
1197+ * console.log(urls);
1198+ * Output: [
1199+ * "https://192.168.1.1:443",
1200+ * "http://192.168.1.2:80"
1201+ * ]
1202+ */
1203+ public static generateValidatorURLs ( {
1204+ activeValidatorStructs,
1205+ nodeProtocol,
1206+ litNetwork,
1207+ } : {
1208+ activeValidatorStructs : ValidatorStruct [ ] ;
1209+ nodeProtocol ?: string ;
1210+ litNetwork : LIT_NETWORK_VALUES ;
1211+ } ) : string [ ] {
1212+ return activeValidatorStructs . map ( ( item ) => {
1213+ // Convert the integer IP to a string format
1214+ const ip = intToIP ( item . ip ) ;
1215+ const port = item . port ;
1216+
1217+ // Determine the protocol to use based on conditions
1218+ const protocol =
1219+ nodeProtocol || // Use nodeProtocol if defined
1220+ ( port === 443 ? HTTPS : HTTP_BY_NETWORK [ litNetwork ] ) || // HTTPS for port 443 or network-specific HTTP
1221+ HTTP ; // Fallback to HTTP
1222+
1223+ // Construct the URL
1224+ const url = `${ protocol } ${ ip } :${ port } ` ;
1225+
1226+ // Log the constructed URL for debugging
1227+ LitContracts . logger . debug ( "Validator's URL:" , url ) ;
1228+
1229+ return url ;
1230+ } ) ;
1231+ }
1232+
11781233 /**
11791234 * Retrieves the connection information for a given network.
11801235 *
@@ -1193,17 +1248,31 @@ export class LitContracts {
11931248 networkContext,
11941249 rpcUrl,
11951250 nodeProtocol,
1251+ sortByPrice
11961252 } : {
11971253 litNetwork : LIT_NETWORKS_KEYS ;
11981254 networkContext ?: LitContractContext | LitContractResolverContext ;
11991255 rpcUrl ?: string ;
12001256 nodeProtocol ?: typeof HTTP | typeof HTTPS | null ;
1257+ sortByPrice ?: boolean ;
12011258 } ) : Promise < {
12021259 stakingContract : ethers . Contract ;
12031260 epochInfo : EpochInfo ;
12041261 minNodeCount : number ;
12051262 bootstrapUrls : string [ ] ;
1263+ priceByNetwork : Record < string , number > ;
12061264 } > => {
1265+
1266+ // if it's true, we will sort the networks by price feed from lowest to highest
1267+ // if it's false, we will not sort the networks
1268+ let _sortByPrice = sortByPrice || true ;
1269+
1270+ if ( _sortByPrice ) {
1271+ log ( 'Sorting networks by price feed from lowest to highest' ) ;
1272+ } else {
1273+ log ( 'Not sorting networks by price feed' ) ;
1274+ }
1275+
12071276 const stakingContract = await LitContracts . getStakingContract (
12081277 litNetwork ,
12091278 networkContext ,
@@ -1246,32 +1315,44 @@ export class LitContracts {
12461315 } ;
12471316 } ) ;
12481317
1249- const networks = activeValidatorStructs . map ( ( item : ValidatorStruct ) => {
1250- // Convert the integer IP to a string format
1251- const ip = intToIP ( item . ip ) ;
1252- const port = item . port ;
1253-
1254- // Determine the protocol to use based on various conditions
1255- const protocol =
1256- // If nodeProtocol is defined, use it
1257- nodeProtocol ||
1258- // If port is 443, use HTTPS, otherwise use network-specific HTTP
1259- ( port === 443 ? HTTPS : HTTP_BY_NETWORK [ litNetwork ] ) ||
1260- // Fallback to HTTP if no other conditions are met
1261- HTTP ;
1318+ const unsortedNetworks = LitContracts . generateValidatorURLs ( {
1319+ activeValidatorStructs,
1320+ litNetwork,
1321+ } )
12621322
1263- const url = `${ protocol } ${ ip } :${ port } ` ;
1323+ // networks are all the nodes we know, but we also want to sort it by price feed
1324+ const priceFeedInfo = await LitContracts . getPriceFeedInfo ( {
1325+ litNetwork,
1326+ networkContext,
1327+ rpcUrl,
1328+ nodeProtocol,
1329+ } ) ;
12641330
1265- LitContracts . logger . debug ( "Validator's URL:" , url ) ;
1331+ // example of Network to Price Map: {
1332+ // 'http://xxx:7470': 100, <-- lowest price
1333+ // 'http://yyy:7471': 300, <-- highest price
1334+ // 'http://zzz:7472': 200 <-- middle price
1335+ // }
1336+ const PRICE_BY_NETWORK = priceFeedInfo . networkPrices . mapByAddress ;
1337+
1338+ // sorted networks by prices (lowest to highest)
1339+ // [
1340+ // 'http://xxx:7470', <-- lowest price
1341+ // 'http://zzz:7472', <-- middle price
1342+ // 'http://yyy:7471' <-- highest price
1343+ // ]
1344+ const sortedNetworks = unsortedNetworks . sort ( ( a , b ) =>
1345+ PRICE_BY_NETWORK [ a ] - PRICE_BY_NETWORK [ b ]
1346+ ) ;
12661347
1267- return url ;
1268- } ) ;
1348+ const bootstrapUrls = _sortByPrice ? sortedNetworks : unsortedNetworks ;
12691349
12701350 return {
12711351 stakingContract,
12721352 epochInfo : typedEpochInfo ,
12731353 minNodeCount : minNodeCountInt ,
1274- bootstrapUrls : networks ,
1354+ bootstrapUrls : bootstrapUrls ,
1355+ priceByNetwork : PRICE_BY_NETWORK ,
12751356 } ;
12761357 } ;
12771358
@@ -1280,79 +1361,66 @@ export class LitContracts {
12801361 networkContext,
12811362 rpcUrl,
12821363 nodeProtocol,
1364+ productIds, // Array of product IDs
12831365 } : {
1284- litNetwork : LIT_NETWORKS_KEYS ;
1366+ litNetwork : LIT_NETWORKS_KEYS ,
12851367 networkContext ?: LitContractContext | LitContractResolverContext ;
12861368 rpcUrl ?: string ;
12871369 nodeProtocol ?: typeof HTTP | typeof HTTPS | null ;
1370+ productIds ?: number [ ] ;
12881371 } ) => {
1372+
1373+ if ( ! productIds || productIds . length === 0 ) {
1374+ log ( 'No product IDs provided. Defaulting to 0' ) ;
1375+
1376+ // You should use all [0,1,2] because we fetch the price first to connect to the cheapest node. And after that the user calls the actual function
1377+ productIds = [ 0 , 1 , 2 ]
1378+ }
1379+
12891380 const priceFeedContract = await LitContracts . getPriceFeedContract (
12901381 litNetwork ,
12911382 networkContext ,
1292- rpcUrl
1293- ) ;
1383+ rpcUrl ,
1384+ )
12941385
1295- const nodesForRequest = await priceFeedContract [ 'getNodesForRequest' ] ( [ 0 ] ) ;
1386+ const nodesForRequest = await priceFeedContract [ 'getNodesForRequest' ] ( productIds ) ;
12961387
12971388 const epochId = nodesForRequest [ 0 ] . toNumber ( ) ;
12981389 const minNodeCount = nodesForRequest [ 1 ] . toNumber ( ) ;
12991390 const nodesAndPrices = nodesForRequest [ 2 ] ;
13001391
1301- // const totalNodes = nodesAndPrices.length;
1302-
1303- const activeValidatorStructs : ValidatorStruct [ ] = nodesAndPrices . map (
1304- ( item : any ) => {
1305- const activeUnkickedValidatorStruct = item . validator ;
1306- return {
1307- ip : activeUnkickedValidatorStruct . ip ,
1308- ipv6 : activeUnkickedValidatorStruct . ipv6 ,
1309- port : activeUnkickedValidatorStruct . port ,
1310- nodeAddress : activeUnkickedValidatorStruct . nodeAddress ,
1311- reward : activeUnkickedValidatorStruct . reward ,
1312- seconderPubkey : activeUnkickedValidatorStruct . seconderPubkey ,
1313- receiverPubkey : activeUnkickedValidatorStruct . receiverPubkey ,
1314- } ;
1392+ const activeValidatorStructs : ValidatorStruct [ ] = nodesAndPrices . map ( ( item : any ) => {
1393+ return {
1394+ ip : item . validator . ip ,
1395+ ipv6 : item . validator . ipv6 ,
1396+ port : item . validator . port ,
1397+ nodeAddress : item . validator . nodeAddress ,
1398+ reward : item . validator . reward ,
1399+ seconderPubkey : item . validator . seconderPubkey ,
1400+ receiverPubkey : item . validator . receiverPubkey ,
13151401 }
1316- ) ;
1317-
1318- const networks = activeValidatorStructs . map ( ( item : ValidatorStruct ) => {
1319- // Convert the integer IP to a string format
1320- const ip = intToIP ( item . ip ) ;
1321- const port = item . port ;
1322-
1323- // Determine the protocol to use based on various conditions
1324- const protocol =
1325- // If nodeProtocol is defined, use it
1326- nodeProtocol ||
1327- // If port is 443, use HTTPS, otherwise use network-specific HTTP
1328- ( port === 443 ? HTTPS : HTTP_BY_NETWORK [ litNetwork ] ) ||
1329- // Fallback to HTTP if no other conditions are met
1330- HTTP ;
1331-
1332- const url = `${ protocol } ${ ip } :${ port } ` ;
1333-
1334- LitContracts . logger . debug ( "Validator's URL:" , url ) ;
1335-
1336- return url ;
13371402 } ) ;
13381403
1339- console . log ( 'networks:' , networks ) ;
1404+ const networks = LitContracts . generateValidatorURLs ( {
1405+ activeValidatorStructs,
1406+ litNetwork,
1407+ } )
1408+
1409+ console . log ( "networks:" , networks ) ;
13401410
13411411 const prices = nodesAndPrices . flatMap ( ( item : any ) => {
13421412 // Flatten the nested prices array and convert BigNumber to number
1343- return item . prices . map ( ( price : ethers . BigNumber ) =>
1344- parseFloat ( price . toString ( ) )
1345- ) ;
1413+ return item . prices . map ( ( price : ethers . BigNumber ) => parseFloat ( price . toString ( ) ) ) ;
13461414 } ) ;
13471415
1348- console . log ( ' Prices as numbers:' , prices ) ;
1416+ console . log ( " Prices as numbers:" , prices ) ;
13491417
1350- const networkPriceMap = networks . reduce ( ( acc : any , network , index ) => {
1418+ const networkPriceMap : Record < string , number > = networks . reduce ( ( acc : any , network , index ) => {
13511419 acc [ network ] = prices [ index ] ;
13521420 return acc ;
13531421 } , { } ) ;
13541422
1355- console . log ( ' Network to Price Map:' , networkPriceMap ) ;
1423+ console . log ( " Network to Price Map:" , networkPriceMap ) ;
13561424
13571425 const networkPriceObjArr = networks . map ( ( network , index ) => {
13581426 return {
@@ -1365,11 +1433,11 @@ export class LitContracts {
13651433 epochId,
13661434 minNodeCount,
13671435 networkPrices : {
1368- arrObjects : networkPriceObjArr ,
1369- map : networkPriceMap ,
1436+ arr : networkPriceObjArr ,
1437+ mapByAddress : networkPriceMap
13701438 } ,
1371- } ;
1372- } ;
1439+ }
1440+ }
13731441
13741442 private static async _resolveContractContext (
13751443 network : LIT_NETWORK_VALUES
0 commit comments