@playmos/sdk 0.3.13 → 0.3.15

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/CHANGELOG.md CHANGED
@@ -4,6 +4,19 @@ All notable changes to `@playmos/sdk`. This project adheres to [Semantic Version
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## [0.3.15] — 2026-09-11
8
+
9
+ ### Added
10
+ - **`epochs.prepareSeries`** / `POST /v1/epochs/series/prepare` — after register, one call returns an unsigned `createSeries` tx (`broadcast: false`, default `feeBps` 100). The studio EOA signs. Playmos never broadcasts. (#742 / #739)
11
+
12
+ ### Fixed (service)
13
+ - Studio-operated pool `POST /v1/epochs/:epochId/settle` returns 4xx naming `settleEpoch` / `executeSignedSettlement`, not a product 502/504. (#741 / #736)
14
+
15
+ ## [0.3.14] — 2026-09-11
16
+
17
+ ### Added
18
+ - **`settle: "server"`** on `new Playmos({ ... })` — explicit sandbox no-wallet path. `pay()` / `enterRound()` server-settle even when MetaMask is installed. Live keys throw. Default without the ask is unchanged (wallet present → player signs).
19
+
7
20
  ## [0.3.13] — 2026-09-11
8
21
 
9
22
  ### Docs
package/README.md CHANGED
@@ -16,14 +16,14 @@ Requires **Node 18 / 20 / 22 LTS** for local development and CI toolchains. Full
16
16
 
17
17
  ## Browser vs server entry
18
18
 
19
- - **Games (browser / Vite / Phaser):** `import { Playmos } from "@playmos/sdk"` — this entry is browser-safe and does **not** import Node `crypto` (issue #9).
19
+ - **Games (browser / Vite / Phaser):** `import { Playmos } from "@playmos/sdk"` — this entry is browser-safe and does **not** import Node `crypto` (issue #9). The package depends on **viem**; the named import tree-shakes, but a default Vite single chunk can still exceed 500 kB gzip-warn.
20
20
  - **Your backend (webhook receivers):** `import { verifyWebhook } from "@playmos/sdk/server"` — HMAC verification uses Node crypto and must stay off the client bundle.
21
21
 
22
22
  The sandbox API also sends CORS headers so browser `pay()` / `enterRound()` work without a same-origin proxy (issue #10).
23
23
 
24
24
  ## Quickstart — the no-wallet sandbox
25
25
 
26
- The public sandbox key `pk_test_playmos_sandbox` runs against a real Playmos test service on Base Sepolia. **You don't need a wallet:** on a `pk_test` key with no `wallet` configured, the SDK routes to a server-settle path where Playmos signs and submits the on-chain transaction for you. You still get a real, confirmed `txHash` — just signed by the service. No wallet, no gas, no crypto.
26
+ The public sandbox key `pk_test_playmos_sandbox` runs against a real Playmos test service on Base Sepolia. **You don't need a wallet:** set `settle: "server"` (the quickstart does). The SDK then server-settles even if MetaMask is installed. You still get a real, confirmed `txHash` — signed by the service. No wallet, no gas, no crypto. Omit `settle` only when you want the player to sign.
27
27
 
28
28
  The public key is wired to `game_sandbox_iap`. Skill entry (`game_sandbox_skill`) is an optional later path — not the first-hour default.
29
29
 
@@ -35,7 +35,7 @@ import { Playmos } from "@playmos/sdk";
35
35
 
36
36
  // Public sandbox key — client-safe, like Stripe's pk_test_.
37
37
  // No wallet needed: Playmos server-settles the test payment for you.
38
- const playmos = new Playmos({ apiKey: "pk_test_playmos_sandbox" });
38
+ const playmos = new Playmos({ apiKey: "pk_test_playmos_sandbox", settle: "server" });
39
39
 
40
40
  const payment = await playmos.pay({
41
41
  gameId: "game_sandbox_iap", // required for the public sandbox key
@@ -51,6 +51,9 @@ while (result.status !== "confirmed" && tries !== 0) {
51
51
  result = await playmos.verify(payment.id);
52
52
  tries -= 1;
53
53
  }
54
+ if (result.status !== "confirmed") {
55
+ throw new Error(`not confirmed yet: ${result.status}`);
56
+ }
54
57
 
55
58
  console.log({ id: payment.id, status: result.status, tx: payment.txHash });
56
59
  // You won: pay_… + confirmed on Base Sepolia
@@ -78,24 +81,29 @@ if (result.status === "confirmed") {
78
81
  ### Optional later — skill-game entry (`enterRound()`)
79
82
 
80
83
  ```ts
84
+ const idempotencyKey = "entry-player_abc"; // persist BEFORE enterRound
81
85
  const entry = await playmos.enterRound({
82
86
  gameId: "game_sandbox_skill",
83
87
  roundId: "5m-5944009",
84
88
  amount: "1.00", // grows this round's pool
85
89
  playerId: "player_abc",
90
+ idempotencyKey,
86
91
  });
87
- // entry.status === "confirmed". Public sandbox skill is a shared Playmos pool — not your contest.
88
- // Your 1% contest is live only after you register your own pool (next section).
92
+ // entry.status === "confirmed". Public sandbox skill is a shared Playmos pool —
93
+ // not your contest and not your published take. Persist entry.identity
94
+ // (sandbox#entry_…) when there is no wallet. Same $1.00/request cap as pay().
95
+ // Your 1% is IAP pay() and your own registered pool (next section).
89
96
  ```
90
97
 
91
98
  ### Your own contest pool — `epochs.preparePool` (you sign)
92
99
 
93
- `@playmos/sdk` **0.3.13** ships `epochs.preparePool` with `bytecode` / `unsignedTx` on the typed response. It calls the live API. Playmos sets `broadcast: false` and does **not** send the transaction.
100
+ `@playmos/sdk` **0.3.15** ships `epochs.preparePool` and `epochs.prepareSeries` with `unsignedTx` on the typed response. It calls the live API. Playmos sets `broadcast: false` and does **not** send the transaction.
94
101
 
95
102
  1. Fund your studio wallet on Base Sepolia (ETH + test USDC). Playmos has no faucet — use [Coinbase's faucet docs](https://docs.cdp.coinbase.com/faucets/introduction/welcome).
96
103
  2. Call `epochs.preparePool({ studioWallet })` or `POST /v1/epochs/pools/prepare`. Use `feeSink` from that response (`0xD84c190085aa59c48a9B478Ea333D50B8DF4aD42`).
97
104
  3. Sign `unsignedTx` in your wallet and send it.
98
- 4. `epochs.registerPool` with `walletProof`, then open / enter **your** game — not the shared sandbox game.
105
+ 4. `epochs.registerPool` with `walletProof`.
106
+ 5. `epochs.prepareSeries` — one returned `unsignedTx`, you sign in the same EOA. Then `GET /v1/epochs/series` until `created=true`, then enter **your** game — not the shared sandbox game.
99
107
 
100
108
  Full steps: https://playmos-docs-public.vercel.app/docs#studio-pool-prepare
101
109
 
@@ -345,6 +345,23 @@ var epochPrizePoolViewAbi = [
345
345
  outputs: [{ type: "uint256" }]
346
346
  }
347
347
  ];
348
+ var epochPrizePoolCreateSeriesAbi = [
349
+ {
350
+ type: "function",
351
+ name: "createSeries",
352
+ stateMutability: "nonpayable",
353
+ inputs: [
354
+ { name: "series", type: "bytes32" },
355
+ { name: "genesis", type: "uint256" },
356
+ { name: "epochDuration", type: "uint256" },
357
+ { name: "entry", type: "uint256" },
358
+ { name: "feeBps", type: "uint16" },
359
+ { name: "poolBps", type: "uint16" },
360
+ { name: "seedBps", type: "uint16" }
361
+ ],
362
+ outputs: []
363
+ }
364
+ ];
348
365
  var epochPrizePoolEnterAbi = [
349
366
  {
350
367
  type: "function",
@@ -442,4 +459,4 @@ function buildEpochExecuteSettlementCall(args) {
442
459
  };
443
460
  }
444
461
 
445
- export { AlreadyEnteredError, ApiError, AuthError, ConfigError, InsufficientGasError, InvalidAmountError, MissingFieldError, NothingToWithdrawError, PaymentFailedError, PlaymosError, WalletConnectionError, WalletTimeoutError, assertEnoughGas, buildEntryCalls, buildEpochExecuteSettlementCall, buildIapCalls, buildWithdrawCall, encodeApprove, epochPrizePoolEnterAbi, epochPrizePoolRefundAbi, epochPrizePoolViewAbi, identityToBytes32, prizePoolAbi, sendCalls, seriesToBytes32, toBytes32, waitForCalls };
462
+ export { AlreadyEnteredError, ApiError, AuthError, ConfigError, InsufficientGasError, InvalidAmountError, MissingFieldError, NothingToWithdrawError, PaymentFailedError, PlaymosError, WalletConnectionError, WalletTimeoutError, assertEnoughGas, buildEntryCalls, buildEpochExecuteSettlementCall, buildIapCalls, buildWithdrawCall, encodeApprove, epochPrizePoolCreateSeriesAbi, epochPrizePoolEnterAbi, epochPrizePoolRefundAbi, epochPrizePoolViewAbi, identityToBytes32, prizePoolAbi, sendCalls, seriesToBytes32, toBytes32, waitForCalls };
@@ -76,6 +76,13 @@ interface PlaymosConfig {
76
76
  * even mock results use the REAL status union (never a synthetic "mocked").
77
77
  */
78
78
  mock?: boolean;
79
+ /**
80
+ * Explicit sandbox no-wallet path. On a test key, `pay()` / `enterRound()`
81
+ * server-settle even if MetaMask (or another injected wallet) is installed.
82
+ * Live keys (`pk_live_` / `sk_live_`) throw — never server-settle real money.
83
+ * Omit to keep today's default: server-settle only when no wallet is present.
84
+ */
85
+ settle?: "server";
79
86
  /**
80
87
  * Automatic backoff/retry on HTTP 429 (throttling). Default ON (up to 2 retries,
81
88
  * honoring a `Retry-After` header). Retries are safe — reads are idempotent and
@@ -76,6 +76,13 @@ interface PlaymosConfig {
76
76
  * even mock results use the REAL status union (never a synthetic "mocked").
77
77
  */
78
78
  mock?: boolean;
79
+ /**
80
+ * Explicit sandbox no-wallet path. On a test key, `pay()` / `enterRound()`
81
+ * server-settle even if MetaMask (or another injected wallet) is installed.
82
+ * Live keys (`pk_live_` / `sk_live_`) throw — never server-settle real money.
83
+ * Omit to keep today's default: server-settle only when no wallet is present.
84
+ */
85
+ settle?: "server";
79
86
  /**
80
87
  * Automatic backoff/retry on HTTP 429 (throttling). Default ON (up to 2 retries,
81
88
  * honoring a `Retry-After` header). Retries are safe — reads are idempotent and
package/dist/index.cjs CHANGED
@@ -929,6 +929,23 @@ var epochPrizePoolViewAbi = [
929
929
  outputs: [{ type: "uint256" }]
930
930
  }
931
931
  ];
932
+ var epochPrizePoolCreateSeriesAbi = [
933
+ {
934
+ type: "function",
935
+ name: "createSeries",
936
+ stateMutability: "nonpayable",
937
+ inputs: [
938
+ { name: "series", type: "bytes32" },
939
+ { name: "genesis", type: "uint256" },
940
+ { name: "epochDuration", type: "uint256" },
941
+ { name: "entry", type: "uint256" },
942
+ { name: "feeBps", type: "uint16" },
943
+ { name: "poolBps", type: "uint16" },
944
+ { name: "seedBps", type: "uint16" }
945
+ ],
946
+ outputs: []
947
+ }
948
+ ];
932
949
  var epochPrizePoolEnterAbi = [
933
950
  {
934
951
  type: "function",
@@ -1136,6 +1153,9 @@ function encodeEpochPoolConstructorArgs(args) {
1136
1153
  args.refundTimeout
1137
1154
  ]);
1138
1155
  }
1156
+ var LOCKED_SERIES_FEE_BPS = 100;
1157
+ var DEFAULT_SERIES_POOL_BPS = 7900;
1158
+ var DEFAULT_SERIES_SEED_BPS = 2e3;
1139
1159
  function derivedEpochId(genesis, epochDuration, at) {
1140
1160
  if (epochDuration === 0n) {
1141
1161
  throw new ConfigError("epochDuration must be > 0");
@@ -1304,6 +1324,66 @@ function walletStatus(status) {
1304
1324
  function sameAddr(a, b) {
1305
1325
  return a.toLowerCase() === b.toLowerCase();
1306
1326
  }
1327
+ function parseSeriesBps(raw, field) {
1328
+ if (raw === void 0) return void 0;
1329
+ if (!Number.isInteger(raw) || raw < 0) {
1330
+ throw new ConfigError(`${field} must be a non-negative integer`);
1331
+ }
1332
+ return raw;
1333
+ }
1334
+ function prepareSeriesLocal(input, chainId) {
1335
+ const pool = requireAddr(input.epochPrizePool, "epochPrizePool");
1336
+ const series = requireSeries(input.series);
1337
+ const feeBps = parseSeriesBps(input.feeBps, "feeBps");
1338
+ if (feeBps !== void 0 && feeBps !== LOCKED_SERIES_FEE_BPS) {
1339
+ throw new ConfigError(
1340
+ "feeBps must be 100 (the locked 1% Playmos take); refused before anything signable"
1341
+ );
1342
+ }
1343
+ const poolBps = parseSeriesBps(input.poolBps, "poolBps") ?? DEFAULT_SERIES_POOL_BPS;
1344
+ const seedBps = parseSeriesBps(input.seedBps, "seedBps") ?? DEFAULT_SERIES_SEED_BPS;
1345
+ if (LOCKED_SERIES_FEE_BPS + poolBps + seedBps !== 1e4) {
1346
+ throw new ConfigError("feeBps + poolBps + seedBps must sum to 10000 (fee is 100)");
1347
+ }
1348
+ const epochDuration = toBig(input.epochDuration, 0n);
1349
+ const entry = toBig(input.entry, 0n);
1350
+ if (epochDuration <= 0n) throw new ConfigError("epochDuration must be greater than 0");
1351
+ if (entry <= 0n) throw new ConfigError("entry must be greater than 0");
1352
+ const genesis = toBig(input.genesis, BigInt(Math.floor(Date.now() / 1e3)));
1353
+ if (genesis <= 0n) throw new ConfigError("genesis must be greater than 0");
1354
+ const data = viem.encodeFunctionData({
1355
+ abi: epochPrizePoolCreateSeriesAbi,
1356
+ functionName: "createSeries",
1357
+ args: [
1358
+ seriesToBytes32(series),
1359
+ genesis,
1360
+ epochDuration,
1361
+ entry,
1362
+ LOCKED_SERIES_FEE_BPS,
1363
+ poolBps,
1364
+ seedBps
1365
+ ]
1366
+ });
1367
+ return {
1368
+ series,
1369
+ epochPrizePool: pool,
1370
+ genesis: genesis.toString(),
1371
+ epochDuration: epochDuration.toString(),
1372
+ entry: entry.toString(),
1373
+ feeBps: 100,
1374
+ poolBps,
1375
+ seedBps,
1376
+ unsignedTx: {
1377
+ chainId,
1378
+ to: pool,
1379
+ value: "0",
1380
+ data
1381
+ },
1382
+ broadcast: false,
1383
+ via: "mock",
1384
+ mock: true
1385
+ };
1386
+ }
1307
1387
  function prepareStudioPoolLocal(input, playmosFeeSink, token, playmosAddresses = []) {
1308
1388
  const studio = requireAddr(input.studioWallet, "studioWallet");
1309
1389
  const locked = PLAYMOS_FEE_SINK_DEFAULT;
@@ -1452,6 +1532,7 @@ async function readEpochTerminal(deps, pool, series, epochId) {
1452
1532
  }
1453
1533
  }
1454
1534
  function createEpochsApi(deps) {
1535
+ const mockPreparedSeries = /* @__PURE__ */ new Map();
1455
1536
  return {
1456
1537
  async currentId(input) {
1457
1538
  const series = requireSeries(input?.series);
@@ -1529,6 +1610,9 @@ function createEpochsApi(deps) {
1529
1610
  const series = requireSeries(input?.series);
1530
1611
  const cfg = deps.config();
1531
1612
  if (cfg.mock) {
1613
+ const pool = (input.epochPrizePool ?? cfg.contracts?.epochPrizePool ?? "").toLowerCase();
1614
+ const prepared = mockPreparedSeries.get(`${pool}:${series}`);
1615
+ if (prepared) return prepared;
1532
1616
  return {
1533
1617
  series,
1534
1618
  created: true,
@@ -1549,6 +1633,42 @@ function createEpochsApi(deps) {
1549
1633
  })}`;
1550
1634
  return deps.http().get(path);
1551
1635
  },
1636
+ async prepareSeries(input) {
1637
+ const feeBps = input?.feeBps;
1638
+ if (feeBps !== void 0 && feeBps !== LOCKED_SERIES_FEE_BPS) {
1639
+ throw new ConfigError(
1640
+ "feeBps must be 100 (the locked 1% Playmos take); refused before anything signable"
1641
+ );
1642
+ }
1643
+ const cfg = deps.config();
1644
+ if (cfg.mock) {
1645
+ const prepared = prepareSeriesLocal(input, CHAIN_ID["base-sepolia"]);
1646
+ mockPreparedSeries.set(`${prepared.epochPrizePool}:${prepared.series}`, {
1647
+ series: prepared.series,
1648
+ created: true,
1649
+ genesis: prepared.genesis,
1650
+ epochDuration: prepared.epochDuration,
1651
+ entry: prepared.entry,
1652
+ feeBps: prepared.feeBps,
1653
+ poolBps: prepared.poolBps,
1654
+ seedBps: prepared.seedBps,
1655
+ via: "mock",
1656
+ epochPrizePool: prepared.epochPrizePool,
1657
+ mock: true
1658
+ });
1659
+ return prepared;
1660
+ }
1661
+ return deps.http().post("/epochs/series/prepare", {
1662
+ epochPrizePool: requireAddr(input?.epochPrizePool, "epochPrizePool"),
1663
+ series: requireSeries(input?.series),
1664
+ epochDuration: input.epochDuration,
1665
+ entry: input.entry,
1666
+ genesis: input.genesis,
1667
+ feeBps: input.feeBps,
1668
+ poolBps: input.poolBps,
1669
+ seedBps: input.seedBps
1670
+ });
1671
+ },
1552
1672
  async preparePool(input) {
1553
1673
  const studioWallet = requireAddr(input?.studioWallet, "studioWallet");
1554
1674
  const cfg = deps.config();
@@ -2262,7 +2382,7 @@ var Playmos = class {
2262
2382
  },
2263
2383
  walletAddress: async () => getAccount(this.walletProvider()),
2264
2384
  readView: async (to, data) => {
2265
- if (await walletAvailable(this.config.wallet, this.walletTimeoutPolicy())) {
2385
+ if (!await this.shouldServerSettle() && await walletAvailable(this.config.wallet, this.walletTimeoutPolicy())) {
2266
2386
  const provider = this.walletProvider();
2267
2387
  return await provider.request({
2268
2388
  method: "eth_call",
@@ -3363,8 +3483,14 @@ var Playmos = class {
3363
3483
  );
3364
3484
  }
3365
3485
  }
3486
+ if (config.settle === "server" && !this.env.isTest) {
3487
+ throw new ConfigError(
3488
+ 'settle: "server" is sandbox-only. Live keys must use a player wallet \u2014 never server-settle real money.',
3489
+ { field: "settle" }
3490
+ );
3491
+ }
3366
3492
  this.http = createHttpClient(this.env.apiBaseUrl, config.apiKey, config.retry);
3367
- if (!config.mock) {
3493
+ if (!config.mock && config.settle !== "server") {
3368
3494
  try {
3369
3495
  resolveProvider(config.wallet, this.walletTimeoutPolicy());
3370
3496
  } catch {
@@ -3434,6 +3560,23 @@ var Playmos = class {
3434
3560
  return 0n;
3435
3561
  }
3436
3562
  }
3563
+ /**
3564
+ * Sandbox server-settle when the developer asked (`settle: "server"`) or when
3565
+ * a test key has no wallet. Never calls walletAvailable if the ask is set —
3566
+ * that is the MetaMask-hijack delete (Moonhop leftover Slice 1).
3567
+ */
3568
+ async shouldServerSettle() {
3569
+ if (this.config.settle === "server") {
3570
+ if (!this.env.isTest) {
3571
+ throw new ConfigError(
3572
+ 'settle: "server" is sandbox-only. Live keys must use a player wallet \u2014 never server-settle real money.',
3573
+ { field: "settle" }
3574
+ );
3575
+ }
3576
+ return true;
3577
+ }
3578
+ return this.env.isTest && !await walletAvailable(this.config.wallet, this.walletTimeoutPolicy());
3579
+ }
3437
3580
  /**
3438
3581
  * Wallet timeout policy (#462 / PR #463 residual).
3439
3582
  * - connectTimeoutMs → eth_requestAccounts only
@@ -3561,7 +3704,7 @@ var Playmos = class {
3561
3704
  heldIdem ? { key: heldIdem, terms } : void 0
3562
3705
  );
3563
3706
  }
3564
- if (this.env.isTest && !await walletAvailable(this.config.wallet, this.walletTimeoutPolicy())) {
3707
+ if (await this.shouldServerSettle()) {
3565
3708
  const res = await this.http.post(
3566
3709
  "/payments",
3567
3710
  {
@@ -3666,7 +3809,7 @@ var Playmos = class {
3666
3809
  heldIdem ? { key: heldIdem, terms } : void 0
3667
3810
  );
3668
3811
  }
3669
- const serverSettleNoWallet = this.env.isTest && !await walletAvailable(this.config.wallet, this.walletTimeoutPolicy());
3812
+ const serverSettleNoWallet = await this.shouldServerSettle();
3670
3813
  if (!serverSettleNoWallet) {
3671
3814
  const pinned = typeof input.identity === "string" ? input.identity.trim() : "";
3672
3815
  if (!pinned) {
@@ -3996,7 +4139,7 @@ var Playmos = class {
3996
4139
  });
3997
4140
  let raw;
3998
4141
  try {
3999
- if (await walletAvailable(this.config.wallet, this.walletTimeoutPolicy())) {
4142
+ if (!await this.shouldServerSettle() && await walletAvailable(this.config.wallet, this.walletTimeoutPolicy())) {
4000
4143
  const provider = this.walletProvider();
4001
4144
  raw = await provider.request({
4002
4145
  method: "eth_call",
@@ -4215,8 +4358,11 @@ exports.C1_ENTER_SPLIT_MICRO = C1_ENTER_SPLIT_MICRO;
4215
4358
  exports.CHAIN_ID = CHAIN_ID;
4216
4359
  exports.ConfigError = ConfigError;
4217
4360
  exports.DEFAULT_API_BASE_URL = DEFAULT_API_BASE_URL;
4361
+ exports.DEFAULT_SERIES_POOL_BPS = DEFAULT_SERIES_POOL_BPS;
4362
+ exports.DEFAULT_SERIES_SEED_BPS = DEFAULT_SERIES_SEED_BPS;
4218
4363
  exports.InsufficientGasError = InsufficientGasError;
4219
4364
  exports.InvalidAmountError = InvalidAmountError;
4365
+ exports.LOCKED_SERIES_FEE_BPS = LOCKED_SERIES_FEE_BPS;
4220
4366
  exports.MICRO_PER_USDC = MICRO_PER_USDC;
4221
4367
  exports.MissingFieldError = MissingFieldError;
4222
4368
  exports.NothingToWithdrawError = NothingToWithdrawError;
@@ -4256,6 +4402,7 @@ exports.networkToCaip2 = networkToCaip2;
4256
4402
  exports.parsePaymentRequirement = parsePaymentRequirement;
4257
4403
  exports.parseUsdToMicro = parseUsdToMicro;
4258
4404
  exports.prefixedId = prefixedId;
4405
+ exports.prepareSeriesLocal = prepareSeriesLocal;
4259
4406
  exports.prepareStudioPoolLocal = prepareStudioPoolLocal;
4260
4407
  exports.previewEscrowFee = previewEscrowFee;
4261
4408
  exports.previewIapSplit = previewIapSplit;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as Call, P as PlaymosConfig, O as OnchainStatus, N as Network, W as WebhookEvent, R as RoundOpenInput, a as RoundState, b as RoundSettleInput, S as SettleRoundResult, c as RoundCancelInput, d as CancelRoundResult, e as RoundGetResult, A as ActiveSeriesRound, f as PayoutNoticesResult, g as PrizeBalance, h as WithdrawResult, i as AgentWallet, j as AgentFundResult, T as TransferResult, E as EscrowHoldInput, k as EscrowHoldResult, l as EscrowResolveResult, M as MarketplaceListInput, L as Listing, m as MarketplaceSaleResult, n as MarketplaceGetResult, o as PayInput, p as Payment, q as EnterRoundInput, r as TransferReconcile, s as WaitOptions, t as TransferInput, u as TransferConfirmOptions, V as VerifyResult, v as PayoutRule } from './errors-CYKgt2VU.cjs';
2
- export { w as ActiveForSeriesResult, x as AgentEconomyConfig, y as AlreadyEnteredError, z as ApiError, B as AuthError, D as ConfigError, F as ContractConfig, G as Eip1193Provider, H as GasConfig, I as GasMode, J as InsufficientGasError, K as InvalidAmountError, Q as ListingStatus, U as MarketplaceItem, X as MarketplaceSale, Y as MissingFieldError, Z as NothingToWithdrawError, _ as PaymentFailedError, $ as PaymentStatus, a0 as PayoutNotice, a1 as PayoutNoticeStatus, a2 as PlaymosError, a3 as PlaymosErrorCode, a4 as RetryOptions, a5 as RoundGetVia, a6 as RoundStatus, a7 as WalletConfig, a8 as WalletConnectionError, a9 as WalletConnector, aa as WalletTimeoutError, ab as WebhookEventType, ac as buildEpochExecuteSettlementCall } from './errors-CYKgt2VU.cjs';
1
+ import { C as Call, P as PlaymosConfig, O as OnchainStatus, N as Network, W as WebhookEvent, R as RoundOpenInput, a as RoundState, b as RoundSettleInput, S as SettleRoundResult, c as RoundCancelInput, d as CancelRoundResult, e as RoundGetResult, A as ActiveSeriesRound, f as PayoutNoticesResult, g as PrizeBalance, h as WithdrawResult, i as AgentWallet, j as AgentFundResult, T as TransferResult, E as EscrowHoldInput, k as EscrowHoldResult, l as EscrowResolveResult, M as MarketplaceListInput, L as Listing, m as MarketplaceSaleResult, n as MarketplaceGetResult, o as PayInput, p as Payment, q as EnterRoundInput, r as TransferReconcile, s as WaitOptions, t as TransferInput, u as TransferConfirmOptions, V as VerifyResult, v as PayoutRule } from './errors-WcDNfb4q.cjs';
2
+ export { w as ActiveForSeriesResult, x as AgentEconomyConfig, y as AlreadyEnteredError, z as ApiError, B as AuthError, D as ConfigError, F as ContractConfig, G as Eip1193Provider, H as GasConfig, I as GasMode, J as InsufficientGasError, K as InvalidAmountError, Q as ListingStatus, U as MarketplaceItem, X as MarketplaceSale, Y as MissingFieldError, Z as NothingToWithdrawError, _ as PaymentFailedError, $ as PaymentStatus, a0 as PayoutNotice, a1 as PayoutNoticeStatus, a2 as PlaymosError, a3 as PlaymosErrorCode, a4 as RetryOptions, a5 as RoundGetVia, a6 as RoundStatus, a7 as WalletConfig, a8 as WalletConnectionError, a9 as WalletConnector, aa as WalletTimeoutError, ab as WebhookEventType, ac as buildEpochExecuteSettlementCall } from './errors-WcDNfb4q.cjs';
3
3
 
4
4
  /**
5
5
  * Thin REST client for the Playmos service. Every SDK method that touches the
@@ -88,6 +88,8 @@ declare function buildEpochWithdrawCall(epochPrizePool: `0x${string}`): Call;
88
88
  * `epochs.enter` — pay into whatever window the clock says is current; no open
89
89
  * gate. `epochs.preparePool` / `registerPool` / `createPool` — studio wallet
90
90
  * deploys the existing EpochPrizePool constructor; Playmos is feeSink only.
91
+ * `epochs.prepareSeries` — unsigned createSeries tx; studio EOA signs; Playmos
92
+ * never broadcasts (sdk#739).
91
93
  * `epochs.settle` — secret key, operator wallet, broadcast + 202. Passes the
92
94
  * roster through; does not build a hub roster (R7b).
93
95
  * `epochs.claimableRefund` / `claimRefund` / `withdraw` — wallet-direct on
@@ -243,6 +245,44 @@ interface EpochEntry {
243
245
  via: EpochReadVia;
244
246
  mock?: true;
245
247
  }
248
+ /** Locked 1% Playmos take on a studio series. Any other fee is refused before signable bytes. */
249
+ declare const LOCKED_SERIES_FEE_BPS = 100;
250
+ /** Default prize-bucket bps when the studio does not type a split (fee 100 + seed 2000 = 10000). */
251
+ declare const DEFAULT_SERIES_POOL_BPS = 7900;
252
+ /** Default next-window seed bps when the studio does not type a split. */
253
+ declare const DEFAULT_SERIES_SEED_BPS = 2000;
254
+ interface EpochPrepareSeriesInput {
255
+ epochPrizePool: `0x${string}`;
256
+ series: string;
257
+ epochDuration: number | string;
258
+ /** Integer micro-USDC. */
259
+ entry: number | string;
260
+ genesis?: number | string;
261
+ /** Must be 100 if set — the locked 1% Playmos take. */
262
+ feeBps?: number;
263
+ poolBps?: number;
264
+ seedBps?: number;
265
+ }
266
+ interface EpochPreparedSeries {
267
+ series: string;
268
+ epochPrizePool: `0x${string}`;
269
+ genesis: string;
270
+ epochDuration: string;
271
+ entry: string;
272
+ feeBps: 100;
273
+ poolBps: number;
274
+ seedBps: number;
275
+ unsignedTx: {
276
+ chainId: number;
277
+ to: `0x${string}`;
278
+ value: "0";
279
+ data: `0x${string}`;
280
+ };
281
+ /** Playmos never broadcasts. Studio EOA sends. */
282
+ broadcast: false;
283
+ via: EpochReadVia;
284
+ mock?: true;
285
+ }
246
286
  interface EpochPreparePoolInput {
247
287
  studioWallet: `0x${string}`;
248
288
  /** Must equal the Playmos sink if set; any other address is refused before broadcast. */
@@ -531,6 +571,7 @@ interface EpochsApi {
531
571
  prize(input: EpochPrizeInput): Promise<EpochPrize>;
532
572
  get(input: EpochGetInput): Promise<EpochView>;
533
573
  getSeries(input: EpochGetSeriesInput): Promise<EpochSeriesView>;
574
+ prepareSeries(input: EpochPrepareSeriesInput): Promise<EpochPreparedSeries>;
534
575
  preparePool(input: EpochPreparePoolInput): Promise<EpochPreparedPool>;
535
576
  registerPool(input: EpochRegisterPoolInput): Promise<EpochStudioPool>;
536
577
  createPool(input: EpochCreatePoolInput): Promise<EpochStudioPool>;
@@ -603,6 +644,8 @@ interface EpochsDeps {
603
644
  declare function derivedEpochId(genesis: bigint, epochDuration: bigint, at: bigint): bigint;
604
645
  /** Settle payable is inherited seed + this epoch's pool (not outgoingSeed). */
605
646
  declare function epochPayableMicro(incomingSeed: bigint, pool: bigint): bigint;
647
+ /** Same refuse-before-signable posture as preparePool's feeSink check. */
648
+ declare function prepareSeriesLocal(input: EpochPrepareSeriesInput, chainId: number): EpochPreparedSeries;
606
649
  /** Client-side pin — same refusals as the service, so a bad sink never reaches broadcast. */
607
650
  declare function prepareStudioPoolLocal(input: EpochPreparePoolInput, playmosFeeSink: `0x${string}`, token: `0x${string}`, playmosAddresses?: `0x${string}`[]): EpochPreparedPool;
608
651
  /**
@@ -1323,6 +1366,12 @@ declare class Playmos {
1323
1366
  */
1324
1367
  private readonly mockByIdempotencyKey;
1325
1368
  constructor(config: PlaymosConfig);
1369
+ /**
1370
+ * Sandbox server-settle when the developer asked (`settle: "server"`) or when
1371
+ * a test key has no wallet. Never calls walletAvailable if the ask is set —
1372
+ * that is the MetaMask-hijack delete (Moonhop leftover Slice 1).
1373
+ */
1374
+ private shouldServerSettle;
1326
1375
  /**
1327
1376
  * Wallet timeout policy (#462 / PR #463 residual).
1328
1377
  * - connectTimeoutMs → eth_requestAccounts only
@@ -1587,4 +1636,4 @@ declare function ulid(seedTime?: number): string;
1587
1636
  /** `${prefix}_${ulid()}` — e.g. `pay_01J…`, `entry_01J…`, `idem_01J…`. */
1588
1637
  declare function prefixedId(prefix: string): string;
1589
1638
 
1590
- export { ActiveSeriesRound, AgentFundResult, AgentWallet, type Authorization, C1_ENTER_SPLIT_MICRO, CHAIN_ID, CancelRoundResult, type CreatePaymentRequirementInput, DEFAULT_API_BASE_URL, EnterRoundInput, type EpochAttestation, type EpochClaimRefundInput, type EpochClaimRefundResult, type EpochClaimableRefund, type EpochClaimableRefundInput, type EpochCreatePoolInput, type EpochCurrentId, type EpochCurrentIdInput, type EpochEnterInput, type EpochEntry, type EpochEntryStatus, type EpochExecuteSettlementInput, type EpochExecuteSettlementResult, type EpochGetAttestationInput, type EpochGetInput, type EpochGetSeriesInput, type EpochPoolOwnership, type EpochPoolWalletProof, type EpochPreparePoolInput, type EpochPreparedPool, type EpochPrize, type EpochPrizeInput, type EpochReadVia, type EpochRegisterPoolInput, type EpochSeriesView, type EpochSettleInput, type EpochSettleResult, type EpochSettleStatus, type EpochSettleWinner, type EpochStudioPool, type EpochTerminal, type EpochView, type EpochWinnerIn, type EpochWithdrawInput, type EpochWithdrawResult, type EpochsApi, EscrowHoldInput, EscrowHoldResult, EscrowResolveResult, Listing, MICRO_PER_USDC, MarketplaceGetResult, MarketplaceListInput, MarketplaceSaleResult, Network, PLAYMOS_FEE_SINK_DEFAULT, PayInput, Payment, type PaymentRequirement, PayoutError, PayoutNoticesResult, PayoutRule, PayoutRule as PayoutRuleCompute, Playmos, PlaymosConfig, PrizeBalance, RETIRED_PLAYMOS_FEE_SINK, RoundCancelInput, RoundGetResult, RoundOpenInput, RoundSettleInput, RoundState, SettleRoundResult, type SettlementAsset, type SettlementTypedData, type SettlementTypedDataInput, TransferConfirmOptions, TransferInput, TransferReconcile, TransferResult, USDC_ADDRESS, USDC_DECIMALS, VerifyResult, WaitOptions, type WalletSignatureAuthorization, WebhookEvent, WithdrawResult, type X402ChallengeInput, type X402ChallengeResult, type X402FulfillAuthorization, type X402PayInput, type X402PayloadAuthorization, type X402PayloadMode, type X402PaymentRequired, type X402SettleResult, buildEpochClaimRefundCall, buildEpochEntryCalls, buildEpochWithdrawCall, clientRuntimeSignals, computeIapSplit, computePayout, computePoolSplit, createEpochsApi, createPaymentRequirement, createX402Challenge, decodePaymentHeader, derivedEpochId, encodeEpochPoolConstructorArgs, encodePaymentHeader, epochPayableMicro, epochPoolProofMessage, formatMicroToUsd, identityToBytes32, isClientRuntime, isWalletSignatureAuthorization, isX402PayloadAuthorization, networkToCaip2, parsePaymentRequirement, parseUsdToMicro, prefixedId, prepareStudioPoolLocal, previewEscrowFee, previewIapSplit, previewMarketplaceSplit, previewPoolSplit, previewTransferSplit, resolveEnv, serializePaymentRequirement, seriesToBytes32, settlementTypedData, toX402PaymentRequired, ulid, validateX402ChallengeInput };
1639
+ export { ActiveSeriesRound, AgentFundResult, AgentWallet, type Authorization, C1_ENTER_SPLIT_MICRO, CHAIN_ID, CancelRoundResult, type CreatePaymentRequirementInput, DEFAULT_API_BASE_URL, DEFAULT_SERIES_POOL_BPS, DEFAULT_SERIES_SEED_BPS, EnterRoundInput, type EpochAttestation, type EpochClaimRefundInput, type EpochClaimRefundResult, type EpochClaimableRefund, type EpochClaimableRefundInput, type EpochCreatePoolInput, type EpochCurrentId, type EpochCurrentIdInput, type EpochEnterInput, type EpochEntry, type EpochEntryStatus, type EpochExecuteSettlementInput, type EpochExecuteSettlementResult, type EpochGetAttestationInput, type EpochGetInput, type EpochGetSeriesInput, type EpochPoolOwnership, type EpochPoolWalletProof, type EpochPreparePoolInput, type EpochPrepareSeriesInput, type EpochPreparedPool, type EpochPreparedSeries, type EpochPrize, type EpochPrizeInput, type EpochReadVia, type EpochRegisterPoolInput, type EpochSeriesView, type EpochSettleInput, type EpochSettleResult, type EpochSettleStatus, type EpochSettleWinner, type EpochStudioPool, type EpochTerminal, type EpochView, type EpochWinnerIn, type EpochWithdrawInput, type EpochWithdrawResult, type EpochsApi, EscrowHoldInput, EscrowHoldResult, EscrowResolveResult, LOCKED_SERIES_FEE_BPS, Listing, MICRO_PER_USDC, MarketplaceGetResult, MarketplaceListInput, MarketplaceSaleResult, Network, PLAYMOS_FEE_SINK_DEFAULT, PayInput, Payment, type PaymentRequirement, PayoutError, PayoutNoticesResult, PayoutRule, PayoutRule as PayoutRuleCompute, Playmos, PlaymosConfig, PrizeBalance, RETIRED_PLAYMOS_FEE_SINK, RoundCancelInput, RoundGetResult, RoundOpenInput, RoundSettleInput, RoundState, SettleRoundResult, type SettlementAsset, type SettlementTypedData, type SettlementTypedDataInput, TransferConfirmOptions, TransferInput, TransferReconcile, TransferResult, USDC_ADDRESS, USDC_DECIMALS, VerifyResult, WaitOptions, type WalletSignatureAuthorization, WebhookEvent, WithdrawResult, type X402ChallengeInput, type X402ChallengeResult, type X402FulfillAuthorization, type X402PayInput, type X402PayloadAuthorization, type X402PayloadMode, type X402PaymentRequired, type X402SettleResult, buildEpochClaimRefundCall, buildEpochEntryCalls, buildEpochWithdrawCall, clientRuntimeSignals, computeIapSplit, computePayout, computePoolSplit, createEpochsApi, createPaymentRequirement, createX402Challenge, decodePaymentHeader, derivedEpochId, encodeEpochPoolConstructorArgs, encodePaymentHeader, epochPayableMicro, epochPoolProofMessage, formatMicroToUsd, identityToBytes32, isClientRuntime, isWalletSignatureAuthorization, isX402PayloadAuthorization, networkToCaip2, parsePaymentRequirement, parseUsdToMicro, prefixedId, prepareSeriesLocal, prepareStudioPoolLocal, previewEscrowFee, previewIapSplit, previewMarketplaceSplit, previewPoolSplit, previewTransferSplit, resolveEnv, serializePaymentRequirement, seriesToBytes32, settlementTypedData, toX402PaymentRequired, ulid, validateX402ChallengeInput };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as Call, P as PlaymosConfig, O as OnchainStatus, N as Network, W as WebhookEvent, R as RoundOpenInput, a as RoundState, b as RoundSettleInput, S as SettleRoundResult, c as RoundCancelInput, d as CancelRoundResult, e as RoundGetResult, A as ActiveSeriesRound, f as PayoutNoticesResult, g as PrizeBalance, h as WithdrawResult, i as AgentWallet, j as AgentFundResult, T as TransferResult, E as EscrowHoldInput, k as EscrowHoldResult, l as EscrowResolveResult, M as MarketplaceListInput, L as Listing, m as MarketplaceSaleResult, n as MarketplaceGetResult, o as PayInput, p as Payment, q as EnterRoundInput, r as TransferReconcile, s as WaitOptions, t as TransferInput, u as TransferConfirmOptions, V as VerifyResult, v as PayoutRule } from './errors-CYKgt2VU.js';
2
- export { w as ActiveForSeriesResult, x as AgentEconomyConfig, y as AlreadyEnteredError, z as ApiError, B as AuthError, D as ConfigError, F as ContractConfig, G as Eip1193Provider, H as GasConfig, I as GasMode, J as InsufficientGasError, K as InvalidAmountError, Q as ListingStatus, U as MarketplaceItem, X as MarketplaceSale, Y as MissingFieldError, Z as NothingToWithdrawError, _ as PaymentFailedError, $ as PaymentStatus, a0 as PayoutNotice, a1 as PayoutNoticeStatus, a2 as PlaymosError, a3 as PlaymosErrorCode, a4 as RetryOptions, a5 as RoundGetVia, a6 as RoundStatus, a7 as WalletConfig, a8 as WalletConnectionError, a9 as WalletConnector, aa as WalletTimeoutError, ab as WebhookEventType, ac as buildEpochExecuteSettlementCall } from './errors-CYKgt2VU.js';
1
+ import { C as Call, P as PlaymosConfig, O as OnchainStatus, N as Network, W as WebhookEvent, R as RoundOpenInput, a as RoundState, b as RoundSettleInput, S as SettleRoundResult, c as RoundCancelInput, d as CancelRoundResult, e as RoundGetResult, A as ActiveSeriesRound, f as PayoutNoticesResult, g as PrizeBalance, h as WithdrawResult, i as AgentWallet, j as AgentFundResult, T as TransferResult, E as EscrowHoldInput, k as EscrowHoldResult, l as EscrowResolveResult, M as MarketplaceListInput, L as Listing, m as MarketplaceSaleResult, n as MarketplaceGetResult, o as PayInput, p as Payment, q as EnterRoundInput, r as TransferReconcile, s as WaitOptions, t as TransferInput, u as TransferConfirmOptions, V as VerifyResult, v as PayoutRule } from './errors-WcDNfb4q.js';
2
+ export { w as ActiveForSeriesResult, x as AgentEconomyConfig, y as AlreadyEnteredError, z as ApiError, B as AuthError, D as ConfigError, F as ContractConfig, G as Eip1193Provider, H as GasConfig, I as GasMode, J as InsufficientGasError, K as InvalidAmountError, Q as ListingStatus, U as MarketplaceItem, X as MarketplaceSale, Y as MissingFieldError, Z as NothingToWithdrawError, _ as PaymentFailedError, $ as PaymentStatus, a0 as PayoutNotice, a1 as PayoutNoticeStatus, a2 as PlaymosError, a3 as PlaymosErrorCode, a4 as RetryOptions, a5 as RoundGetVia, a6 as RoundStatus, a7 as WalletConfig, a8 as WalletConnectionError, a9 as WalletConnector, aa as WalletTimeoutError, ab as WebhookEventType, ac as buildEpochExecuteSettlementCall } from './errors-WcDNfb4q.js';
3
3
 
4
4
  /**
5
5
  * Thin REST client for the Playmos service. Every SDK method that touches the
@@ -88,6 +88,8 @@ declare function buildEpochWithdrawCall(epochPrizePool: `0x${string}`): Call;
88
88
  * `epochs.enter` — pay into whatever window the clock says is current; no open
89
89
  * gate. `epochs.preparePool` / `registerPool` / `createPool` — studio wallet
90
90
  * deploys the existing EpochPrizePool constructor; Playmos is feeSink only.
91
+ * `epochs.prepareSeries` — unsigned createSeries tx; studio EOA signs; Playmos
92
+ * never broadcasts (sdk#739).
91
93
  * `epochs.settle` — secret key, operator wallet, broadcast + 202. Passes the
92
94
  * roster through; does not build a hub roster (R7b).
93
95
  * `epochs.claimableRefund` / `claimRefund` / `withdraw` — wallet-direct on
@@ -243,6 +245,44 @@ interface EpochEntry {
243
245
  via: EpochReadVia;
244
246
  mock?: true;
245
247
  }
248
+ /** Locked 1% Playmos take on a studio series. Any other fee is refused before signable bytes. */
249
+ declare const LOCKED_SERIES_FEE_BPS = 100;
250
+ /** Default prize-bucket bps when the studio does not type a split (fee 100 + seed 2000 = 10000). */
251
+ declare const DEFAULT_SERIES_POOL_BPS = 7900;
252
+ /** Default next-window seed bps when the studio does not type a split. */
253
+ declare const DEFAULT_SERIES_SEED_BPS = 2000;
254
+ interface EpochPrepareSeriesInput {
255
+ epochPrizePool: `0x${string}`;
256
+ series: string;
257
+ epochDuration: number | string;
258
+ /** Integer micro-USDC. */
259
+ entry: number | string;
260
+ genesis?: number | string;
261
+ /** Must be 100 if set — the locked 1% Playmos take. */
262
+ feeBps?: number;
263
+ poolBps?: number;
264
+ seedBps?: number;
265
+ }
266
+ interface EpochPreparedSeries {
267
+ series: string;
268
+ epochPrizePool: `0x${string}`;
269
+ genesis: string;
270
+ epochDuration: string;
271
+ entry: string;
272
+ feeBps: 100;
273
+ poolBps: number;
274
+ seedBps: number;
275
+ unsignedTx: {
276
+ chainId: number;
277
+ to: `0x${string}`;
278
+ value: "0";
279
+ data: `0x${string}`;
280
+ };
281
+ /** Playmos never broadcasts. Studio EOA sends. */
282
+ broadcast: false;
283
+ via: EpochReadVia;
284
+ mock?: true;
285
+ }
246
286
  interface EpochPreparePoolInput {
247
287
  studioWallet: `0x${string}`;
248
288
  /** Must equal the Playmos sink if set; any other address is refused before broadcast. */
@@ -531,6 +571,7 @@ interface EpochsApi {
531
571
  prize(input: EpochPrizeInput): Promise<EpochPrize>;
532
572
  get(input: EpochGetInput): Promise<EpochView>;
533
573
  getSeries(input: EpochGetSeriesInput): Promise<EpochSeriesView>;
574
+ prepareSeries(input: EpochPrepareSeriesInput): Promise<EpochPreparedSeries>;
534
575
  preparePool(input: EpochPreparePoolInput): Promise<EpochPreparedPool>;
535
576
  registerPool(input: EpochRegisterPoolInput): Promise<EpochStudioPool>;
536
577
  createPool(input: EpochCreatePoolInput): Promise<EpochStudioPool>;
@@ -603,6 +644,8 @@ interface EpochsDeps {
603
644
  declare function derivedEpochId(genesis: bigint, epochDuration: bigint, at: bigint): bigint;
604
645
  /** Settle payable is inherited seed + this epoch's pool (not outgoingSeed). */
605
646
  declare function epochPayableMicro(incomingSeed: bigint, pool: bigint): bigint;
647
+ /** Same refuse-before-signable posture as preparePool's feeSink check. */
648
+ declare function prepareSeriesLocal(input: EpochPrepareSeriesInput, chainId: number): EpochPreparedSeries;
606
649
  /** Client-side pin — same refusals as the service, so a bad sink never reaches broadcast. */
607
650
  declare function prepareStudioPoolLocal(input: EpochPreparePoolInput, playmosFeeSink: `0x${string}`, token: `0x${string}`, playmosAddresses?: `0x${string}`[]): EpochPreparedPool;
608
651
  /**
@@ -1323,6 +1366,12 @@ declare class Playmos {
1323
1366
  */
1324
1367
  private readonly mockByIdempotencyKey;
1325
1368
  constructor(config: PlaymosConfig);
1369
+ /**
1370
+ * Sandbox server-settle when the developer asked (`settle: "server"`) or when
1371
+ * a test key has no wallet. Never calls walletAvailable if the ask is set —
1372
+ * that is the MetaMask-hijack delete (Moonhop leftover Slice 1).
1373
+ */
1374
+ private shouldServerSettle;
1326
1375
  /**
1327
1376
  * Wallet timeout policy (#462 / PR #463 residual).
1328
1377
  * - connectTimeoutMs → eth_requestAccounts only
@@ -1587,4 +1636,4 @@ declare function ulid(seedTime?: number): string;
1587
1636
  /** `${prefix}_${ulid()}` — e.g. `pay_01J…`, `entry_01J…`, `idem_01J…`. */
1588
1637
  declare function prefixedId(prefix: string): string;
1589
1638
 
1590
- export { ActiveSeriesRound, AgentFundResult, AgentWallet, type Authorization, C1_ENTER_SPLIT_MICRO, CHAIN_ID, CancelRoundResult, type CreatePaymentRequirementInput, DEFAULT_API_BASE_URL, EnterRoundInput, type EpochAttestation, type EpochClaimRefundInput, type EpochClaimRefundResult, type EpochClaimableRefund, type EpochClaimableRefundInput, type EpochCreatePoolInput, type EpochCurrentId, type EpochCurrentIdInput, type EpochEnterInput, type EpochEntry, type EpochEntryStatus, type EpochExecuteSettlementInput, type EpochExecuteSettlementResult, type EpochGetAttestationInput, type EpochGetInput, type EpochGetSeriesInput, type EpochPoolOwnership, type EpochPoolWalletProof, type EpochPreparePoolInput, type EpochPreparedPool, type EpochPrize, type EpochPrizeInput, type EpochReadVia, type EpochRegisterPoolInput, type EpochSeriesView, type EpochSettleInput, type EpochSettleResult, type EpochSettleStatus, type EpochSettleWinner, type EpochStudioPool, type EpochTerminal, type EpochView, type EpochWinnerIn, type EpochWithdrawInput, type EpochWithdrawResult, type EpochsApi, EscrowHoldInput, EscrowHoldResult, EscrowResolveResult, Listing, MICRO_PER_USDC, MarketplaceGetResult, MarketplaceListInput, MarketplaceSaleResult, Network, PLAYMOS_FEE_SINK_DEFAULT, PayInput, Payment, type PaymentRequirement, PayoutError, PayoutNoticesResult, PayoutRule, PayoutRule as PayoutRuleCompute, Playmos, PlaymosConfig, PrizeBalance, RETIRED_PLAYMOS_FEE_SINK, RoundCancelInput, RoundGetResult, RoundOpenInput, RoundSettleInput, RoundState, SettleRoundResult, type SettlementAsset, type SettlementTypedData, type SettlementTypedDataInput, TransferConfirmOptions, TransferInput, TransferReconcile, TransferResult, USDC_ADDRESS, USDC_DECIMALS, VerifyResult, WaitOptions, type WalletSignatureAuthorization, WebhookEvent, WithdrawResult, type X402ChallengeInput, type X402ChallengeResult, type X402FulfillAuthorization, type X402PayInput, type X402PayloadAuthorization, type X402PayloadMode, type X402PaymentRequired, type X402SettleResult, buildEpochClaimRefundCall, buildEpochEntryCalls, buildEpochWithdrawCall, clientRuntimeSignals, computeIapSplit, computePayout, computePoolSplit, createEpochsApi, createPaymentRequirement, createX402Challenge, decodePaymentHeader, derivedEpochId, encodeEpochPoolConstructorArgs, encodePaymentHeader, epochPayableMicro, epochPoolProofMessage, formatMicroToUsd, identityToBytes32, isClientRuntime, isWalletSignatureAuthorization, isX402PayloadAuthorization, networkToCaip2, parsePaymentRequirement, parseUsdToMicro, prefixedId, prepareStudioPoolLocal, previewEscrowFee, previewIapSplit, previewMarketplaceSplit, previewPoolSplit, previewTransferSplit, resolveEnv, serializePaymentRequirement, seriesToBytes32, settlementTypedData, toX402PaymentRequired, ulid, validateX402ChallengeInput };
1639
+ export { ActiveSeriesRound, AgentFundResult, AgentWallet, type Authorization, C1_ENTER_SPLIT_MICRO, CHAIN_ID, CancelRoundResult, type CreatePaymentRequirementInput, DEFAULT_API_BASE_URL, DEFAULT_SERIES_POOL_BPS, DEFAULT_SERIES_SEED_BPS, EnterRoundInput, type EpochAttestation, type EpochClaimRefundInput, type EpochClaimRefundResult, type EpochClaimableRefund, type EpochClaimableRefundInput, type EpochCreatePoolInput, type EpochCurrentId, type EpochCurrentIdInput, type EpochEnterInput, type EpochEntry, type EpochEntryStatus, type EpochExecuteSettlementInput, type EpochExecuteSettlementResult, type EpochGetAttestationInput, type EpochGetInput, type EpochGetSeriesInput, type EpochPoolOwnership, type EpochPoolWalletProof, type EpochPreparePoolInput, type EpochPrepareSeriesInput, type EpochPreparedPool, type EpochPreparedSeries, type EpochPrize, type EpochPrizeInput, type EpochReadVia, type EpochRegisterPoolInput, type EpochSeriesView, type EpochSettleInput, type EpochSettleResult, type EpochSettleStatus, type EpochSettleWinner, type EpochStudioPool, type EpochTerminal, type EpochView, type EpochWinnerIn, type EpochWithdrawInput, type EpochWithdrawResult, type EpochsApi, EscrowHoldInput, EscrowHoldResult, EscrowResolveResult, LOCKED_SERIES_FEE_BPS, Listing, MICRO_PER_USDC, MarketplaceGetResult, MarketplaceListInput, MarketplaceSaleResult, Network, PLAYMOS_FEE_SINK_DEFAULT, PayInput, Payment, type PaymentRequirement, PayoutError, PayoutNoticesResult, PayoutRule, PayoutRule as PayoutRuleCompute, Playmos, PlaymosConfig, PrizeBalance, RETIRED_PLAYMOS_FEE_SINK, RoundCancelInput, RoundGetResult, RoundOpenInput, RoundSettleInput, RoundState, SettleRoundResult, type SettlementAsset, type SettlementTypedData, type SettlementTypedDataInput, TransferConfirmOptions, TransferInput, TransferReconcile, TransferResult, USDC_ADDRESS, USDC_DECIMALS, VerifyResult, WaitOptions, type WalletSignatureAuthorization, WebhookEvent, WithdrawResult, type X402ChallengeInput, type X402ChallengeResult, type X402FulfillAuthorization, type X402PayInput, type X402PayloadAuthorization, type X402PayloadMode, type X402PaymentRequired, type X402SettleResult, buildEpochClaimRefundCall, buildEpochEntryCalls, buildEpochWithdrawCall, clientRuntimeSignals, computeIapSplit, computePayout, computePoolSplit, createEpochsApi, createPaymentRequirement, createX402Challenge, decodePaymentHeader, derivedEpochId, encodeEpochPoolConstructorArgs, encodePaymentHeader, epochPayableMicro, epochPoolProofMessage, formatMicroToUsd, identityToBytes32, isClientRuntime, isWalletSignatureAuthorization, isX402PayloadAuthorization, networkToCaip2, parsePaymentRequirement, parseUsdToMicro, prefixedId, prepareSeriesLocal, prepareStudioPoolLocal, previewEscrowFee, previewIapSplit, previewMarketplaceSplit, previewPoolSplit, previewTransferSplit, resolveEnv, serializePaymentRequirement, seriesToBytes32, settlementTypedData, toX402PaymentRequired, ulid, validateX402ChallengeInput };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { AuthError, InvalidAmountError, epochPrizePoolEnterAbi, seriesToBytes32, identityToBytes32, encodeApprove, epochPrizePoolRefundAbi, ConfigError, MissingFieldError, buildEpochExecuteSettlementCall, PaymentFailedError, epochPrizePoolViewAbi, ApiError, assertEnoughGas, sendCalls, waitForCalls, NothingToWithdrawError, buildWithdrawCall, WalletTimeoutError, buildIapCalls, buildEntryCalls, prizePoolAbi, WalletConnectionError, AlreadyEnteredError } from './chunk-TZFGZNXV.js';
2
- export { AlreadyEnteredError, ApiError, AuthError, ConfigError, InsufficientGasError, InvalidAmountError, MissingFieldError, NothingToWithdrawError, PaymentFailedError, PlaymosError, WalletConnectionError, WalletTimeoutError, buildEpochExecuteSettlementCall, identityToBytes32, seriesToBytes32 } from './chunk-TZFGZNXV.js';
1
+ import { AuthError, InvalidAmountError, epochPrizePoolEnterAbi, seriesToBytes32, identityToBytes32, encodeApprove, epochPrizePoolRefundAbi, ConfigError, epochPrizePoolCreateSeriesAbi, MissingFieldError, buildEpochExecuteSettlementCall, PaymentFailedError, epochPrizePoolViewAbi, ApiError, assertEnoughGas, sendCalls, waitForCalls, NothingToWithdrawError, buildWithdrawCall, WalletTimeoutError, buildIapCalls, buildEntryCalls, prizePoolAbi, WalletConnectionError, AlreadyEnteredError } from './chunk-ROWX6HVU.js';
2
+ export { AlreadyEnteredError, ApiError, AuthError, ConfigError, InsufficientGasError, InvalidAmountError, MissingFieldError, NothingToWithdrawError, PaymentFailedError, PlaymosError, WalletConnectionError, WalletTimeoutError, buildEpochExecuteSettlementCall, identityToBytes32, seriesToBytes32 } from './chunk-ROWX6HVU.js';
3
3
  import { encodeFunctionData, encodeAbiParameters, decodeFunctionResult } from 'viem';
4
4
 
5
5
  // src/config.ts
@@ -692,6 +692,9 @@ function encodeEpochPoolConstructorArgs(args) {
692
692
  args.refundTimeout
693
693
  ]);
694
694
  }
695
+ var LOCKED_SERIES_FEE_BPS = 100;
696
+ var DEFAULT_SERIES_POOL_BPS = 7900;
697
+ var DEFAULT_SERIES_SEED_BPS = 2e3;
695
698
  function derivedEpochId(genesis, epochDuration, at) {
696
699
  if (epochDuration === 0n) {
697
700
  throw new ConfigError("epochDuration must be > 0");
@@ -860,6 +863,66 @@ function walletStatus(status) {
860
863
  function sameAddr(a, b) {
861
864
  return a.toLowerCase() === b.toLowerCase();
862
865
  }
866
+ function parseSeriesBps(raw, field) {
867
+ if (raw === void 0) return void 0;
868
+ if (!Number.isInteger(raw) || raw < 0) {
869
+ throw new ConfigError(`${field} must be a non-negative integer`);
870
+ }
871
+ return raw;
872
+ }
873
+ function prepareSeriesLocal(input, chainId) {
874
+ const pool = requireAddr(input.epochPrizePool, "epochPrizePool");
875
+ const series = requireSeries(input.series);
876
+ const feeBps = parseSeriesBps(input.feeBps, "feeBps");
877
+ if (feeBps !== void 0 && feeBps !== LOCKED_SERIES_FEE_BPS) {
878
+ throw new ConfigError(
879
+ "feeBps must be 100 (the locked 1% Playmos take); refused before anything signable"
880
+ );
881
+ }
882
+ const poolBps = parseSeriesBps(input.poolBps, "poolBps") ?? DEFAULT_SERIES_POOL_BPS;
883
+ const seedBps = parseSeriesBps(input.seedBps, "seedBps") ?? DEFAULT_SERIES_SEED_BPS;
884
+ if (LOCKED_SERIES_FEE_BPS + poolBps + seedBps !== 1e4) {
885
+ throw new ConfigError("feeBps + poolBps + seedBps must sum to 10000 (fee is 100)");
886
+ }
887
+ const epochDuration = toBig(input.epochDuration, 0n);
888
+ const entry = toBig(input.entry, 0n);
889
+ if (epochDuration <= 0n) throw new ConfigError("epochDuration must be greater than 0");
890
+ if (entry <= 0n) throw new ConfigError("entry must be greater than 0");
891
+ const genesis = toBig(input.genesis, BigInt(Math.floor(Date.now() / 1e3)));
892
+ if (genesis <= 0n) throw new ConfigError("genesis must be greater than 0");
893
+ const data = encodeFunctionData({
894
+ abi: epochPrizePoolCreateSeriesAbi,
895
+ functionName: "createSeries",
896
+ args: [
897
+ seriesToBytes32(series),
898
+ genesis,
899
+ epochDuration,
900
+ entry,
901
+ LOCKED_SERIES_FEE_BPS,
902
+ poolBps,
903
+ seedBps
904
+ ]
905
+ });
906
+ return {
907
+ series,
908
+ epochPrizePool: pool,
909
+ genesis: genesis.toString(),
910
+ epochDuration: epochDuration.toString(),
911
+ entry: entry.toString(),
912
+ feeBps: 100,
913
+ poolBps,
914
+ seedBps,
915
+ unsignedTx: {
916
+ chainId,
917
+ to: pool,
918
+ value: "0",
919
+ data
920
+ },
921
+ broadcast: false,
922
+ via: "mock",
923
+ mock: true
924
+ };
925
+ }
863
926
  function prepareStudioPoolLocal(input, playmosFeeSink, token, playmosAddresses = []) {
864
927
  const studio = requireAddr(input.studioWallet, "studioWallet");
865
928
  const locked = PLAYMOS_FEE_SINK_DEFAULT;
@@ -1008,6 +1071,7 @@ async function readEpochTerminal(deps, pool, series, epochId) {
1008
1071
  }
1009
1072
  }
1010
1073
  function createEpochsApi(deps) {
1074
+ const mockPreparedSeries = /* @__PURE__ */ new Map();
1011
1075
  return {
1012
1076
  async currentId(input) {
1013
1077
  const series = requireSeries(input?.series);
@@ -1085,6 +1149,9 @@ function createEpochsApi(deps) {
1085
1149
  const series = requireSeries(input?.series);
1086
1150
  const cfg = deps.config();
1087
1151
  if (cfg.mock) {
1152
+ const pool = (input.epochPrizePool ?? cfg.contracts?.epochPrizePool ?? "").toLowerCase();
1153
+ const prepared = mockPreparedSeries.get(`${pool}:${series}`);
1154
+ if (prepared) return prepared;
1088
1155
  return {
1089
1156
  series,
1090
1157
  created: true,
@@ -1105,6 +1172,42 @@ function createEpochsApi(deps) {
1105
1172
  })}`;
1106
1173
  return deps.http().get(path);
1107
1174
  },
1175
+ async prepareSeries(input) {
1176
+ const feeBps = input?.feeBps;
1177
+ if (feeBps !== void 0 && feeBps !== LOCKED_SERIES_FEE_BPS) {
1178
+ throw new ConfigError(
1179
+ "feeBps must be 100 (the locked 1% Playmos take); refused before anything signable"
1180
+ );
1181
+ }
1182
+ const cfg = deps.config();
1183
+ if (cfg.mock) {
1184
+ const prepared = prepareSeriesLocal(input, CHAIN_ID["base-sepolia"]);
1185
+ mockPreparedSeries.set(`${prepared.epochPrizePool}:${prepared.series}`, {
1186
+ series: prepared.series,
1187
+ created: true,
1188
+ genesis: prepared.genesis,
1189
+ epochDuration: prepared.epochDuration,
1190
+ entry: prepared.entry,
1191
+ feeBps: prepared.feeBps,
1192
+ poolBps: prepared.poolBps,
1193
+ seedBps: prepared.seedBps,
1194
+ via: "mock",
1195
+ epochPrizePool: prepared.epochPrizePool,
1196
+ mock: true
1197
+ });
1198
+ return prepared;
1199
+ }
1200
+ return deps.http().post("/epochs/series/prepare", {
1201
+ epochPrizePool: requireAddr(input?.epochPrizePool, "epochPrizePool"),
1202
+ series: requireSeries(input?.series),
1203
+ epochDuration: input.epochDuration,
1204
+ entry: input.entry,
1205
+ genesis: input.genesis,
1206
+ feeBps: input.feeBps,
1207
+ poolBps: input.poolBps,
1208
+ seedBps: input.seedBps
1209
+ });
1210
+ },
1108
1211
  async preparePool(input) {
1109
1212
  const studioWallet = requireAddr(input?.studioWallet, "studioWallet");
1110
1213
  const cfg = deps.config();
@@ -1818,7 +1921,7 @@ var Playmos = class {
1818
1921
  },
1819
1922
  walletAddress: async () => getAccount(this.walletProvider()),
1820
1923
  readView: async (to, data) => {
1821
- if (await walletAvailable(this.config.wallet, this.walletTimeoutPolicy())) {
1924
+ if (!await this.shouldServerSettle() && await walletAvailable(this.config.wallet, this.walletTimeoutPolicy())) {
1822
1925
  const provider = this.walletProvider();
1823
1926
  return await provider.request({
1824
1927
  method: "eth_call",
@@ -2919,8 +3022,14 @@ var Playmos = class {
2919
3022
  );
2920
3023
  }
2921
3024
  }
3025
+ if (config.settle === "server" && !this.env.isTest) {
3026
+ throw new ConfigError(
3027
+ 'settle: "server" is sandbox-only. Live keys must use a player wallet \u2014 never server-settle real money.',
3028
+ { field: "settle" }
3029
+ );
3030
+ }
2922
3031
  this.http = createHttpClient(this.env.apiBaseUrl, config.apiKey, config.retry);
2923
- if (!config.mock) {
3032
+ if (!config.mock && config.settle !== "server") {
2924
3033
  try {
2925
3034
  resolveProvider(config.wallet, this.walletTimeoutPolicy());
2926
3035
  } catch {
@@ -2990,6 +3099,23 @@ var Playmos = class {
2990
3099
  return 0n;
2991
3100
  }
2992
3101
  }
3102
+ /**
3103
+ * Sandbox server-settle when the developer asked (`settle: "server"`) or when
3104
+ * a test key has no wallet. Never calls walletAvailable if the ask is set —
3105
+ * that is the MetaMask-hijack delete (Moonhop leftover Slice 1).
3106
+ */
3107
+ async shouldServerSettle() {
3108
+ if (this.config.settle === "server") {
3109
+ if (!this.env.isTest) {
3110
+ throw new ConfigError(
3111
+ 'settle: "server" is sandbox-only. Live keys must use a player wallet \u2014 never server-settle real money.',
3112
+ { field: "settle" }
3113
+ );
3114
+ }
3115
+ return true;
3116
+ }
3117
+ return this.env.isTest && !await walletAvailable(this.config.wallet, this.walletTimeoutPolicy());
3118
+ }
2993
3119
  /**
2994
3120
  * Wallet timeout policy (#462 / PR #463 residual).
2995
3121
  * - connectTimeoutMs → eth_requestAccounts only
@@ -3117,7 +3243,7 @@ var Playmos = class {
3117
3243
  heldIdem ? { key: heldIdem, terms } : void 0
3118
3244
  );
3119
3245
  }
3120
- if (this.env.isTest && !await walletAvailable(this.config.wallet, this.walletTimeoutPolicy())) {
3246
+ if (await this.shouldServerSettle()) {
3121
3247
  const res = await this.http.post(
3122
3248
  "/payments",
3123
3249
  {
@@ -3222,7 +3348,7 @@ var Playmos = class {
3222
3348
  heldIdem ? { key: heldIdem, terms } : void 0
3223
3349
  );
3224
3350
  }
3225
- const serverSettleNoWallet = this.env.isTest && !await walletAvailable(this.config.wallet, this.walletTimeoutPolicy());
3351
+ const serverSettleNoWallet = await this.shouldServerSettle();
3226
3352
  if (!serverSettleNoWallet) {
3227
3353
  const pinned = typeof input.identity === "string" ? input.identity.trim() : "";
3228
3354
  if (!pinned) {
@@ -3552,7 +3678,7 @@ var Playmos = class {
3552
3678
  });
3553
3679
  let raw;
3554
3680
  try {
3555
- if (await walletAvailable(this.config.wallet, this.walletTimeoutPolicy())) {
3681
+ if (!await this.shouldServerSettle() && await walletAvailable(this.config.wallet, this.walletTimeoutPolicy())) {
3556
3682
  const provider = this.walletProvider();
3557
3683
  raw = await provider.request({
3558
3684
  method: "eth_call",
@@ -3764,4 +3890,4 @@ function isX402PayloadAuthorization(auth) {
3764
3890
  return auth.kind === "x402-payload";
3765
3891
  }
3766
3892
 
3767
- export { C1_ENTER_SPLIT_MICRO, CHAIN_ID, DEFAULT_API_BASE_URL, MICRO_PER_USDC, PLAYMOS_FEE_SINK_DEFAULT, PayoutError, Playmos, RETIRED_PLAYMOS_FEE_SINK, USDC_ADDRESS, USDC_DECIMALS, buildEpochClaimRefundCall, buildEpochEntryCalls, buildEpochWithdrawCall, clientRuntimeSignals, computeIapSplit, computePayout, computePoolSplit, createEpochsApi, createPaymentRequirement, createX402Challenge, decodePaymentHeader, derivedEpochId, encodeEpochPoolConstructorArgs, encodePaymentHeader, epochPayableMicro, epochPoolProofMessage, formatMicroToUsd, isClientRuntime, isWalletSignatureAuthorization, isX402PayloadAuthorization, networkToCaip2, parsePaymentRequirement, parseUsdToMicro, prefixedId, prepareStudioPoolLocal, previewEscrowFee, previewIapSplit, previewMarketplaceSplit, previewPoolSplit, previewTransferSplit, resolveEnv, serializePaymentRequirement, settlementTypedData, toX402PaymentRequired, ulid, validateX402ChallengeInput };
3893
+ export { C1_ENTER_SPLIT_MICRO, CHAIN_ID, DEFAULT_API_BASE_URL, DEFAULT_SERIES_POOL_BPS, DEFAULT_SERIES_SEED_BPS, LOCKED_SERIES_FEE_BPS, MICRO_PER_USDC, PLAYMOS_FEE_SINK_DEFAULT, PayoutError, Playmos, RETIRED_PLAYMOS_FEE_SINK, USDC_ADDRESS, USDC_DECIMALS, buildEpochClaimRefundCall, buildEpochEntryCalls, buildEpochWithdrawCall, clientRuntimeSignals, computeIapSplit, computePayout, computePoolSplit, createEpochsApi, createPaymentRequirement, createX402Challenge, decodePaymentHeader, derivedEpochId, encodeEpochPoolConstructorArgs, encodePaymentHeader, epochPayableMicro, epochPoolProofMessage, formatMicroToUsd, isClientRuntime, isWalletSignatureAuthorization, isX402PayloadAuthorization, networkToCaip2, parsePaymentRequirement, parseUsdToMicro, prefixedId, prepareSeriesLocal, prepareStudioPoolLocal, previewEscrowFee, previewIapSplit, previewMarketplaceSplit, previewPoolSplit, previewTransferSplit, resolveEnv, serializePaymentRequirement, settlementTypedData, toX402PaymentRequired, ulid, validateX402ChallengeInput };
package/dist/server.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { a2 as PlaymosError, W as WebhookEvent } from './errors-CYKgt2VU.cjs';
2
- export { ad as enterPathToBytes32 } from './errors-CYKgt2VU.cjs';
1
+ import { a2 as PlaymosError, W as WebhookEvent } from './errors-WcDNfb4q.cjs';
2
+ export { ad as enterPathToBytes32 } from './errors-WcDNfb4q.cjs';
3
3
 
4
4
  /**
5
5
  * Webhook signature verification (spec §6.2) — server-side only.
package/dist/server.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { a2 as PlaymosError, W as WebhookEvent } from './errors-CYKgt2VU.js';
2
- export { ad as enterPathToBytes32 } from './errors-CYKgt2VU.js';
1
+ import { a2 as PlaymosError, W as WebhookEvent } from './errors-WcDNfb4q.js';
2
+ export { ad as enterPathToBytes32 } from './errors-WcDNfb4q.js';
3
3
 
4
4
  /**
5
5
  * Webhook signature verification (spec §6.2) — server-side only.
package/dist/server.js CHANGED
@@ -1,5 +1,5 @@
1
- import { PlaymosError, ApiError, toBytes32 } from './chunk-TZFGZNXV.js';
2
- export { toBytes32 as enterPathToBytes32 } from './chunk-TZFGZNXV.js';
1
+ import { PlaymosError, ApiError, toBytes32 } from './chunk-ROWX6HVU.js';
2
+ export { toBytes32 as enterPathToBytes32 } from './chunk-ROWX6HVU.js';
3
3
  import { timingSafeEqual, createHmac } from 'crypto';
4
4
  import { encodeFunctionData, decodeFunctionResult } from 'viem';
5
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playmos/sdk",
3
- "version": "0.3.13",
3
+ "version": "0.3.15",
4
4
  "description": "Playmos SDK — stablecoin payments for games on Base. IAP and skill contests are flat 1%. USD in, USDC on-chain, no crypto UX for players.",
5
5
  "license": "MIT",
6
6
  "private": false,