@playmos/sdk 0.3.10 → 0.3.12

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
@@ -267,10 +267,10 @@ function validateMetadata(metadata) {
267
267
 
268
268
  // src/payout.ts
269
269
  var PayoutError = class extends Error {
270
- constructor(message) {
270
+ constructor(message, code = "payout_invalid") {
271
271
  super(message);
272
- this.code = "payout_invalid";
273
272
  this.name = "PayoutError";
273
+ this.code = code;
274
274
  }
275
275
  };
276
276
  var ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
@@ -315,7 +315,8 @@ function computePayout(pool, ranking, rule) {
315
315
  }
316
316
  if (splits.length > wallets.length) {
317
317
  throw new PayoutError(
318
- `top-n needs ${splits.length} ranked wallets but ranking only has ${wallets.length}`
318
+ `ranking_too_short: a top-n rule cannot be settled from a ranking shorter than splitsBps.length (need ${splits.length}, got ${wallets.length}) \u2014 open with winner-take-all, or settle with explicit winners[{wallet,amount}]`,
319
+ "ranking_too_short"
319
320
  );
320
321
  }
321
322
  let sumBps = 0;
@@ -628,7 +629,7 @@ function resolveRawProvider(wallet) {
628
629
  }
629
630
  if (injected) return injected;
630
631
  throw new WalletConnectionError(
631
- "base-account connector needs a provider. In the Base App it is injected automatically; elsewhere, create one with @base-org/account and pass it as wallet.provider."
632
+ 'base-account connector needs a provider \u2014 this is not a built-in Base Account/passkey flow in @playmos/sdk. In the Base App a provider is often injected; elsewhere install @base-org/account, construct a provider, and pass it as wallet.provider (or use connector: "injected" in a wallet browser).'
632
633
  );
633
634
  }
634
635
  function resolveProvider(wallet, opts) {
@@ -801,6 +802,182 @@ async function assertEnoughGas(provider, from, minWei) {
801
802
  });
802
803
  }
803
804
  }
805
+ function seriesToBytes32(series) {
806
+ const s = series.trim();
807
+ if (/^0x[0-9a-fA-F]{64}$/.test(s)) return s.toLowerCase();
808
+ return viem.keccak256(viem.toHex(s));
809
+ }
810
+ var identityToBytes32 = seriesToBytes32;
811
+
812
+ // src/chain/epochPrizePoolAbi.ts
813
+ var epochPrizePoolViewAbi = [
814
+ {
815
+ type: "function",
816
+ name: "currentEpochId",
817
+ stateMutability: "view",
818
+ inputs: [{ name: "series", type: "bytes32" }],
819
+ outputs: [{ type: "uint256" }]
820
+ },
821
+ {
822
+ type: "function",
823
+ name: "epochIdAt",
824
+ stateMutability: "view",
825
+ inputs: [
826
+ { name: "series", type: "bytes32" },
827
+ { name: "timestamp", type: "uint256" }
828
+ ],
829
+ outputs: [{ type: "uint256" }]
830
+ },
831
+ {
832
+ type: "function",
833
+ name: "epochPool",
834
+ stateMutability: "view",
835
+ inputs: [
836
+ { name: "series", type: "bytes32" },
837
+ { name: "epochId", type: "uint256" }
838
+ ],
839
+ outputs: [{ type: "uint256" }]
840
+ },
841
+ {
842
+ type: "function",
843
+ name: "epochOutgoingSeed",
844
+ stateMutability: "view",
845
+ inputs: [
846
+ { name: "series", type: "bytes32" },
847
+ { name: "epochId", type: "uint256" }
848
+ ],
849
+ outputs: [{ type: "uint256" }]
850
+ },
851
+ {
852
+ type: "function",
853
+ name: "epochIncomingSeed",
854
+ stateMutability: "view",
855
+ inputs: [
856
+ { name: "series", type: "bytes32" },
857
+ { name: "epochId", type: "uint256" }
858
+ ],
859
+ outputs: [{ type: "uint256" }]
860
+ },
861
+ {
862
+ type: "function",
863
+ name: "epochEntryCount",
864
+ stateMutability: "view",
865
+ inputs: [
866
+ { name: "series", type: "bytes32" },
867
+ { name: "epochId", type: "uint256" }
868
+ ],
869
+ outputs: [{ type: "uint256" }]
870
+ },
871
+ {
872
+ type: "function",
873
+ name: "getSeries",
874
+ stateMutability: "view",
875
+ inputs: [{ name: "series", type: "bytes32" }],
876
+ outputs: [
877
+ { name: "created", type: "bool" },
878
+ { name: "genesis", type: "uint256" },
879
+ { name: "epochDuration", type: "uint256" },
880
+ { name: "entry", type: "uint256" },
881
+ { name: "feeBps", type: "uint16" },
882
+ { name: "poolBps", type: "uint16" },
883
+ { name: "seedBps", type: "uint16" }
884
+ ]
885
+ },
886
+ {
887
+ type: "function",
888
+ name: "getEpoch",
889
+ stateMutability: "view",
890
+ inputs: [
891
+ { name: "series", type: "bytes32" },
892
+ { name: "epochId", type: "uint256" }
893
+ ],
894
+ outputs: [
895
+ { name: "pool", type: "uint256" },
896
+ { name: "outgoingSeed", type: "uint256" },
897
+ { name: "incomingSeed", type: "uint256" },
898
+ { name: "entryCount_", type: "uint256" },
899
+ { name: "terminal", type: "uint8" }
900
+ ]
901
+ },
902
+ {
903
+ type: "function",
904
+ name: "entryCount",
905
+ stateMutability: "view",
906
+ inputs: [
907
+ { name: "series", type: "bytes32" },
908
+ { name: "epochId", type: "uint256" },
909
+ { name: "identity", type: "bytes32" }
910
+ ],
911
+ outputs: [{ type: "uint256" }]
912
+ },
913
+ {
914
+ type: "function",
915
+ name: "claimableRefund",
916
+ stateMutability: "view",
917
+ inputs: [
918
+ { name: "series", type: "bytes32" },
919
+ { name: "epochId", type: "uint256" },
920
+ { name: "payer", type: "address" }
921
+ ],
922
+ outputs: [{ type: "uint256" }]
923
+ },
924
+ {
925
+ type: "function",
926
+ name: "withdrawable",
927
+ stateMutability: "view",
928
+ inputs: [{ name: "account", type: "address" }],
929
+ outputs: [{ type: "uint256" }]
930
+ }
931
+ ];
932
+ var epochPrizePoolEnterAbi = [
933
+ {
934
+ type: "function",
935
+ name: "enter",
936
+ stateMutability: "nonpayable",
937
+ inputs: [
938
+ { name: "series", type: "bytes32" },
939
+ { name: "identity", type: "bytes32" }
940
+ ],
941
+ outputs: []
942
+ }
943
+ ];
944
+ var epochPrizePoolRefundAbi = [
945
+ {
946
+ type: "function",
947
+ name: "claimRefund",
948
+ stateMutability: "nonpayable",
949
+ inputs: [
950
+ { name: "series", type: "bytes32" },
951
+ { name: "epochId", type: "uint256" },
952
+ { name: "payer", type: "address" }
953
+ ],
954
+ outputs: [{ name: "amount", type: "uint256" }]
955
+ },
956
+ {
957
+ type: "function",
958
+ name: "withdraw",
959
+ stateMutability: "nonpayable",
960
+ inputs: [],
961
+ outputs: []
962
+ }
963
+ ];
964
+ var epochPrizePoolExecuteSettlementAbi = [
965
+ {
966
+ type: "function",
967
+ name: "executeSignedSettlement",
968
+ stateMutability: "nonpayable",
969
+ inputs: [
970
+ { name: "series", type: "bytes32" },
971
+ { name: "epochId", type: "uint256" },
972
+ { name: "winners", type: "address[]" },
973
+ { name: "amounts", type: "uint256[]" },
974
+ { name: "signature", type: "bytes" }
975
+ ],
976
+ outputs: []
977
+ }
978
+ ];
979
+
980
+ // src/chain/calls.ts
804
981
  var toBytes32 = (s) => viem.keccak256(viem.toBytes(s));
