@provable-games/metagame-sdk 0.1.3 → 0.1.4

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/dist/index.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var starknet = require('starknet');
4
+ var strkMerkleTree = require('@ericnordelo/strk-merkle-tree');
4
5
 
5
6
  // src/types/extensions.ts
6
7
  var QualifyingMode = /* @__PURE__ */ ((QualifyingMode2) => {
@@ -477,12 +478,13 @@ function parseKeyValueFormat(input) {
477
478
  // src/utils/extensions.ts
478
479
  var EXTENSION_ADDRESSES = {
479
480
  SN_SEPOLIA: {
480
- tournamentValidator: "0x04c9ba290bbebd47f87141801623748884af610230cdaf69232007b4135be3f1",
481
- erc20BalanceValidator: "0x05437210fd8231893da2317971983ae1f810d8d500344ad09e5b258b5820a908",
481
+ tournamentValidator: "0x07eade45e4317b1a036e3a8123bb1f95215d37a6f6b0cea25cdd48030a932dfc",
482
+ erc20BalanceValidator: "0x028112199f873e919963277b41ef1231365986e2fd7722501cd7d293de60b64e",
482
483
  opusTrovesValidator: "0x0317f96eeff41d1badffd9bda126d36c1806523d8c91a5b440f9bdf2aa2b9fe7",
483
- snapshotValidator: "0x02628045f0688663ecacbaefdbbc46c2e315b2da39271419e8a3443fe4120f61",
484
+ snapshotValidator: "0x05520239f16dc58c5dfdccb1f0480977e7fea18d4305e64f5eae88ae786a22fe",
484
485
  zkPassportValidator: "0x025b65137c5297e03491830635f4c5d76eb17e5b2db3bf49dbee34bba94ad3e6",
485
- governanceValidator: "0x0585e6b1aa8daac7711004a92a8d0d3b5a81eaac8c9a07db24fc34b1a4f2322c"
486
+ governanceValidator: "0x0585e6b1aa8daac7711004a92a8d0d3b5a81eaac8c9a07db24fc34b1a4f2322c",
487
+ merkleValidator: "0x07a5b716c65ac5726ecd0b023ff1b4cc9090820707a508b49f5ed42e44a401c4"
486
488
  },
487
489
  SN_MAIN: {
488
490
  tournamentValidator: "0x0771b57c0709fc4407ff8b63d573f302b96fb03638364032fad734e3c310b9e0",
@@ -490,7 +492,9 @@ var EXTENSION_ADDRESSES = {
490
492
  opusTrovesValidator: "0x0604bc0d54727f439786c65d7dc6f7d46de1aba7e129ef9caf65fef111a1644e",
491
493
  snapshotValidator: "0x03e6820e9e1cfb5c22465a86f469c651355f05397e29fc94de8e832d5f3d8ede",
492
494
  zkPassportValidator: "0x01a25f04c8a39da7bb37adcc53d595fde9e60702611d15b1c498df0657001479",
493
- governanceValidator: ""
495
+ governanceValidator: "",
496
+ // placeholder until deployed
497
+ merkleValidator: ""
494
498
  // placeholder until deployed
495
499
  }
496
500
  };
@@ -555,6 +559,9 @@ function identifyExtensionType(extensionAddress, chainId) {
555
559
  if (addresses.governanceValidator && normalizeAddr(addresses.governanceValidator) === normalized) {
556
560
  return "governance";
557
561
  }
562
+ if (addresses.merkleValidator && normalizeAddr(addresses.merkleValidator) === normalized) {
563
+ return "merkle";
564
+ }
558
565
  return "unknown";
559
566
  }
560
567
  function parseTournamentValidatorConfig(config) {
@@ -625,6 +632,12 @@ function parseGovernanceValidatorConfig(config) {
625
632
  votesPerEntry: BigInt(config[10]) << 128n | BigInt(config[9])
626
633
  };
627
634
  }
635
+ function parseMerkleValidatorConfig(config) {
636
+ if (!config || config.length === 0) return null;
637
+ return {
638
+ treeId: String(Number(BigInt(config[0] ?? "0")))
639
+ };
640
+ }
628
641
  function parseExtensionConfig(extensionAddress, config, chainId) {
629
642
  const extensionType = identifyExtensionType(extensionAddress, chainId);
630
643
  switch (extensionType) {
@@ -652,6 +665,10 @@ function parseExtensionConfig(extensionAddress, config, chainId) {
652
665
  const parsed = parseGovernanceValidatorConfig(config);
653
666
  return parsed ? { type: "governance", config: parsed } : null;
654
667
  }
668
+ case "merkle": {
669
+ const parsed = parseMerkleValidatorConfig(config);
670
+ return parsed ? { type: "merkle", config: parsed } : null;
671
+ }
655
672
  default:
656
673
  return { type: "unknown", config: null };
657
674
  }
@@ -1012,6 +1029,285 @@ function calculatePaidPlaces(sponsoredPrizes, entryFeeDistributionCount) {
1012
1029
  return Math.max(maxSponsoredPosition, entryFeeDistributionCount);
1013
1030
  }
1014
1031
 
1032
+ // src/utils/merkleApi.ts
1033
+ var DEFAULT_MERKLE_API_URLS = {
1034
+ SN_SEPOLIA: "https://merkle-api-sepolia.up.railway.app",
1035
+ SN_MAIN: "https://merkle-api.up.railway.app"
1036
+ };
1037
+ var merkleApiUrls = { ...DEFAULT_MERKLE_API_URLS };
1038
+ function setMerkleApiUrl(chainId, url) {
1039
+ merkleApiUrls[chainId] = url;
1040
+ }
1041
+ function getMerkleApiUrl(chainId) {
1042
+ if (chainId && merkleApiUrls[chainId]) {
1043
+ return merkleApiUrls[chainId];
1044
+ }
1045
+ return merkleApiUrls.SN_MAIN ?? DEFAULT_MERKLE_API_URLS.SN_MAIN;
1046
+ }
1047
+ async function fetchMerkleTrees(options) {
1048
+ const empty = {
1049
+ data: [],
1050
+ total: 0,
1051
+ page: 1,
1052
+ limit: 20,
1053
+ totalPages: 0
1054
+ };
1055
+ try {
1056
+ const baseUrl = getMerkleApiUrl(options?.chainId);
1057
+ const params = new URLSearchParams();
1058
+ if (options?.page) params.set("page", String(options.page));
1059
+ if (options?.limit) params.set("limit", String(options.limit));
1060
+ const qs = params.toString();
1061
+ const res = await fetch(`${baseUrl}/trees${qs ? `?${qs}` : ""}`);
1062
+ if (!res.ok) return empty;
1063
+ const json = await res.json();
1064
+ return {
1065
+ data: json.data ?? [],
1066
+ total: json.total ?? 0,
1067
+ page: json.page ?? 1,
1068
+ limit: json.limit ?? 20,
1069
+ totalPages: json.totalPages ?? 0
1070
+ };
1071
+ } catch {
1072
+ return empty;
1073
+ }
1074
+ }
1075
+ async function createMerkleTree(request, chainId) {
1076
+ const baseUrl = getMerkleApiUrl(chainId);
1077
+ const res = await fetch(`${baseUrl}/trees`, {
1078
+ method: "POST",
1079
+ headers: { "Content-Type": "application/json" },
1080
+ body: JSON.stringify(request)
1081
+ });
1082
+ if (!res.ok) {
1083
+ const text = await res.text();
1084
+ throw new Error(`Failed to create merkle tree: ${text}`);
1085
+ }
1086
+ return await res.json();
1087
+ }
1088
+ async function fetchMerkleTreeEntries(treeId, options) {
1089
+ const empty = {
1090
+ data: [],
1091
+ total: 0,
1092
+ page: 1,
1093
+ limit: 50,
1094
+ totalPages: 0
1095
+ };
1096
+ try {
1097
+ const baseUrl = getMerkleApiUrl(options?.chainId);
1098
+ const params = new URLSearchParams();
1099
+ if (options?.page) params.set("page", String(options.page));
1100
+ if (options?.limit) params.set("limit", String(options.limit));
1101
+ const qs = params.toString();
1102
+ const res = await fetch(
1103
+ `${baseUrl}/trees/${treeId}/entries${qs ? `?${qs}` : ""}`
1104
+ );
1105
+ if (!res.ok) return empty;
1106
+ const json = await res.json();
1107
+ return {
1108
+ data: json.data ?? [],
1109
+ total: json.total ?? 0,
1110
+ page: json.page ?? 1,
1111
+ limit: json.limit ?? 50,
1112
+ totalPages: json.totalPages ?? 0
1113
+ };
1114
+ } catch {
1115
+ return empty;
1116
+ }
1117
+ }
1118
+ async function fetchMerkleProof(treeId, playerAddress, chainId) {
1119
+ try {
1120
+ const baseUrl = getMerkleApiUrl(chainId);
1121
+ const res = await fetch(
1122
+ `${baseUrl}/trees/${treeId}/proof/${playerAddress.toLowerCase()}`
1123
+ );
1124
+ if (!res.ok) return null;
1125
+ return await res.json();
1126
+ } catch {
1127
+ return null;
1128
+ }
1129
+ }
1130
+ function computeLeafValue(address, count) {
1131
+ const intermediate = starknet.hash.computePedersenHash("0x0", address);
1132
+ return starknet.hash.computePedersenHash(intermediate, "0x" + count.toString(16));
1133
+ }
1134
+ function buildMerkleTree(entries) {
1135
+ if (entries.length === 0) {
1136
+ throw new Error("Cannot build merkle tree with no entries");
1137
+ }
1138
+ const leaves = entries.map((e) => [computeLeafValue(e.address, e.count)]);
1139
+ const tree = strkMerkleTree.StandardMerkleTree.of(leaves, ["felt252"], {
1140
+ sortLeaves: true
1141
+ });
1142
+ return {
1143
+ version: 2,
1144
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1145
+ entries,
1146
+ root: tree.root,
1147
+ tree: tree.dump()
1148
+ };
1149
+ }
1150
+
1151
+ // src/merkle/client.ts
1152
+ var DEFAULT_MERKLE_API_URLS2 = {
1153
+ SN_SEPOLIA: "https://merkle-api-sepolia.up.railway.app",
1154
+ SN_MAIN: "https://merkle-api.up.railway.app"
1155
+ };
1156
+ var MerkleClient = class {
1157
+ apiUrl;
1158
+ constructor(config) {
1159
+ this.apiUrl = config.apiUrl ?? DEFAULT_MERKLE_API_URLS2[config.chainId] ?? DEFAULT_MERKLE_API_URLS2.SN_MAIN;
1160
+ }
1161
+ /** Fetch paginated list of merkle trees. */
1162
+ async getTrees(options) {
1163
+ const empty = {
1164
+ data: [],
1165
+ total: 0,
1166
+ page: 1,
1167
+ limit: 20,
1168
+ totalPages: 0
1169
+ };
1170
+ try {
1171
+ const params = new URLSearchParams();
1172
+ if (options?.page) params.set("page", String(options.page));
1173
+ if (options?.limit) params.set("limit", String(options.limit));
1174
+ const qs = params.toString();
1175
+ const res = await fetch(`${this.apiUrl}/trees${qs ? `?${qs}` : ""}`);
1176
+ if (!res.ok) return empty;
1177
+ const json = await res.json();
1178
+ return {
1179
+ data: json.data ?? [],
1180
+ total: json.total ?? 0,
1181
+ page: json.page ?? 1,
1182
+ limit: json.limit ?? 20,
1183
+ totalPages: json.totalPages ?? 0
1184
+ };
1185
+ } catch {
1186
+ return empty;
1187
+ }
1188
+ }
1189
+ /** Fetch entries for a specific merkle tree. */
1190
+ async getTreeEntries(treeId, options) {
1191
+ const empty = {
1192
+ data: [],
1193
+ total: 0,
1194
+ page: 1,
1195
+ limit: 50,
1196
+ totalPages: 0
1197
+ };
1198
+ try {
1199
+ const params = new URLSearchParams();
1200
+ if (options?.page) params.set("page", String(options.page));
1201
+ if (options?.limit) params.set("limit", String(options.limit));
1202
+ const qs = params.toString();
1203
+ const res = await fetch(
1204
+ `${this.apiUrl}/trees/${treeId}/entries${qs ? `?${qs}` : ""}`
1205
+ );
1206
+ if (!res.ok) return empty;
1207
+ const json = await res.json();
1208
+ return {
1209
+ data: json.data ?? [],
1210
+ total: json.total ?? 0,
1211
+ page: json.page ?? 1,
1212
+ limit: json.limit ?? 50,
1213
+ totalPages: json.totalPages ?? 0
1214
+ };
1215
+ } catch {
1216
+ return empty;
1217
+ }
1218
+ }
1219
+ /** Fetch a merkle proof for a player address. */
1220
+ async getProof(treeId, playerAddress) {
1221
+ try {
1222
+ const res = await fetch(
1223
+ `${this.apiUrl}/trees/${treeId}/proof/${playerAddress.toLowerCase()}`
1224
+ );
1225
+ if (!res.ok) return null;
1226
+ return await res.json();
1227
+ } catch {
1228
+ return null;
1229
+ }
1230
+ }
1231
+ /** Store a merkle tree in the API (after on-chain registration). */
1232
+ async storeTree(request) {
1233
+ const res = await fetch(`${this.apiUrl}/trees`, {
1234
+ method: "POST",
1235
+ headers: { "Content-Type": "application/json" },
1236
+ body: JSON.stringify(request)
1237
+ });
1238
+ if (!res.ok) {
1239
+ const text = await res.text();
1240
+ throw new Error(`Failed to create merkle tree: ${text}`);
1241
+ }
1242
+ return await res.json();
1243
+ }
1244
+ /**
1245
+ * Build a merkle tree locally and return the calldata needed
1246
+ * to register it on-chain via the validator contract.
1247
+ *
1248
+ * The consumer is responsible for executing the call with their
1249
+ * own account/signer.
1250
+ */
1251
+ buildTreeCalldata(entries, validatorAddress) {
1252
+ const tree = buildMerkleTree(entries);
1253
+ return {
1254
+ tree,
1255
+ call: {
1256
+ contractAddress: validatorAddress,
1257
+ entrypoint: "create_tree",
1258
+ calldata: [tree.root]
1259
+ }
1260
+ };
1261
+ }
1262
+ /**
1263
+ * Parse the tree ID from transaction receipt events.
1264
+ * Returns null if the event cannot be found.
1265
+ */
1266
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1267
+ parseTreeIdFromEvents(events, validatorAddress) {
1268
+ const validatorBigInt = BigInt(validatorAddress);
1269
+ const treeEvent = events.find((e) => {
1270
+ try {
1271
+ return BigInt(e.from_address) === validatorBigInt;
1272
+ } catch {
1273
+ return false;
1274
+ }
1275
+ });
1276
+ if (!treeEvent?.keys?.[1]) return null;
1277
+ return Number(BigInt(treeEvent.keys[1]));
1278
+ }
1279
+ /**
1280
+ * Store the tree in the API after on-chain registration.
1281
+ * Pass the tree ID from the on-chain event along with the original entries.
1282
+ */
1283
+ async createTree(params) {
1284
+ return this.storeTree({
1285
+ id: params.treeId,
1286
+ name: params.name,
1287
+ description: params.description,
1288
+ entries: params.entries
1289
+ });
1290
+ }
1291
+ };
1292
+
1293
+ // src/client.ts
1294
+ var MetagameClient = class {
1295
+ chainId;
1296
+ merkle;
1297
+ constructor(config) {
1298
+ this.chainId = config.chainId;
1299
+ const merkleConfig = {
1300
+ chainId: config.chainId,
1301
+ apiUrl: config.merkleApiUrl
1302
+ };
1303
+ this.merkle = new MerkleClient(merkleConfig);
1304
+ }
1305
+ };
1306
+ function createMetagameClient(config) {
1307
+ return new MetagameClient(config);
1308
+ }
1309
+
1310
+ exports.MetagameClient = MetagameClient;
1015
1311
  exports.QualifyingMode = QualifyingMode;
1016
1312
  exports.aggregatePrizesByPosition = aggregatePrizesByPosition;
1017
1313
  exports.aggregatePrizesBySponsor = aggregatePrizesBySponsor;
@@ -1031,11 +1327,16 @@ exports.calculatePayouts = calculatePayouts;
1031
1327
  exports.calculatePrizeValue = calculatePrizeValue;
1032
1328
  exports.calculateTotalPrizeValueUSD = calculateTotalPrizeValueUSD;
1033
1329
  exports.computeStatus = computeStatus;
1330
+ exports.createMerkleTree = createMerkleTree;
1331
+ exports.createMetagameClient = createMetagameClient;
1034
1332
  exports.displayAddress = displayAddress;
1035
1333
  exports.distributePool = distributePool;
1036
1334
  exports.evaluateExtensionQualification = evaluateExtensionQualification;
1037
1335
  exports.evaluateQualification = evaluateQualification;
1038
1336
  exports.evaluateTokenQualification = evaluateTokenQualification;
1337
+ exports.fetchMerkleProof = fetchMerkleProof;
1338
+ exports.fetchMerkleTreeEntries = fetchMerkleTreeEntries;
1339
+ exports.fetchMerkleTrees = fetchMerkleTrees;
1039
1340
  exports.filterClaimablePrizes = filterClaimablePrizes;
1040
1341
  exports.filterZeroPrizes = filterZeroPrizes;
1041
1342
  exports.findAllBannableEntries = findAllBannableEntries;
@@ -1048,6 +1349,7 @@ exports.formatTime = formatTime;
1048
1349
  exports.formatTokenAmount = formatTokenAmount;
1049
1350
  exports.formatUsdValue = formatUsdValue;
1050
1351
  exports.getExtensionAddresses = getExtensionAddresses;
1352
+ exports.getMerkleApiUrl = getMerkleApiUrl;
1051
1353
  exports.getOpusSupportedAssets = getOpusSupportedAssets;
1052
1354
  exports.getOrdinalSuffix = getOrdinalSuffix;
1053
1355
  exports.getQualifyingModeInfo = getQualifyingModeInfo;
@@ -1061,11 +1363,13 @@ exports.padAddress = padAddress;
1061
1363
  exports.parseERC20BalanceValidatorConfig = parseERC20BalanceValidatorConfig;
1062
1364
  exports.parseExtensionConfig = parseExtensionConfig;
1063
1365
  exports.parseGovernanceValidatorConfig = parseGovernanceValidatorConfig;
1366
+ exports.parseMerkleValidatorConfig = parseMerkleValidatorConfig;
1064
1367
  exports.parseNFTBulkInput = parseNFTBulkInput;
1065
1368
  exports.parseOpusTrovesValidatorConfig = parseOpusTrovesValidatorConfig;
1066
1369
  exports.parseSnapshotValidatorConfig = parseSnapshotValidatorConfig;
1067
1370
  exports.parseTournamentValidatorConfig = parseTournamentValidatorConfig;
1068
1371
  exports.parseZkPassportValidatorConfig = parseZkPassportValidatorConfig;
1069
1372
  exports.resolveTournamentQualifications = resolveTournamentQualifications;
1373
+ exports.setMerkleApiUrl = setMerkleApiUrl;
1070
1374
  //# sourceMappingURL=index.cjs.map
1071
1375
  //# sourceMappingURL=index.cjs.map