805
982
  function buildIapCalls(args) {
806
983
  const payData = viem.encodeFunctionData({
@@ -832,6 +1009,53 @@ function buildWithdrawCall(prizePool) {
832
1009
  });
833
1010
  return { to: prizePool, data };
834
1011
  }
1012
+ function buildEpochExecuteSettlementCall(args) {
1013
+ return {
1014
+ to: args.epochPrizePool,
1015
+ data: viem.encodeFunctionData({
1016
+ abi: epochPrizePoolExecuteSettlementAbi,
1017
+ functionName: "executeSignedSettlement",
1018
+ args: [
1019
+ seriesToBytes32(args.series),
1020
+ args.epochId,
1021
+ args.winners.map((w) => viem.getAddress(w.toLowerCase())),
1022
+ args.amounts,
1023
+ args.signature
1024
+ ]
1025
+ })
1026
+ };
1027
+ }
1028
+ function buildEpochEntryCalls(args) {
1029
+ const enterData = viem.encodeFunctionData({
1030
+ abi: epochPrizePoolEnterAbi,
1031
+ functionName: "enter",
1032
+ args: [seriesToBytes32(args.series), identityToBytes32(args.identity)]
1033
+ });
1034
+ return [
1035
+ { to: args.usdc, data: encodeApprove(args.epochPrizePool, args.entryMicro) },
1036
+ { to: args.epochPrizePool, data: enterData }
1037
+ ];
1038
+ }
1039
+ function buildEpochClaimRefundCall(args) {
1040
+ return {
1041
+ to: args.epochPrizePool,
1042
+ data: viem.encodeFunctionData({
1043
+ abi: epochPrizePoolRefundAbi,
1044
+ functionName: "claimRefund",
1045
+ args: [seriesToBytes32(args.series), args.epochId, args.payer]
1046
+ })
1047
+ };
1048
+ }
1049
+ function buildEpochWithdrawCall(epochPrizePool) {
1050
+ return {
1051
+ to: epochPrizePool,
1052
+ data: viem.encodeFunctionData({
1053
+ abi: epochPrizePoolRefundAbi,
1054
+ functionName: "withdraw",
1055
+ args: []
1056
+ })
1057
+ };
1058
+ }
835
1059
 
836
1060
  // src/mock.ts
837
1061
  var MOCK_TX = `0x${"0".repeat(56)}deadbeef`;
@@ -893,13 +1117,893 @@ function mockVerifyResult(payment) {
893
1117
  return { ...payment, mock: true };
894
1118
  }
895
1119
 
896
- // src/x402.ts
1120
+ // src/epochs.ts
1121
+ var PLAYMOS_FEE_SINK_DEFAULT = "0x1b8031e20ed96131a849a52290b4d640f286998d";
1122
+ var EPOCH_POOL_CONSTRUCTOR_TYPES = [
1123
+ { type: "address" },
1124
+ { type: "address" },
1125
+ { type: "address" },
1126
+ { type: "address" },
1127
+ { type: "uint256" }
1128
+ ];
1129
+ function encodeEpochPoolConstructorArgs(args) {
1130
+ return viem.encodeAbiParameters(EPOCH_POOL_CONSTRUCTOR_TYPES, [
1131
+ args.token,
1132
+ args.admin,
1133
+ args.operator,
1134
+ args.feeSink,
1135
+ args.refundTimeout
1136
+ ]);
1137
+ }
1138
+ function derivedEpochId(genesis, epochDuration, at) {
1139
+ if (epochDuration === 0n) {
1140
+ throw new ConfigError("epochDuration must be > 0");
1141
+ }
1142
+ if (at < genesis) {
1143
+ throw new ConfigError("epoch has not started (at < genesis)");
1144
+ }
1145
+ return (at - genesis) / epochDuration;
1146
+ }
1147
+ function epochPayableMicro(incomingSeed, pool) {
1148
+ return incomingSeed + pool;
1149
+ }
1150
+ function mockPodiumPayable(winners) {
1151
+ if (winners.length === 0) return void 0;
1152
+ let total = 0n;
1153
+ for (const w of winners) {
1154
+ if (typeof w === "string") return void 0;
1155
+ if (w.amountMicro != null && w.amountMicro !== "") {
1156
+ try {
1157
+ total += BigInt(w.amountMicro);
1158
+ } catch {
1159
+ return void 0;
1160
+ }
1161
+ continue;
1162
+ }
1163
+ if (w.amount) {
1164
+ try {
1165
+ total += parseUsdToMicro(w.amount);
1166
+ } catch {
1167
+ return void 0;
1168
+ }
1169
+ continue;
1170
+ }
1171
+ return void 0;
1172
+ }
1173
+ return {
1174
+ payable: formatMicroToUsd(total),
1175
+ payableMicro: total.toString()
1176
+ };
1177
+ }
1178
+ function mockWinnerMicro(w) {
1179
+ if (typeof w === "string") return void 0;
1180
+ if (w.amountMicro != null && w.amountMicro !== "" && /^\d+$/.test(w.amountMicro)) {
1181
+ return BigInt(w.amountMicro);
1182
+ }
1183
+ if (w.amount) {
1184
+ try {
1185
+ return parseUsdToMicro(w.amount);
1186
+ } catch {
1187
+ return void 0;
1188
+ }
1189
+ }
1190
+ return void 0;
1191
+ }
1192
+ function mockWinnerAmount(w) {
1193
+ const micro = mockWinnerMicro(w);
1194
+ if (micro === void 0) return {};
1195
+ return { amount: formatMicroToUsd(micro), amountMicro: micro.toString() };
1196
+ }
1197
+ var TERMINALS = ["none", "settled", "refunded", "rolled"];
1198
+ function terminalFromChain(raw) {
1199
+ return TERMINALS[raw] ?? "none";
1200
+ }
1201
+ function requireSeries(series) {
1202
+ if (typeof series !== "string" || series.trim() === "") {
1203
+ throw new MissingFieldError("series");
1204
+ }
1205
+ return series.trim();
1206
+ }
1207
+ function requireIdentity(identity) {
1208
+ if (typeof identity !== "string" || identity.trim() === "") {
1209
+ throw new MissingFieldError("identity");
1210
+ }
1211
+ return identity.trim();
1212
+ }
1213
+ function assertPinParity(label, local, fromService) {
1214
+ if (!fromService) {
1215
+ throw new ConfigError(
1216
+ `epochs.enter: the service did not return ${label}Bytes32 \u2014 cannot prove the on-chain id matches before paying.`,
1217
+ { field: `${label}Bytes32` }
1218
+ );
1219
+ }
1220
+ if (local.toLowerCase() !== fromService.toLowerCase()) {
1221
+ throw new ConfigError(
1222
+ `epochs.enter: ${label} derives differently in the SDK and the service (sdk=${local}, service=${fromService}). Refusing to enter \u2014 a paid entry under a mismatched id is never scored. See sdk#633.`,
1223
+ { field: `${label}Bytes32` }
1224
+ );
1225
+ }
1226
+ }
1227
+ function epochPathId(epochId) {
1228
+ if (epochId === void 0 || epochId === null || epochId === "") return "current";
1229
+ const s = String(epochId).trim();
1230
+ if (s === "current") return "current";
1231
+ if (!/^\d+$/.test(s)) {
1232
+ throw new ConfigError(`epochId must be a non-negative integer, got: ${JSON.stringify(epochId)}`);
1233
+ }
1234
+ return s;
1235
+ }
1236
+ function qs(params) {
1237
+ const u = new URLSearchParams();
1238
+ for (const [k, v] of Object.entries(params)) {
1239
+ if (v) u.set(k, v);
1240
+ }
1241
+ const s = u.toString();
1242
+ return s ? `?${s}` : "";
1243
+ }
1244
+ function pictureFromMicros(args) {
1245
+ const payableMicro = epochPayableMicro(args.incomingSeedMicro, args.poolMicro);
1246
+ return {
1247
+ series: args.series,
1248
+ epochId: args.epochId,
1249
+ pool: formatMicroToUsd(args.poolMicro),
1250
+ incomingSeed: formatMicroToUsd(args.incomingSeedMicro),
1251
+ outgoingSeed: formatMicroToUsd(args.outgoingSeedMicro),
1252
+ payable: formatMicroToUsd(payableMicro),
1253
+ poolMicro: args.poolMicro.toString(),
1254
+ incomingSeedMicro: args.incomingSeedMicro.toString(),
1255
+ outgoingSeedMicro: args.outgoingSeedMicro.toString(),
1256
+ payableMicro: payableMicro.toString(),
1257
+ via: args.via,
1258
+ epochPrizePool: args.epochPrizePool,
1259
+ ...args.mock ? { mock: true } : {}
1260
+ };
1261
+ }
1262
+ function toBig(value, fallback) {
1263
+ if (value === void 0) return fallback;
1264
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) return BigInt(Math.trunc(value));
1265
+ if (typeof value === "string" && /^\d+$/.test(value.trim())) return BigInt(value.trim());
1266
+ throw new ConfigError(`expected a non-negative integer, got: ${JSON.stringify(value)}`);
1267
+ }
897
1268
  var ADDRESS_RE2 = /^0x[0-9a-fA-F]{40}$/;
1269
+ function requireAddr(raw, field) {
1270
+ if (typeof raw !== "string" || !ADDRESS_RE2.test(raw.trim())) {
1271
+ throw new MissingFieldError(field);
1272
+ }
1273
+ return raw.trim().toLowerCase();
1274
+ }
1275
+ function requireAddrKeepCase(raw, field) {
1276
+ if (typeof raw !== "string" || !ADDRESS_RE2.test(raw.trim())) {
1277
+ throw new MissingFieldError(field);
1278
+ }
1279
+ return raw.trim();
1280
+ }
1281
+ function requireEpochPool(input, cfg) {
1282
+ const raw = input?.epochPrizePool ?? cfg.contracts?.epochPrizePool;
1283
+ if (!raw) {
1284
+ throw new ConfigError(
1285
+ "epochs refund rail needs contracts.epochPrizePool (or pass epochPrizePool) \u2014 this is EpochPrizePool, not PrizePool",
1286
+ { field: "epochPrizePool" }
1287
+ );
1288
+ }
1289
+ return requireAddr(raw, "epochPrizePool");
1290
+ }
1291
+ function requirePastEpochId(epochId) {
1292
+ const id = epochPathId(epochId);
1293
+ if (id === "current") {
1294
+ throw new ConfigError("epochs refund rail requires a past epochId \u2014 Current is still taking entries");
1295
+ }
1296
+ return id;
1297
+ }
1298
+ function walletStatus(status) {
1299
+ if (status === "CONFIRMED") return "confirmed";
1300
+ if (status === "FAILED") return "failed";
1301
+ return "pending";
1302
+ }
1303
+ function sameAddr(a, b) {
1304
+ return a.toLowerCase() === b.toLowerCase();
1305
+ }
1306
+ function prepareStudioPoolLocal(input, playmosFeeSink, token, playmosAddresses = []) {
1307
+ const studio = requireAddr(input.studioWallet, "studioWallet");
1308
+ const sink = playmosFeeSink.toLowerCase();
1309
+ const forbidden = [sink, ...playmosAddresses.map((a) => a.toLowerCase())];
1310
+ if (forbidden.some((a) => sameAddr(a, studio))) {
1311
+ throw new ConfigError("studioWallet must not be a Playmos address (Playmos never holds studio keys)");
1312
+ }
1313
+ if (input.feeSink && !sameAddr(input.feeSink, sink)) {
1314
+ throw new ConfigError("feeSink must equal the Playmos sink; refused before broadcast");
1315
+ }
1316
+ if (input.admin && !sameAddr(input.admin, studio)) {
1317
+ throw new ConfigError("admin must be the studio wallet from block one");
1318
+ }
1319
+ if (input.operator && !sameAddr(input.operator, studio)) {
1320
+ throw new ConfigError("operator must be the studio wallet from block one");
1321
+ }
1322
+ if (input.admin && forbidden.some((a) => sameAddr(a, input.admin))) {
1323
+ throw new ConfigError("Playmos must not be ADMIN_ROLE");
1324
+ }
1325
+ if (input.operator && forbidden.some((a) => sameAddr(a, input.operator))) {
1326
+ throw new ConfigError("Playmos must not be OPERATOR_ROLE");
1327
+ }
1328
+ const refundTimeout = toBig(input.refundTimeout, 604800n);
1329
+ if (refundTimeout < 3600n || refundTimeout > 7776000n) {
1330
+ throw new ConfigError("refundTimeout must be between 3600 (1h) and 7776000 (90d) seconds");
1331
+ }
1332
+ return {
1333
+ token: token.toLowerCase(),
1334
+ admin: studio,
1335
+ operator: studio,
1336
+ feeSink: sink,
1337
+ refundTimeout: refundTimeout.toString(),
1338
+ constructorArgs: encodeEpochPoolConstructorArgs({
1339
+ token: token.toLowerCase(),
1340
+ admin: studio,
1341
+ operator: studio,
1342
+ feeSink: sink,
1343
+ refundTimeout
1344
+ }),
1345
+ constructor: "EpochPrizePool",
1346
+ studioHoldsAdmin: true,
1347
+ studioHoldsOperator: true,
1348
+ playmosIsAdmin: false,
1349
+ playmosIsOperator: false,
1350
+ via: "mock",
1351
+ mock: true
1352
+ };
1353
+ }
1354
+ function epochPoolProofMessage(terms) {
1355
+ return [
1356
+ "Playmos EpochPrizePool registration",
1357
+ `studio: ${terms.studioId}`,
1358
+ `pool: ${terms.poolAddress.toLowerCase()}`,
1359
+ `wallet: ${terms.studioWallet.toLowerCase()}`,
1360
+ `chainId: ${terms.chainId}`
1361
+ ].join("\n");
1362
+ }
1363
+ async function readWithdrawableMicro(deps, pool) {
1364
+ const read = deps.readView;
1365
+ const account = deps.walletAddress;
1366
+ if (!read || !account) return null;
1367
+ try {
1368
+ const raw = await read(
1369
+ pool,
1370
+ viem.encodeFunctionData({
1371
+ abi: epochPrizePoolViewAbi,
1372
+ functionName: "withdrawable",
1373
+ args: [await account()]
1374
+ })
1375
+ );
1376
+ return viem.decodeFunctionResult({
1377
+ abi: epochPrizePoolViewAbi,
1378
+ functionName: "withdrawable",
1379
+ data: raw
1380
+ });
1381
+ } catch {
1382
+ return null;
1383
+ }
1384
+ }
1385
+ function settlementTypedData(input) {
1386
+ const pool = requireAddr(input.pool, "pool");
1387
+ const series = requireSeries(input.series);
1388
+ const epochId = requirePastEpochId(input.epochId);
1389
+ if (!Array.isArray(input.winners) || !Array.isArray(input.amounts)) {
1390
+ throw new MissingFieldError("winners");
1391
+ }
1392
+ if (input.winners.length !== input.amounts.length) {
1393
+ throw new ConfigError("winners and amounts must be the same length");
1394
+ }
1395
+ return {
1396
+ domain: {
1397
+ name: "EpochPrizePool",
1398
+ version: "1",
1399
+ chainId: input.chainId,
1400
+ verifyingContract: pool
1401
+ },
1402
+ types: {
1403
+ Settlement: [
1404
+ { name: "series", type: "bytes32" },
1405
+ { name: "epochId", type: "uint256" },
1406
+ { name: "winners", type: "address[]" },
1407
+ { name: "amounts", type: "uint256[]" }
1408
+ ]
1409
+ },
1410
+ primaryType: "Settlement",
1411
+ message: {
1412
+ series: seriesToBytes32(series),
1413
+ epochId: BigInt(epochId),
1414
+ winners: input.winners.map((w, i) => requireAddrKeepCase(w, `winners[${i}]`)),
1415
+ amounts: input.amounts.map((a, i) => parseMicroString(a, `amounts[${i}]`))
1416
+ }
1417
+ };
1418
+ }
1419
+ function parseMicroString(raw, field) {
1420
+ if (typeof raw !== "string" || !/^(0|[1-9]\d*)$/.test(raw)) {
1421
+ throw new ConfigError(`${field} must be an integer micro-USDC string`, { field });
1422
+ }
1423
+ return BigInt(raw);
1424
+ }
1425
+ async function readEpochTerminal(deps, pool, series, epochId) {
1426
+ const read = deps.readView;
1427
+ if (!read) return null;
1428
+ try {
1429
+ const raw = await read(
1430
+ pool,
1431
+ viem.encodeFunctionData({
1432
+ abi: epochPrizePoolViewAbi,
1433
+ functionName: "getEpoch",
1434
+ args: [seriesToBytes32(series), BigInt(epochId)]
1435
+ })
1436
+ );
1437
+ const decoded = viem.decodeFunctionResult({
1438
+ abi: epochPrizePoolViewAbi,
1439
+ functionName: "getEpoch",
1440
+ data: raw
1441
+ });
1442
+ const termRaw = decoded && typeof decoded === "object" && "terminal" in decoded ? Number(decoded.terminal) : Number(decoded[4]);
1443
+ return terminalFromChain(termRaw);
1444
+ } catch {
1445
+ return null;
1446
+ }
1447
+ }
1448
+ function createEpochsApi(deps) {
1449
+ return {
1450
+ async currentId(input) {
1451
+ const series = requireSeries(input?.series);
1452
+ const cfg = deps.config();
1453
+ if (cfg.mock) {
1454
+ const genesis = toBig(input.genesis, 0n);
1455
+ const duration = toBig(input.epochDuration, 1n);
1456
+ const at = toBig(input.at, BigInt(Math.floor(Date.now() / 1e3)));
1457
+ const epochId = derivedEpochId(genesis, duration, at);
1458
+ return {
1459
+ series,
1460
+ epochId: epochId.toString(),
1461
+ via: "mock",
1462
+ epochPrizePool: input.epochPrizePool ?? cfg.contracts?.epochPrizePool ?? null,
1463
+ mock: true
1464
+ };
1465
+ }
1466
+ const path = `/epochs/current${qs({
1467
+ series,
1468
+ epochPrizePool: input.epochPrizePool ?? cfg.contracts?.epochPrizePool
1469
+ })}`;
1470
+ return deps.http().get(path);
1471
+ },
1472
+ async prize(input) {
1473
+ const series = requireSeries(input?.series);
1474
+ const cfg = deps.config();
1475
+ const epochId = epochPathId(input?.epochId);
1476
+ if (cfg.mock) {
1477
+ return pictureFromMicros({
1478
+ series,
1479
+ epochId: epochId === "current" ? "0" : epochId,
1480
+ poolMicro: 0n,
1481
+ incomingSeedMicro: 0n,
1482
+ outgoingSeedMicro: 0n,
1483
+ via: "mock",
1484
+ epochPrizePool: input.epochPrizePool ?? cfg.contracts?.epochPrizePool ?? null,
1485
+ mock: true
1486
+ });
1487
+ }
1488
+ const path = `/epochs/${encodeURIComponent(epochId)}/prize${qs({
1489
+ series,
1490
+ epochPrizePool: input.epochPrizePool ?? cfg.contracts?.epochPrizePool
1491
+ })}`;
1492
+ const res = await deps.http().get(path);
1493
+ return res.prize;
1494
+ },
1495
+ async get(input) {
1496
+ const series = requireSeries(input?.series);
1497
+ const cfg = deps.config();
1498
+ const epochId = epochPathId(input?.epochId);
1499
+ if (cfg.mock) {
1500
+ const prize = pictureFromMicros({
1501
+ series,
1502
+ epochId: epochId === "current" ? "0" : epochId,
1503
+ poolMicro: 0n,
1504
+ incomingSeedMicro: 0n,
1505
+ outgoingSeedMicro: 0n,
1506
+ via: "mock",
1507
+ epochPrizePool: input.epochPrizePool ?? cfg.contracts?.epochPrizePool ?? null,
1508
+ mock: true
1509
+ });
1510
+ return {
1511
+ ...prize,
1512
+ entryCount: "0",
1513
+ terminal: "none"
1514
+ };
1515
+ }
1516
+ const path = `/epochs/${encodeURIComponent(epochId)}${qs({
1517
+ series,
1518
+ epochPrizePool: input.epochPrizePool ?? cfg.contracts?.epochPrizePool
1519
+ })}`;
1520
+ return deps.http().get(path);
1521
+ },
1522
+ async getSeries(input) {
1523
+ const series = requireSeries(input?.series);
1524
+ const cfg = deps.config();
1525
+ if (cfg.mock) {
1526
+ return {
1527
+ series,
1528
+ created: true,
1529
+ genesis: "0",
1530
+ epochDuration: "1",
1531
+ entry: "0",
1532
+ feeBps: 1e3,
1533
+ poolBps: 6e3,
1534
+ seedBps: 3e3,
1535
+ via: "mock",
1536
+ epochPrizePool: input.epochPrizePool ?? cfg.contracts?.epochPrizePool ?? null,
1537
+ mock: true
1538
+ };
1539
+ }
1540
+ const path = `/epochs/series${qs({
1541
+ series,
1542
+ epochPrizePool: input.epochPrizePool ?? cfg.contracts?.epochPrizePool
1543
+ })}`;
1544
+ return deps.http().get(path);
1545
+ },
1546
+ async preparePool(input) {
1547
+ const studioWallet = requireAddr(input?.studioWallet, "studioWallet");
1548
+ const cfg = deps.config();
1549
+ const sink = (input.playmosFeeSink ?? PLAYMOS_FEE_SINK_DEFAULT).toLowerCase();
1550
+ const token = (input.token ?? cfg.contracts?.usdc ?? "0x036cbd53842c5426634e7929541ec2318f3dcf7e").toLowerCase();
1551
+ if (cfg.mock) {
1552
+ return prepareStudioPoolLocal({ ...input, studioWallet }, sink, token);
1553
+ }
1554
+ return deps.http().post("/epochs/pools/prepare", {
1555
+ studioWallet,
1556
+ feeSink: input.feeSink,
1557
+ admin: input.admin,
1558
+ operator: input.operator,
1559
+ refundTimeout: input.refundTimeout
1560
+ });
1561
+ },
1562
+ async registerPool(input) {
1563
+ const studioWallet = requireAddr(input?.studioWallet, "studioWallet");
1564
+ const poolAddress = requireAddr(input?.poolAddress, "poolAddress");
1565
+ const cfg = deps.config();
1566
+ if (cfg.mock) {
1567
+ const sink = (input.feeSink ?? PLAYMOS_FEE_SINK_DEFAULT).toLowerCase();
1568
+ const token = (cfg.contracts?.usdc ?? "0x036cbd53842c5426634e7929541ec2318f3dcf7e").toLowerCase();
1569
+ const prepared = prepareStudioPoolLocal({ studioWallet, feeSink: input.feeSink, admin: input.admin, operator: input.operator }, sink, token);
1570
+ return {
1571
+ poolAddress,
1572
+ studioWallet: prepared.admin,
1573
+ feeSink: prepared.feeSink,
1574
+ admin: prepared.admin,
1575
+ operator: prepared.operator,
1576
+ studioHoldsAdmin: true,
1577
+ studioHoldsOperator: true,
1578
+ playmosIsAdmin: false,
1579
+ playmosIsOperator: false,
1580
+ ownership: input.walletProof ? "confirmed" : "pending",
1581
+ owner: null,
1582
+ via: "mock",
1583
+ mock: true
1584
+ };
1585
+ }
1586
+ return deps.http().post("/epochs/pools", {
1587
+ studioWallet,
1588
+ poolAddress,
1589
+ feeSink: input.feeSink,
1590
+ admin: input.admin,
1591
+ operator: input.operator,
1592
+ txHash: input.txHash,
1593
+ walletProof: input.walletProof
1594
+ });
1595
+ },
1596
+ async createPool(input) {
1597
+ const prepared = await this.preparePool(input);
1598
+ const cfg = deps.config();
1599
+ if (cfg.mock) {
1600
+ const poolAddress = input.poolAddress ? requireAddr(input.poolAddress, "poolAddress") : `0x${prepared.constructorArgs.slice(2, 42).padEnd(40, "0")}`;
1601
+ return this.registerPool({
1602
+ studioWallet: prepared.admin,
1603
+ poolAddress,
1604
+ feeSink: prepared.feeSink,
1605
+ walletProof: input.walletProof
1606
+ });
1607
+ }
1608
+ if (!input.poolAddress) {
1609
+ throw new ConfigError(
1610
+ "createPool on the live path needs poolAddress after the studio wallet submits the constructor \u2014 Playmos does not broadcast"
1611
+ );
1612
+ }
1613
+ return this.registerPool({
1614
+ studioWallet: prepared.admin,
1615
+ poolAddress: input.poolAddress,
1616
+ feeSink: prepared.feeSink,
1617
+ txHash: input.txHash,
1618
+ walletProof: input.walletProof
1619
+ });
1620
+ },
1621
+ async settle(input) {
1622
+ const series = requireSeries(input?.series);
1623
+ if (!Array.isArray(input?.winners)) {
1624
+ throw new MissingFieldError("winners");
1625
+ }
1626
+ const epochId = epochPathId(input?.epochId);
1627
+ if (epochId === "current") {
1628
+ throw new ConfigError("epochs.settle requires a past epochId \u2014 Current is still taking entries");
1629
+ }
1630
+ const cfg = deps.config();
1631
+ if (!cfg.mock) {
1632
+ if (deps.assertSecretKey) deps.assertSecretKey("playmos.epochs.settle");
1633
+ else if (!String(cfg.apiKey ?? "").startsWith("sk_")) {
1634
+ throw new AuthError(
1635
+ "playmos.epochs.settle requires a secret (sk_) key \u2014 use sk_test_\u2026 / sk_live_\u2026 on the server. Publishable (pk_) keys cannot settle.",
1636
+ { surface: "playmos.epochs.settle", isSecret: false }
1637
+ );
1638
+ }
1639
+ }
1640
+ if (cfg.mock) {
1641
+ const empty = input.winners.length === 0;
1642
+ const podiumPayable = mockPodiumPayable(input.winners);
1643
+ return {
1644
+ series,
1645
+ epochId,
1646
+ status: empty ? "rolled" : "settled",
1647
+ txHash: "0x0000000000000000000000000000000000000000000000000000000000000001",
1648
+ winners: input.winners.map((w) => {
1649
+ const wallet = (typeof w === "string" ? w : w.wallet).toLowerCase();
1650
+ return { wallet, ...mockWinnerAmount(w) };
1651
+ }),
1652
+ ...podiumPayable ?? {},
1653
+ terminal: empty ? "rolled" : "settled",
1654
+ via: "mock",
1655
+ epochPrizePool: input.epochPrizePool ?? cfg.contracts?.epochPrizePool ?? null,
1656
+ mock: true
1657
+ };
1658
+ }
1659
+ const body = await deps.http().post(
1660
+ `/epochs/${encodeURIComponent(epochId)}/settle`,
1661
+ {
1662
+ series,
1663
+ winners: input.winners,
1664
+ epochPrizePool: input.epochPrizePool ?? cfg.contracts?.epochPrizePool
1665
+ },
1666
+ { acceptStatuses: [200, 202] }
1667
+ );
1668
+ return body.settle;
1669
+ },
1670
+ async enter(input) {
1671
+ const series = requireSeries(input?.series);
1672
+ const identity = requireIdentity(input?.identity);
1673
+ const cfg = deps.config();
1674
+ const seriesBytes32 = seriesToBytes32(series);
1675
+ const identityBytes32 = identityToBytes32(identity);
1676
+ if (cfg.mock) {
1677
+ const genesis = toBig(input.genesis, 0n);
1678
+ const duration = toBig(input.epochDuration, 1n);
1679
+ const at = toBig(input.at, BigInt(Math.floor(Date.now() / 1e3)));
1680
+ return {
1681
+ series,
1682
+ identity,
1683
+ epochId: derivedEpochId(genesis, duration, at).toString(),
1684
+ epochIdSource: "mock",
1685
+ seriesBytes32,
1686
+ identityBytes32,
1687
+ entry: formatMicroToUsd(C1_ENTER_SPLIT_MICRO.entry),
1688
+ entryMicro: C1_ENTER_SPLIT_MICRO.entry.toString(),
1689
+ status: "confirmed",
1690
+ txHash: MOCK_TX,
1691
+ identityEntryCount: "1",
1692
+ epochPrizePool: input.epochPrizePool ?? cfg.contracts?.epochPrizePool ?? null,
1693
+ usdc: cfg.contracts?.usdc ?? null,
1694
+ via: "mock",
1695
+ mock: true
1696
+ };
1697
+ }
1698
+ const epochPrizePool = input.epochPrizePool ?? cfg.contracts?.epochPrizePool;
1699
+ const plan = await deps.http().post("/epochs/enter", {
1700
+ series,
1701
+ identity,
1702
+ ...epochPrizePool ? { epochPrizePool } : {}
1703
+ });
1704
+ assertPinParity("series", seriesBytes32, plan.clientParams?.seriesBytes32);
1705
+ assertPinParity("identity", identityBytes32, plan.clientParams?.identityBytes32);
1706
+ const send = deps.sendEntry;
1707
+ if (!send) {
1708
+ throw new ConfigError(
1709
+ "epochs.enter needs a player wallet \u2014 pass `wallet` to the Playmos client. Entry money goes straight to the contract; Playmos never holds it. Building the calls yourself? Import `buildEpochEntryCalls`.",
1710
+ { field: "wallet" }
1711
+ );
1712
+ }
1713
+ const entryMicro = BigInt(plan.clientParams.entryMicro);
1714
+ const calls = buildEpochEntryCalls({
1715
+ usdc: plan.clientParams.usdc,
1716
+ epochPrizePool: plan.clientParams.epochPrizePool,
1717
+ series,
1718
+ identity,
1719
+ entryMicro
1720
+ });
1721
+ const sent = await send(calls, { paymasterUrl: plan.clientParams.paymasterUrl });
1722
+ const base = {
1723
+ series,
1724
+ identity,
1725
+ epochId: plan.entry.epochId,
1726
+ epochIdSource: "chain-clock",
1727
+ seriesBytes32,
1728
+ identityBytes32,
1729
+ entry: plan.entry.entry,
1730
+ entryMicro: plan.entry.entryMicro,
1731
+ status: sent.status === "FAILED" ? "failed" : "pending",
1732
+ txHash: sent.txHash,
1733
+ epochPrizePool: plan.clientParams.epochPrizePool,
1734
+ usdc: plan.clientParams.usdc,
1735
+ via: "service"
1736
+ };
1737
+ if (!sent.txHash) return base;
1738
+ try {
1739
+ const confirmed = await deps.http().post("/epochs/enter/confirm", {
1740
+ series,
1741
+ identity,
1742
+ txHash: sent.txHash,
1743
+ ...epochPrizePool ? { epochPrizePool } : {}
1744
+ });
1745
+ return {
1746
+ ...base,
1747
+ epochId: confirmed.entry.epochId,
1748
+ epochIdSource: confirmed.entry.epochIdSource,
1749
+ status: confirmed.entry.status,
1750
+ identityEntryCount: confirmed.entry.identityEntryCount
1751
+ };
1752
+ } catch {
1753
+ return base;
1754
+ }
1755
+ },
1756
+ async claimableRefund(input) {
1757
+ const series = requireSeries(input?.series);
1758
+ const payer = requireAddr(input?.payer, "payer");
1759
+ const epochId = requirePastEpochId(input?.epochId);
1760
+ const cfg = deps.config();
1761
+ const seriesBytes32 = seriesToBytes32(series);
1762
+ const epochPrizePool = input.epochPrizePool ?? cfg.contracts?.epochPrizePool ?? null;
1763
+ if (cfg.mock) {
1764
+ return {
1765
+ series,
1766
+ epochId,
1767
+ payer,
1768
+ claimable: "0.00",
1769
+ claimableMicro: "0",
1770
+ seriesBytes32,
1771
+ epochPrizePool,
1772
+ via: "mock",
1773
+ mock: true
1774
+ };
1775
+ }
1776
+ const pool = requireEpochPool(input, cfg);
1777
+ const data = viem.encodeFunctionData({
1778
+ abi: epochPrizePoolViewAbi,
1779
+ functionName: "claimableRefund",
1780
+ args: [seriesBytes32, BigInt(epochId), payer]
1781
+ });
1782
+ const read = deps.readView;
1783
+ if (!read) {
1784
+ throw new ConfigError(
1785
+ "epochs.claimableRefund needs a wallet or RPC to read EpochPrizePool \u2014 pass `wallet` to the Playmos client",
1786
+ { field: "wallet" }
1787
+ );
1788
+ }
1789
+ const raw = await read(pool, data);
1790
+ const amount = viem.decodeFunctionResult({
1791
+ abi: epochPrizePoolViewAbi,
1792
+ functionName: "claimableRefund",
1793
+ data: raw
1794
+ });
1795
+ return {
1796
+ series,
1797
+ epochId,
1798
+ payer,
1799
+ claimable: formatMicroToUsd(amount),
1800
+ claimableMicro: amount.toString(),
1801
+ seriesBytes32,
1802
+ epochPrizePool: pool,
1803
+ via: "chain"
1804
+ };
1805
+ },
1806
+ async claimRefund(input) {
1807
+ const series = requireSeries(input?.series);
1808
+ const payer = requireAddr(input?.payer, "payer");
1809
+ const epochId = requirePastEpochId(input?.epochId);
1810
+ const cfg = deps.config();
1811
+ const pool = requireEpochPool(input, cfg);
1812
+ if (cfg.mock) {
1813
+ return {
1814
+ series,
1815
+ epochId,
1816
+ payer,
1817
+ txHash: MOCK_TX,
1818
+ status: "confirmed",
1819
+ epochPrizePool: pool,
1820
+ via: "mock",
1821
+ mock: true
1822
+ };
1823
+ }
1824
+ const send = deps.sendEntry;
1825
+ if (!send) {
1826
+ throw new ConfigError(
1827
+ "epochs.claimRefund needs a player wallet \u2014 pass `wallet` to the Playmos client. The credit lands on `payer`; the connected wallet only signs.",
1828
+ { field: "wallet" }
1829
+ );
1830
+ }
1831
+ const call = buildEpochClaimRefundCall({
1832
+ epochPrizePool: pool,
1833
+ series,
1834
+ epochId: BigInt(epochId),
1835
+ payer
1836
+ });
1837
+ const sent = await send([call], {});
1838
+ return {
1839
+ series,
1840
+ epochId,
1841
+ payer,
1842
+ txHash: sent.txHash,
1843
+ status: walletStatus(sent.status),
1844
+ epochPrizePool: pool,
1845
+ via: "chain"
1846
+ };
1847
+ },
1848
+ async withdraw(input = {}) {
1849
+ const cfg = deps.config();
1850
+ const pool = requireEpochPool(input, cfg);
1851
+ if (cfg.mock) {
1852
+ return {
1853
+ epochPrizePool: pool,
1854
+ txHash: MOCK_TX,
1855
+ status: "confirmed",
1856
+ via: "mock",
1857
+ mock: true
1858
+ };
1859
+ }
1860
+ const send = deps.sendEntry;
1861
+ if (!send) {
1862
+ throw new ConfigError(
1863
+ "epochs.withdraw needs the payer wallet \u2014 pass `wallet` to the Playmos client. USDC is pulled to msg.sender on EpochPrizePool, not PrizePool.",
1864
+ { field: "wallet" }
1865
+ );
1866
+ }
1867
+ const creditedMicro = await readWithdrawableMicro(deps, pool);
1868
+ const call = buildEpochWithdrawCall(pool);
1869
+ const sent = await send([call], {});
1870
+ return {
1871
+ epochPrizePool: pool,
1872
+ ...creditedMicro === null ? {} : {
1873
+ amount: formatMicroToUsd(creditedMicro),
1874
+ amountMicro: creditedMicro.toString()
1875
+ },
1876
+ txHash: sent.txHash,
1877
+ status: walletStatus(sent.status),
1878
+ via: "chain"
1879
+ };
1880
+ },
1881
+ async getAttestation(input) {
1882
+ const series = requireSeries(input?.series);
1883
+ const epochId = requirePastEpochId(input?.epochId);
1884
+ const cfg = deps.config();
1885
+ if (cfg.mock) {
1886
+ const pool = input.epochPrizePool ?? cfg.contracts?.epochPrizePool;
1887
+ if (!pool) {
1888
+ throw new ConfigError("epochs.getAttestation needs contracts.epochPrizePool (or pass epochPrizePool)", {
1889
+ field: "epochPrizePool"
1890
+ });
1891
+ }
1892
+ return {
1893
+ series,
1894
+ epochId,
1895
+ winners: input.winners ?? [],
1896
+ amountsMicro: input.amountsMicro ?? [],
1897
+ signature: input.signature ?? `0x${"00".repeat(65)}`,
1898
+ epochPrizePool: requireAddr(pool, "epochPrizePool"),
1899
+ via: "mock",
1900
+ mock: true
1901
+ };
1902
+ }
1903
+ const path = `/epochs/${encodeURIComponent(epochId)}/attestation${qs({
1904
+ series,
1905
+ epochPrizePool: input.epochPrizePool ?? cfg.contracts?.epochPrizePool
1906
+ })}`;
1907
+ const res = await deps.http().get(path);
1908
+ const inner = res.attestation ?? res;
1909
+ if (!inner.series || !inner.winners || !inner.amountsMicro || !inner.signature || !inner.epochPrizePool) {
1910
+ throw new ConfigError("attestation response missing posted fields \u2014 refusing to invent a podium");
1911
+ }
1912
+ return {
1913
+ series: inner.series,
1914
+ epochId: String(inner.epochId ?? epochId),
1915
+ winners: inner.winners,
1916
+ amountsMicro: inner.amountsMicro,
1917
+ signature: inner.signature,
1918
+ epochPrizePool: inner.epochPrizePool,
1919
+ ...res.terminal !== void 0 ? { terminal: res.terminal } : {},
1920
+ ...res.dueAt !== void 0 ? { dueAt: res.dueAt } : {},
1921
+ via: res.via ?? "service"
1922
+ };
1923
+ },
1924
+ async executeSettlement(input) {
1925
+ const series = requireSeries(input?.series);
1926
+ const epochId = requirePastEpochId(input?.epochId);
1927
+ if (!Array.isArray(input?.winners)) throw new MissingFieldError("winners");
1928
+ if (!Array.isArray(input?.amounts)) throw new MissingFieldError("amounts");
1929
+ if (typeof input?.signature !== "string" || !/^0x[0-9a-fA-F]{130}$/.test(input.signature)) {
1930
+ throw new MissingFieldError("signature");
1931
+ }
1932
+ const winners = input.winners.map((w, i) => requireAddrKeepCase(w, `winners[${i}]`));
1933
+ const amountsMicro = input.amounts.map((a, i) => {
1934
+ parseMicroString(a, `amounts[${i}]`);
1935
+ return a;
1936
+ });
1937
+ const amounts = amountsMicro.map((a, i) => parseMicroString(a, `amounts[${i}]`));
1938
+ const cfg = deps.config();
1939
+ const pool = requireEpochPool(input, cfg);
1940
+ if (cfg.mock) {
1941
+ return {
1942
+ series,
1943
+ epochId,
1944
+ winners,
1945
+ amountsMicro,
1946
+ signature: input.signature,
1947
+ txHash: MOCK_TX,
1948
+ status: "confirmed",
1949
+ epochPrizePool: pool,
1950
+ via: "mock",
1951
+ mock: true
1952
+ };
1953
+ }
1954
+ const send = deps.sendEntry;
1955
+ if (!send) {
1956
+ throw new ConfigError(
1957
+ "epochs.executeSettlement needs a wallet \u2014 pass `wallet` to the Playmos client. The SDK relays the signed podium; it does not choose winners.",
1958
+ { field: "wallet" }
1959
+ );
1960
+ }
1961
+ const call = buildEpochExecuteSettlementCall({
1962
+ epochPrizePool: pool,
1963
+ series,
1964
+ epochId: BigInt(epochId),
1965
+ winners,
1966
+ amounts,
1967
+ signature: input.signature
1968
+ });
1969
+ let sent;
1970
+ try {
1971
+ sent = await send([call], {});
1972
+ } catch (e) {
1973
+ const msg = e?.message ?? String(e);
1974
+ throw new PaymentFailedError("EpochPrizePool.executeSignedSettlement failed.", { cause: msg });
1975
+ }
1976
+ const terminal = await readEpochTerminal(deps, pool, series, epochId);
1977
+ return {
1978
+ series,
1979
+ epochId,
1980
+ winners,
1981
+ amountsMicro,
1982
+ signature: input.signature,
1983
+ txHash: sent.txHash,
1984
+ status: walletStatus(sent.status),
1985
+ ...terminal === null ? {} : { terminal, settled: terminal === "settled" },
1986
+ epochPrizePool: pool,
1987
+ via: "chain"
1988
+ };
1989
+ }
1990
+ };
1991
+ }
1992
+ var C1_ENTER_SPLIT_MICRO = {
1993
+ entry: 1000000n,
1994
+ fee: 100000n,
1995
+ pool: 600000n,
1996
+ outgoingSeed: 300000n,
1997
+ incomingSeed: 0n
1998
+ };
1999
+
2000
+ // src/x402.ts
2001
+ var ADDRESS_RE3 = /^0x[0-9a-fA-F]{40}$/;
898
2002
  function requireAddress2(value, field) {
899
2003
  if (typeof value !== "string" || value.trim() === "") {
900
2004
  throw new MissingFieldError(field);
901
2005
  }
902
- if (!ADDRESS_RE2.test(value)) {
2006
+ if (!ADDRESS_RE3.test(value)) {
903
2007
  throw new ConfigError(`${field} must be a 0x-prefixed 20-byte address, got: ${JSON.stringify(value)}`, {
904
2008
  field,
905
2009
  value
@@ -1037,7 +2141,7 @@ function validateX402ChallengeInput(input) {
1037
2141
  }
1038
2142
 
1039
2143
  // src/client.ts
1040
- var ADDRESS_RE3 = /^0x[0-9a-fA-F]{40}$/;
2144
+ var ADDRESS_RE4 = /^0x[0-9a-fA-F]{40}$/;
1041
2145
  function mapAlreadyEntered(e) {
1042
2146
  const msg = e instanceof Error ? e.message : typeof e === "object" && e && "message" in e ? String(e.message) : String(e);
1043
2147
  const detail = e instanceof PaymentFailedError || e instanceof ApiError ? e.detail : void 0;
@@ -1085,7 +2189,7 @@ function requireAddressField(value, field) {
1085
2189
  if (typeof value !== "string" || value.trim() === "") {
1086
2190
  throw new MissingFieldError(field);
1087
2191
  }
1088
- if (!ADDRESS_RE3.test(value)) {
2192
+ if (!ADDRESS_RE4.test(value)) {
1089
2193
  throw new ConfigError(
1090
2194
  `${field} must be a 0x-prefixed 20-byte wallet address (Phase 1a transfers settle server-held wallets), got: ${JSON.stringify(value)}`,
1091
2195
  { field, value }
@@ -1124,6 +2228,56 @@ var Playmos = class {
1124
2228
  );
1125
2229
  }
1126
2230
  };
2231
+ /**
2232
+ * Rolling epochs — `currentId` / `prize` / `get` / `getSeries` (sdk#629),
2233
+ * `enter` (sdk#635 / S2), operator `settle` (sdk#639 / S3), the C3
2234
+ * refund pull (sdk#634), and E1b `getAttestation` / `executeSettlement`
2235
+ * (wallet-direct via the same `walletProvider()` funnel as `rounds.withdraw`).
2236
+ * Reads/settle go through the service. Refund claim, withdraw, and signed
2237
+ * execute are wallet-direct on EpochPrizePool — not PrizePool.
2238
+ * Nothing here opens a round.
2239
+ */
2240
+ this.epochs = createEpochsApi({
2241
+ http: () => this.http,
2242
+ config: () => this.config,
2243
+ assertSecretKey: (surface) => this.assertSecretKey(surface),
2244
+ sendEntry: async (calls, opts) => {
2245
+ const provider = this.walletProvider();
2246
+ if (this.config.gas?.mode === "player") {
2247
+ await assertEnoughGas(provider, await getAccount(provider));
2248
+ }
2249
+ const from = await getAccount(provider);
2250
+ const paymasterUrl = this.config.gas?.mode === "player" ? void 0 : this.config.gas?.paymasterUrl ?? opts.paymasterUrl;
2251
+ const { id } = await sendCalls(provider, from, this.env.chainId, calls, paymasterUrl);
2252
+ return waitForCalls(provider, id);
2253
+ },
2254
+ walletAddress: async () => getAccount(this.walletProvider()),
2255
+ readView: async (to, data) => {
2256
+ if (await walletAvailable(this.config.wallet, this.walletTimeoutPolicy())) {
2257
+ const provider = this.walletProvider();
2258
+ return await provider.request({
2259
+ method: "eth_call",
2260
+ params: [{ to, data }, "latest"]
2261
+ });
2262
+ }
2263
+ const rpc = this.env.network === "base" ? "https://mainnet.base.org" : "https://sepolia.base.org";
2264
+ const res = await fetch(rpc, {
2265
+ method: "POST",
2266
+ headers: { "content-type": "application/json" },
2267
+ body: JSON.stringify({
2268
+ jsonrpc: "2.0",
2269
+ id: 1,
2270
+ method: "eth_call",
2271
+ params: [{ to, data }, "latest"]
2272
+ })
2273
+ });
2274
+ const json = await res.json();
2275
+ if (!json.result) {
2276
+ throw new ApiError(`eth_call failed: ${json.error?.message ?? "no result"}`, { to });
2277
+ }
2278
+ return json.result;
2279
+ }
2280
+ });
1127
2281
  this.payouts = {
1128
2282
  /** Choose how the studio is paid: "usdc" (default) or "fiat" (Bridge). Secret key only (#264). */
1129
2283
  setMode: async (mode) => {
@@ -1289,6 +2443,8 @@ var Playmos = class {
1289
2443
  this.mockPotMicro = /* @__PURE__ */ new Map();
1290
2444
  /** Persisted settle winners for re-settle replay (#490 C2) — never re-invent pool-to-everyone. */
1291
2445
  this.mockSettleWinners = /* @__PURE__ */ new Map();
2446
+ /** L34 notices after mock settle — funded only with a push txHash. */
2447
+ this.mockPayoutNotices = /* @__PURE__ */ new Map();
1292
2448
  this.rounds = {
1293
2449
  open: async (input) => {
1294
2450
  if (!this.config.mock) this.assertSecretKey("playmos.rounds.open");
@@ -1454,6 +2610,16 @@ var Playmos = class {
1454
2610
  };
1455
2611
  this.mockRounds.set(input.roundId, settled);
1456
2612
  this.mockSettleWinners.set(input.roundId, winners);
2613
+ this.mockPayoutNotices.set(
2614
+ input.roundId,
2615
+ rows.filter((w) => w.micro > 0n).map((w) => ({
2616
+ roundId: input.roundId,
2617
+ wallet: w.wallet.toLowerCase(),
2618
+ amountMicro: w.micro.toString(),
2619
+ status: "claimable",
2620
+ reason: "prizepool_pull_credit"
2621
+ }))
2622
+ );
1457
2623
  for (const w of rows) {
1458
2624
  if (w.micro <= 0n) continue;
1459
2625
  const k = `${input.roundId}:${w.wallet.toLowerCase()}`;
@@ -1695,6 +2861,93 @@ var Playmos = class {
1695
2861
  }
1696
2862
  return body.active;
1697
2863
  },
2864
+ /**
2865
+ * L34 / sdk#610 — notifications for a settled round.
2866
+ * Each win is `funded` (push txHash) or `claimable` (withdraw fallback).
2867
+ * Never returns `funded` without a transfer txHash.
2868
+ */
2869
+ payoutNotices: async (input) => {
2870
+ requireField(input?.roundId, "roundId");
2871
+ if (this.config.mock) {
2872
+ const existing = this.mockRounds.get(input.roundId);
2873
+ if (!existing) {
2874
+ throw new ApiError(`unknown round: ${input.roundId}`, { status: 404, code: "not_found" });
2875
+ }
2876
+ let notices = this.mockPayoutNotices.get(input.roundId) ?? [];
2877
+ if (input.wallet) {
2878
+ if (!ADDRESS_RE4.test(input.wallet)) {
2879
+ throw new ConfigError("wallet must be a 0x-prefixed 20-byte address");
2880
+ }
2881
+ const want = input.wallet.toLowerCase();
2882
+ notices = notices.filter((n) => n.wallet === want);
2883
+ }
2884
+ for (const n of notices) {
2885
+ if (n.status === "funded" && !n.txHash) {
2886
+ throw new ApiError("cannot label funded without a transfer txHash", {
2887
+ status: 500,
2888
+ code: "payout_notice_invalid"
2889
+ });
2890
+ }
2891
+ }
2892
+ return { roundId: input.roundId, status: existing.status, notices };
2893
+ }
2894
+ const q = input.wallet ? `?wallet=${encodeURIComponent(input.wallet)}` : "";
2895
+ return this.http.get(
2896
+ `/rounds/${encodeURIComponent(input.roundId)}/payout-notices${q}`
2897
+ );
2898
+ },
2899
+ /**
2900
+ * Operator push-attempt after settle (L34). Mock: mark a winner funded only
2901
+ * when a real-shaped txHash is supplied; otherwise that row stays claimable.
2902
+ */
2903
+ pushWinnings: async (input) => {
2904
+ requireField(input?.roundId, "roundId");
2905
+ if (!this.config.mock) this.assertSecretKey("playmos.rounds.pushWinnings");
2906
+ if (this.config.mock) {
2907
+ const existing = this.mockRounds.get(input.roundId);
2908
+ if (!existing) {
2909
+ throw new ApiError(`unknown round: ${input.roundId}`, { status: 404, code: "not_found" });
2910
+ }
2911
+ if (existing.status !== "settled") {
2912
+ throw new ApiError(`round ${input.roundId} is not settled \u2014 push after the clock`, {
2913
+ status: 409,
2914
+ code: "conflict"
2915
+ });
2916
+ }
2917
+ const prior = this.mockPayoutNotices.get(input.roundId) ?? [];
2918
+ const byWallet = new Map(
2919
+ (input.pushes ?? []).map((p) => [p.wallet.toLowerCase(), p])
2920
+ );
2921
+ const TX_RE = /^0x[0-9a-fA-F]{64}$/;
2922
+ const ZERO = "0x" + "00".repeat(32);
2923
+ const notices = prior.map((n) => {
2924
+ const p = byWallet.get(n.wallet);
2925
+ if (p && !p.failed && p.txHash && TX_RE.test(p.txHash) && p.txHash.toLowerCase() !== ZERO) {
2926
+ return {
2927
+ ...n,
2928
+ status: "funded",
2929
+ txHash: p.txHash.toLowerCase(),
2930
+ reason: void 0
2931
+ };
2932
+ }
2933
+ if (p && (p.failed || !p.txHash || !TX_RE.test(p.txHash) || p.txHash.toLowerCase() === ZERO)) {
2934
+ return {
2935
+ ...n,
2936
+ status: "claimable",
2937
+ txHash: void 0,
2938
+ reason: p.failed ? "push_failed" : "push_missing_txHash"
2939
+ };
2940
+ }
2941
+ return n;
2942
+ });
2943
+ this.mockPayoutNotices.set(input.roundId, notices);
2944
+ return { roundId: input.roundId, status: existing.status, notices };
2945
+ }
2946
+ return this.http.post(
2947
+ `/rounds/${encodeURIComponent(input.roundId)}/push-winnings`,
2948
+ {}
2949
+ );
2950
+ },
1698
2951
  /**
1699
2952
  * Read a wallet's **claimable** prize balance for a round (issue #41).
1700
2953
  * Prefers the service chain read (`GET /v1/rounds/:id/prize?wallet=`); falls
@@ -1751,7 +3004,7 @@ var Playmos = class {
1751
3004
  const existing = this.mockRounds.get(input.roundId);
1752
3005
  if (existing?.prizePoolAddress) prizePool2 = existing.prizePoolAddress;
1753
3006
  }
1754
- if (!input.wallet || !ADDRESS_RE3.test(input.wallet)) {
3007
+ if (!input.wallet || !ADDRESS_RE4.test(input.wallet)) {
1755
3008
  throw new ConfigError(
1756
3009
  "mock rounds.withdraw requires wallet (0x\u2026) \u2014 live uses the connected provider; without wallet mock would pay the first non-zero credit to the wrong player (#495)"
1757
3010
  );
@@ -2037,8 +3290,16 @@ var Playmos = class {
2037
3290
  /**
2038
3291
  * Offline mock payments for this client instance — so `verify(id)` after
2039
3292
  * `mock: true` pay/enterRound does not hit the live API (#204).
3293
+ * Keyed by payment id for verify.
2040
3294
  */
2041
3295
  this.mockPayments = /* @__PURE__ */ new Map();
3296
+ /**
3297
+ * sdk#513 / residual B1–B2 — held idempotencyKey offline (live contract rehearsal).
3298
+ * Keyed like service: `${gameId}:${key}` (IAP without gameId uses empty gameId).
3299
+ * Stores terms so same key + different money throws (live IdempotencyConflict), not silent replay.
3300
+ * Auto-minted keys still unique per call (not stored for cross-call replay).
3301
+ */
3302
+ this.mockByIdempotencyKey = /* @__PURE__ */ new Map();
2042
3303
  /**
2043
3304
  * `transfers` — read-back / confirmation for a prior `transfer()` (issues #27, #47).
2044
3305
  * `get` reconciles once against chain truth; `wait` polls it to a terminal state
@@ -2223,18 +3484,62 @@ var Playmos = class {
2223
3484
  }
2224
3485
  };
2225
3486
  }
2226
- rememberMock(payment) {
2227
- if (payment.mock) this.mockPayments.set(payment.id, payment);
3487
+ mockIdemScopeKey(gameId, heldKey) {
3488
+ return `${gameId ?? ""}:${heldKey}`;
3489
+ }
3490
+ rememberMock(payment, held) {
3491
+ if (payment.mock) {
3492
+ this.mockPayments.set(payment.id, payment);
3493
+ if (held) {
3494
+ this.mockByIdempotencyKey.set(this.mockIdemScopeKey(held.terms.gameId, held.key), {
3495
+ payment,
3496
+ terms: held.terms
3497
+ });
3498
+ }
3499
+ }
2228
3500
  return payment;
2229
3501
  }
3502
+ /** Live-shaped conflict when held mock key is reused with different money terms (sdk#513 residual B1). */
3503
+ mockIdemReplayOrThrow(heldKey, terms) {
3504
+ const prior = this.mockByIdempotencyKey.get(this.mockIdemScopeKey(terms.gameId, heldKey));
3505
+ if (!prior) return null;
3506
+ const t = prior.terms;
3507
+ const same = t.kind === terms.kind && t.amount === terms.amount && t.gameId === terms.gameId && t.playerId === terms.playerId && t.roundId === terms.roundId && t.identity === terms.identity && t.sku === terms.sku;
3508
+ if (!same) {
3509
+ throw new ApiError(
3510
+ `idempotencyKey was already used for a payment with DIFFERENT terms (kind/amount/gameId/playerId/roundId/identity/sku); use a fresh key (mock:true mirrors live conflict \u2014 sdk#513).`,
3511
+ {
3512
+ code: "idempotency_conflict",
3513
+ idempotencyKey: heldKey,
3514
+ priorKind: t.kind,
3515
+ priorAmount: t.amount
3516
+ }
3517
+ );
3518
+ }
3519
+ return prior.payment;
3520
+ }
2230
3521
  /** External-studio IAP (1%). Returns a real confirmed Payment with a txHash. */
2231
3522
  async pay(input) {
2232
3523
  const amountMicro = validateAmount(input.amount);
2233
3524
  requireField(input.sku, "sku");
2234
3525
  requireField(input.playerId, "playerId");
2235
3526
  const metadata = validateMetadata(input.metadata);
2236
- const idempotencyKey = input.idempotencyKey ?? prefixedId("idem");
3527
+ const heldIdem = typeof input.idempotencyKey === "string" && input.idempotencyKey.trim() ? input.idempotencyKey.trim() : void 0;
3528
+ const idempotencyKey = heldIdem ?? prefixedId("idem");
2237
3529
  if (this.config.mock) {
3530
+ const terms = {
3531
+ kind: "iap",
3532
+ amount: formatMicroToUsd(amountMicro),
3533
+ gameId: input.gameId ?? "",
3534
+ playerId: input.playerId,
3535
+ roundId: "",
3536
+ identity: "",
3537
+ sku: input.sku.trim()
3538
+ };
3539
+ if (heldIdem) {
3540
+ const replay = this.mockIdemReplayOrThrow(heldIdem, terms);
3541
+ if (replay) return replay;
3542
+ }
2238
3543
  return this.rememberMock(
2239
3544
  mockIapPayment({
2240
3545
  amountMicro,
@@ -2243,7 +3548,8 @@ var Playmos = class {
2243
3548
  playerId: input.playerId,
2244
3549
  chain: this.env.network,
2245
3550
  metadata
2246
- })
3551
+ }),
3552
+ heldIdem ? { key: heldIdem, terms } : void 0
2247
3553
  );
2248
3554
  }
2249
3555
  if (this.env.isTest && !await walletAvailable(this.config.wallet, this.walletTimeoutPolicy())) {
@@ -2265,7 +3571,13 @@ var Playmos = class {
2265
3571
  );
2266
3572
  let payment = this.mapServerPayment(res.payment, "iap", amountMicro);
2267
3573
  if (payment.status === "pending" || payment.status === "created") {
2268
- payment = await this.waitForServerSettle(payment.id, "iap", amountMicro);
3574
+ payment = await this.waitForServerSettle(
3575
+ payment.id,
3576
+ "iap",
3577
+ amountMicro,
3578
+ input.settleTimeoutMs ?? 6e4,
3579
+ input.signal
3580
+ );
2269
3581
  }
2270
3582
  return payment;
2271
3583
  }
@@ -2303,15 +3615,29 @@ var Playmos = class {
2303
3615
  const { txHash } = await waitForCalls(provider, callsId);
2304
3616
  return this.settle(intent.payment.id, txHash);
2305
3617
  }
2306
- /** Playmos-owned skill-game entry (10%, 60/30/10 prize pool). Closes #343. */
3618
+ /** Skill-game prize-pool entry. Live sandbox / Playmos Lab uses 60/30/10. Studio contest take is 1% (not a live separate pool yet). Closes #343. */
2307
3619
  async enterRound(input) {
2308
3620
  const amountMicro = validateAmount(input.amount);
2309
3621
  requireField(input.gameId, "gameId");
2310
3622
  requireField(input.roundId, "roundId");
2311
3623
  requireField(input.playerId, "playerId");
2312
3624
  const metadata = validateMetadata(input.metadata);
2313
- const idempotencyKey = input.idempotencyKey ?? prefixedId("idem");
3625
+ const heldIdem = typeof input.idempotencyKey === "string" && input.idempotencyKey.trim() ? input.idempotencyKey.trim() : void 0;
3626
+ const idempotencyKey = heldIdem ?? prefixedId("idem");
2314
3627
  if (this.config.mock) {
3628
+ const terms = {
3629
+ kind: "entry",
3630
+ amount: formatMicroToUsd(amountMicro),
3631
+ gameId: input.gameId,
3632
+ playerId: input.playerId,
3633
+ roundId: input.roundId,
3634
+ identity: typeof input.identity === "string" ? input.identity.trim() : "",
3635
+ sku: ""
3636
+ };
3637
+ if (heldIdem) {
3638
+ const replay = this.mockIdemReplayOrThrow(heldIdem, terms);
3639
+ if (replay) return replay;
3640
+ }
2315
3641
  this.mockAccumulateEntry(input.roundId, amountMicro);
2316
3642
  return this.rememberMock(
2317
3643
  mockEntryPayment({
@@ -2327,7 +3653,8 @@ var Playmos = class {
2327
3653
  // B2 pins — must survive mock path for hub hasEntered parity (#204).
2328
3654
  roundKey: input.roundKey,
2329
3655
  identity: input.identity
2330
- })
3656
+ }),
3657
+ heldIdem ? { key: heldIdem, terms } : void 0
2331
3658
  );
2332
3659
  }
2333
3660
  const serverSettleNoWallet = this.env.isTest && !await walletAvailable(this.config.wallet, this.walletTimeoutPolicy());
@@ -2360,7 +3687,13 @@ var Playmos = class {
2360
3687
  );
2361
3688
  let payment2 = this.mapServerPayment(res.payment, "entry", amountMicro);
2362
3689
  if (payment2.status === "pending" || payment2.status === "created") {
2363
- payment2 = await this.waitForServerSettle(payment2.id, "entry", amountMicro);
3690
+ payment2 = await this.waitForServerSettle(
3691
+ payment2.id,
3692
+ "entry",
3693
+ amountMicro,
3694
+ input.settleTimeoutMs ?? 6e4,
3695
+ input.signal
3696
+ );
2364
3697
  }
2365
3698
  if (input.identity) payment2.identity = input.identity;
2366
3699
  const pool = res.clientParams?.contractAddress ?? this.config.contracts?.prizePool;
@@ -2543,10 +3876,16 @@ var Playmos = class {
2543
3876
  * until chain verify catches PaymentSettled (RPC lag) — poll so pay() matches
2544
3877
  * the documented "confirmed" sandbox promise (TTFSC-T0 / cold-run).
2545
3878
  */
2546
- async waitForServerSettle(paymentId, kind, amountMicro, timeoutMs = 6e4) {
3879
+ async waitForServerSettle(paymentId, kind, amountMicro, timeoutMs = 6e4, signal) {
2547
3880
  const start = Date.now();
2548
3881
  let delayMs = 400;
2549
3882
  while (Date.now() - start < timeoutMs) {
3883
+ if (signal?.aborted) {
3884
+ throw new ApiError(
3885
+ `Aborted waiting for server-settle of ${paymentId}. Re-check with playmos.verify("${paymentId}").`,
3886
+ { paymentId, aborted: true, asyncSettle: true }
3887
+ );
3888
+ }
2550
3889
  const raw = await this.http.get(
2551
3890
  `/payments/${encodeURIComponent(paymentId)}`
2552
3891
  );
@@ -2561,12 +3900,46 @@ var Playmos = class {
2561
3900
  { paymentId, timeout: true, asyncSettle: true }
2562
3901
  );
2563
3902
  }
3903
+ /**
3904
+ * sdk#509 — recover a payment after a lost response by the idempotency key you held.
3905
+ * Requires secret sk_test_ (same as list). Returns null if not found.
3906
+ */
3907
+ async findPaymentByIdempotencyKey(idempotencyKey, opts) {
3908
+ requireField(idempotencyKey, "idempotencyKey");
3909
+ if (this.config.mock) {
3910
+ return null;
3911
+ }
3912
+ const q = new URLSearchParams({ idempotencyKey });
3913
+ if (opts?.gameId) q.set("gameId", opts.gameId);
3914
+ try {
3915
+ const res = await this.http.get(
3916
+ `/payments?${q.toString()}`
3917
+ );
3918
+ const row = res.data?.[0];
3919
+ if (!row || typeof row.id !== "string") return null;
3920
+ const kind = row.kind === "entry" ? "entry" : "iap";
3921
+ const amountMicro = validateAmount(String(row.amount ?? "0.01"));
3922
+ return this.mapServerPayment(
3923
+ row,
3924
+ kind,
3925
+ amountMicro
3926
+ );
3927
+ } catch (e) {
3928
+ if (e instanceof ApiError && (e.detail?.status === 404 || e.detail?.code === "not_found" || /not found/i.test(e.message))) {
3929
+ return null;
3930
+ }
3931
+ throw e;
3932
+ }
3933
+ }
2564
3934
  /**
2565
3935
  * Map a server-settled payment (from the `settle: "server"` response) into the
2566
3936
  * SDK's `Payment` shape. The service already signed + confirmed on-chain, so we
2567
- * pass its authoritative `status`/`txHash`/amounts straight through and, for an
2568
- * entry, derive the 60/30/10 USD `split` locally (exactly as the mock + the
2569
- * contract compute it) from the parsed amount.
3937
+ * pass its authoritative `status`/`txHash`/amounts straight through.
3938
+ *
3939
+ * For entry, `Payment.split` is a **first-party 60/30/10 projection** from the
3940
+ * request amount using SDK BPS constants (same integer math as mock) — not a
3941
+ * chain read of pool constructor bps or post-lock payable pot (sdk#514 residual N1).
3942
+ * Prefer `chainAmount` / on-chain `getRound` for money truth.
2570
3943
  */
2571
3944
  mapServerPayment(p, kind, amountMicro) {
2572
3945
  const payment = {
@@ -2734,13 +4107,13 @@ function previewPoolSplit(amount) {
2734
4107
  }
2735
4108
 
2736
4109
  // src/settlement.ts
2737
- var ADDRESS_RE4 = /^0x[0-9a-fA-F]{40}$/;
4110
+ var ADDRESS_RE5 = /^0x[0-9a-fA-F]{40}$/;
2738
4111
  var DEFAULT_TTL_MS = 15 * 60 * 1e3;
2739
4112
  function requireAddress3(value, field) {
2740
4113
  if (typeof value !== "string" || value.trim() === "") {
2741
4114
  throw new MissingFieldError(field);
2742
4115
  }
2743
- if (!ADDRESS_RE4.test(value)) {
4116
+ if (!ADDRESS_RE5.test(value)) {
2744
4117
  throw new ConfigError(`${field} must be a 0x-prefixed 20-byte address, got: ${JSON.stringify(value)}`, {
2745
4118
  field,
2746
4119
  value
@@ -2829,6 +4202,7 @@ function isX402PayloadAuthorization(auth) {
2829
4202
  exports.AlreadyEnteredError = AlreadyEnteredError;
2830
4203
  exports.ApiError = ApiError;
2831
4204
  exports.AuthError = AuthError;
4205
+ exports.C1_ENTER_SPLIT_MICRO = C1_ENTER_SPLIT_MICRO;
2832
4206
  exports.CHAIN_ID = CHAIN_ID;
2833
4207
  exports.ConfigError = ConfigError;
2834
4208
  exports.DEFAULT_API_BASE_URL = DEFAULT_API_BASE_URL;
@@ -2837,6 +4211,7 @@ exports.InvalidAmountError = InvalidAmountError;
2837
4211
  exports.MICRO_PER_USDC = MICRO_PER_USDC;
2838
4212
  exports.MissingFieldError = MissingFieldError;
2839
4213
  exports.NothingToWithdrawError = NothingToWithdrawError;
4214
+ exports.PLAYMOS_FEE_SINK_DEFAULT = PLAYMOS_FEE_SINK_DEFAULT;
2840
4215
  exports.PaymentFailedError = PaymentFailedError;
2841
4216
  exports.PayoutError = PayoutError;
2842
4217
  exports.Playmos = Playmos;
@@ -2845,15 +4220,25 @@ exports.USDC_ADDRESS = USDC_ADDRESS;
2845
4220
  exports.USDC_DECIMALS = USDC_DECIMALS;
2846
4221
  exports.WalletConnectionError = WalletConnectionError;
2847
4222
  exports.WalletTimeoutError = WalletTimeoutError;
4223
+ exports.buildEpochClaimRefundCall = buildEpochClaimRefundCall;
4224
+ exports.buildEpochEntryCalls = buildEpochEntryCalls;
4225
+ exports.buildEpochExecuteSettlementCall = buildEpochExecuteSettlementCall;
4226
+ exports.buildEpochWithdrawCall = buildEpochWithdrawCall;
2848
4227
  exports.clientRuntimeSignals = clientRuntimeSignals;
2849
4228
  exports.computeIapSplit = computeIapSplit;
2850
4229
  exports.computePayout = computePayout;
2851
4230
  exports.computePoolSplit = computePoolSplit;
4231
+ exports.createEpochsApi = createEpochsApi;
2852
4232
  exports.createPaymentRequirement = createPaymentRequirement;
2853
4233
  exports.createX402Challenge = createX402Challenge;
2854
4234
  exports.decodePaymentHeader = decodePaymentHeader;
4235
+ exports.derivedEpochId = derivedEpochId;
4236
+ exports.encodeEpochPoolConstructorArgs = encodeEpochPoolConstructorArgs;
2855
4237
  exports.encodePaymentHeader = encodePaymentHeader;
4238
+ exports.epochPayableMicro = epochPayableMicro;
4239
+ exports.epochPoolProofMessage = epochPoolProofMessage;
2856
4240
  exports.formatMicroToUsd = formatMicroToUsd;
4241
+ exports.identityToBytes32 = identityToBytes32;
2857
4242
  exports.isClientRuntime = isClientRuntime;
2858
4243
  exports.isWalletSignatureAuthorization = isWalletSignatureAuthorization;
2859
4244
  exports.isX402PayloadAuthorization = isX402PayloadAuthorization;
@@ -2861,6 +4246,7 @@ exports.networkToCaip2 = networkToCaip2;
2861
4246
  exports.parsePaymentRequirement = parsePaymentRequirement;
2862
4247
  exports.parseUsdToMicro = parseUsdToMicro;
2863
4248
  exports.prefixedId = prefixedId;
4249
+ exports.prepareStudioPoolLocal = prepareStudioPoolLocal;
2864
4250
  exports.previewEscrowFee = previewEscrowFee;
2865
4251
  exports.previewIapSplit = previewIapSplit;
2866
4252
  exports.previewMarketplaceSplit = previewMarketplaceSplit;
@@ -2868,6 +4254,8 @@ exports.previewPoolSplit = previewPoolSplit;
2868
4254
  exports.previewTransferSplit = previewTransferSplit;
2869
4255
  exports.resolveEnv = resolveEnv;
2870
4256
  exports.serializePaymentRequirement = serializePaymentRequirement;
4257
+ exports.seriesToBytes32 = seriesToBytes32;
4258
+ exports.settlementTypedData = settlementTypedData;
2871
4259
  exports.toX402PaymentRequired = toX402PaymentRequired;
2872
4260
  exports.ulid = ulid;
2873
4261
  exports.validateX402ChallengeInput = validateX402ChallengeInput;