@playmos/sdk 0.1.5 → 0.2.0

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.d.ts CHANGED
@@ -1,178 +1,5 @@
1
- /**
2
- * Public types pruned to WIRED capability only (fixes #344/I and #335/E).
3
- *
4
- * The old vendored surface advertised four wallet connectors that silently
5
- * returned {connected:false} and config fields the code never read. Here the
6
- * types expose exactly what V1 does: two connectors, the fields `pay()` /
7
- * `enterRound()` actually use, and no more.
8
- */
9
- type Network = "base" | "base-sepolia";
10
- /** Only the connectors that are actually implemented. */
11
- type WalletConnector = "base-account" | "injected";
12
- type GasMode = "sponsored" | "player";
13
- type PaymentStatus = "created" | "pending" | "confirmed" | "failed";
14
- /** Minimal EIP-1193 provider shape (what the wallet must expose). */
15
- interface Eip1193Provider {
16
- request(args: {
17
- method: string;
18
- params?: unknown[] | object;
19
- }): Promise<unknown>;
20
- }
21
- interface GasConfig {
22
- mode?: GasMode;
23
- /** CDP paymaster URL — required only for `mode: "sponsored"`. */
24
- paymasterUrl?: string;
25
- }
26
- interface WalletConfig {
27
- connector?: WalletConnector;
28
- /**
29
- * Pre-built EIP-1193 provider. If omitted, `injected` uses globalThis.ethereum
30
- * and `base-account` expects a provider supplied by the Base Account SDK host.
31
- */
32
- provider?: Eip1193Provider;
33
- }
34
- /** Per-game contract addresses. On testnet these come from the service intent,
35
- * but a studio may pin them explicitly (matches the dogfood adapter). */
36
- interface ContractConfig {
37
- usdc?: `0x${string}`;
38
- playmosPay?: `0x${string}`;
39
- prizePool?: `0x${string}`;
40
- }
41
- interface AgentEconomyConfig {
42
- enabled?: boolean;
43
- /** Tax skimmed to Playmos on each agent↔agent transfer. 100 = 1% (default). */
44
- taxBps?: number;
45
- }
46
- interface PlaymosConfig {
47
- /** pk_test_* (sandbox / Base Sepolia) or pk_live_* (production / Base mainnet).
48
- * The key prefix selects the environment. */
49
- apiKey: string;
50
- /** Override the network derived from the key. Explicit value wins. */
51
- network?: Network;
52
- wallet?: WalletConfig;
53
- gas?: GasConfig;
54
- agentEconomy?: AgentEconomyConfig;
55
- contracts?: ContractConfig;
56
- /** Escape hatch; defaults to the right env's base URL. */
57
- apiBaseUrl?: string;
58
- /**
59
- * Explicit, clearly-labeled offline unit-test helper — instant, deterministic,
60
- * NO network, NO chain. Never the default, never conflated with sandbox. Note:
61
- * even mock results use the REAL status union (never a synthetic "mocked").
62
- */
63
- mock?: boolean;
64
- }
65
- interface PayInput {
66
- /** USD decimal string ("4.99"). Rejected: ≤ 0, non-numeric, > 2 dp. */
67
- amount: string;
68
- /** Your product id, echoed on the receipt + webhook. */
69
- sku: string;
70
- /** Your opaque user id. */
71
- playerId: string;
72
- /**
73
- * The game this IAP belongs to. Optional: when your API key maps to exactly
74
- * one game the service resolves it for you (the quickstart). Supply it
75
- * explicitly when your key spans multiple games.
76
- */
77
- gameId?: string;
78
- /** The studio wallet that receives the 99%. Falls back to the service default. */
79
- studio?: `0x${string}`;
80
- /** Supply your own to make retries safe; omit and the SDK generates a ULID. */
81
- idempotencyKey?: string;
82
- metadata?: Record<string, string>;
83
- }
84
- interface EnterRoundInput {
85
- /** The game — selects its prize-pool contract. */
86
- gameId: string;
87
- /** The round being entered. */
88
- roundId: string;
89
- /** USD entry — grows this round's pool. */
90
- amount: string;
91
- playerId: string;
92
- idempotencyKey?: string;
93
- metadata?: Record<string, string>;
94
- /**
95
- * Advanced (games that manage their own on-chain rounds, e.g. the Playmos
96
- * game-hub kit): the EXACT on-chain round key to enter — overrides the default
97
- * `${gameId}:${roundId}` derivation. Set it to the value your game's server
98
- * verifies `hasEntered` against (e.g. "bjtest:T1").
99
- */
100
- roundKey?: string;
101
- /**
102
- * Advanced: the EXACT on-chain identity for this paid attempt (e.g.
103
- * "0xWallet#nonce"). Overrides the server-generated identity so the on-chain
104
- * entry and your server's `hasEntered` check line up. One entry per identity.
105
- */
106
- identity?: string;
107
- }
108
- interface Payment {
109
- /** `pay_…` (IAP) or `entry_…` (prize-pool) — ULID, server-issued, unique. */
110
- id: string;
111
- status: PaymentStatus;
112
- /** "iap" (1% external) or "entry" (60/30/10 prize-pool). */
113
- kind: "iap" | "entry";
114
- amount: string;
115
- fee: string;
116
- net: string;
117
- /** Present for prize-pool entries: the 60/30/10 breakdown in USD. */
118
- split?: {
119
- pool: string;
120
- seed: string;
121
- rake: string;
122
- };
123
- sku?: string;
124
- roundId?: string;
125
- gameId?: string;
126
- playerId: string;
127
- txHash?: `0x${string}`;
128
- chain: Network;
129
- createdAt: string;
130
- metadata?: Record<string, string>;
131
- /**
132
- * The on-chain identity used for a prize-pool entry (echo of
133
- * `EnterRoundInput.identity` or the SDK-derived value) — reconcile against your
134
- * server's `hasEntered`.
135
- */
136
- identity?: string;
137
- /** True only when produced by the labeled `mock: true` helper. */
138
- mock?: boolean;
139
- }
140
- /** What `verify()` resolves to (server reads the chain). */
141
- interface VerifyResult {
142
- id: string;
143
- status: PaymentStatus;
144
- amount: string;
145
- fee: string;
146
- net: string;
147
- txHash?: `0x${string}`;
148
- playerId: string;
149
- sku?: string;
150
- roundId?: string;
151
- chain: Network;
152
- /** How the service derived this status: an on-chain read, the honest cache, or
153
- * degraded (chain reads not configured). Lets the SDK stop polling when the
154
- * service can never confirm on-chain. */
155
- verifiedVia?: "chain" | "cache" | "degraded";
156
- chainReads?: "enabled" | "degraded";
157
- }
158
- type WebhookEventType = "payment.confirmed" | "payment.failed" | "payout.settled" | "refund.processed";
159
- interface WebhookEvent {
160
- id: string;
161
- type: WebhookEventType;
162
- createdAt: string;
163
- data: {
164
- id: string;
165
- status: PaymentStatus;
166
- amount: string;
167
- fee: string;
168
- net: string;
169
- playerId: string;
170
- sku?: string;
171
- txHash?: `0x${string}`;
172
- chain: Network;
173
- metadata?: Record<string, string>;
174
- };
175
- }
1
+ import { N as Network, P as PlaymosConfig, W as WebhookEvent, R as RoundOpenInput, a as RoundState, b as RoundSettleInput, S as SettleRoundResult, A as AgentWallet, c as AgentFundResult, T as TransferResult, E as EscrowHoldInput, d as EscrowHoldResult, e as EscrowResolveResult, M as MarketplaceListInput, L as Listing, f as MarketplaceSaleResult, g as MarketplaceGetResult, h as PayInput, i as Payment, j as EnterRoundInput, k as TransferInput, V as VerifyResult } from './errors-CjL85YKR.js';
2
+ export { l as AgentEconomyConfig, m as ApiError, n as AuthError, C as ConfigError, o as ContractConfig, p as Eip1193Provider, G as GasConfig, q as GasMode, I as InsufficientGasError, r as InvalidAmountError, s as ListingStatus, t as MarketplaceItem, u as MarketplaceSale, v as MissingFieldError, w as PaymentFailedError, x as PaymentStatus, y as PayoutRule, z as PlaymosError, B as PlaymosErrorCode, D as RoundStatus, F as WalletConfig, H as WalletConnectionError, J as WalletConnector, K as WebhookEventType } from './errors-CjL85YKR.js';
176
3
 
177
4
  /**
178
5
  * Environment resolution + the canonical address book.
@@ -209,9 +36,20 @@ declare class Playmos {
209
36
  readonly config: PlaymosConfig;
210
37
  readonly env: ResolvedEnv;
211
38
  private readonly http;
39
+ /**
40
+ * Webhook helpers. Signature verification is **server-only** (Node `crypto`) and
41
+ * lives on `@playmos/sdk/server` so browser bundlers never see `node:crypto` (#9).
42
+ *
43
+ * import { verifyWebhook } from "@playmos/sdk/server";
44
+ * const event = verifyWebhook(rawBody, req.headers["x-playmos-signature"], secret);
45
+ */
212
46
  readonly webhooks: {
213
- /** Verify a webhook signature and return the parsed event (server-side). */
214
- verify: (rawBody: string | Buffer, signatureHeader: string | string[] | undefined, secret: string) => WebhookEvent;
47
+ /**
48
+ * @deprecated Use `import { verifyWebhook } from "@playmos/sdk/server"` instead.
49
+ * Throws if called — kept as a discoverable pointer so call sites fail loudly
50
+ * with a fix instruction rather than a silent missing method.
51
+ */
52
+ verify: (_rawBody: string | Buffer, _signatureHeader: string | string[] | undefined, _secret: string) => WebhookEvent;
215
53
  };
216
54
  readonly payouts: {
217
55
  /** Choose how the studio is paid: "usdc" (default) or "fiat" (Bridge). */
@@ -223,25 +61,104 @@ declare class Playmos {
223
61
  url: string;
224
62
  }>;
225
63
  };
64
+ /**
65
+ * Skill/contest **round lifecycle** (issue #13) — closes the enterRound loop.
66
+ *
67
+ * Scores never leave the studio. Flow:
68
+ * rounds.open → players enterRound → studio scores → rounds.lock →
69
+ * rounds.settle({ ranking }) → contract pays winners.
70
+ *
71
+ * Operator txs are signed by the Playmos service (OPERATOR_ROLE key).
72
+ */
73
+ readonly rounds: {
74
+ open: (input: RoundOpenInput) => Promise<RoundState>;
75
+ lock: (input: {
76
+ roundId: string;
77
+ gameId?: string;
78
+ }) => Promise<RoundState>;
79
+ settle: (input: RoundSettleInput) => Promise<SettleRoundResult>;
80
+ get: (input: {
81
+ roundId: string;
82
+ }) => Promise<RoundState>;
83
+ };
84
+ /**
85
+ * `agents` — assign wallets to the NPCs YOUR game already owns, so they can transact USDC in your economy.
86
+ * The game creates the NPCs; the SDK only creates the WALLET for a game-supplied id. Engine-agnostic
87
+ * (Unity/Unreal/Godot/web all call the same REST). Requires a secret test key (`sk_test_`) on the sandbox.
88
+ */
226
89
  readonly agents: {
227
- /** Assign a wallet to any identity (incl. an AI NPC). Idempotent by agentId. */
90
+ /** Assign (or return) the wallet for a game NPC id. Idempotent safe to call wherever your NPCs spawn. */
228
91
  createWallet: (input: {
229
92
  agentId: string;
230
- }) => Promise<{
93
+ }) => Promise<AgentWallet>;
94
+ /** Resolve one NPC's wallet by your id. */
95
+ wallet: (agentId: string) => Promise<AgentWallet>;
96
+ /** List the NPC wallets you've assigned in this studio. */
97
+ list: () => Promise<AgentWallet[]>;
98
+ /** Sandbox faucet: fund an NPC with USDC from your treasury (per-NPC lifetime cap). */
99
+ fund: (input: {
231
100
  agentId: string;
232
- address: `0x${string}`;
233
- chain: string;
234
- }>;
235
- /** Agent↔agent USDC transfer; the configured taxBps is skimmed to Playmos. */
101
+ amount: string;
102
+ }) => Promise<AgentFundResult>;
103
+ /** NPC→NPC (or →player) USDC transfer, by id. A thin alias of `playmos.transfer` (which is canonical). */
236
104
  pay: (input: {
237
105
  from: string;
238
106
  to: string;
239
107
  amount: string;
240
- }) => Promise<{
241
- id: string;
242
- taxUSD: string;
243
- status: string;
244
- }>;
108
+ feeBps?: number;
109
+ feeSink?: `0x${string}`;
110
+ }) => Promise<TransferResult>;
111
+ };
112
+ /**
113
+ * `escrow` — the fair-exchange primitive (Phase 2): lock the payer's USDC on-chain the instant a deal
114
+ * opens (`hold`), then move it exactly once — `release` (→ payee, fee skimmed) XOR `refund` (→ payer,
115
+ * 100%). The contract holds the funds trustlessly; YOUR game decides the rule (who resolves, and when).
116
+ * A `deadline` auto-refund guarantees funds never get stuck. Pass the `hold` result's `id` to the rest.
117
+ */
118
+ readonly escrow: {
119
+ /** Open a deal: lock `amount` of the payer's USDC into the on-chain escrow. Retries are idempotent.
120
+ * `async` so client-side validation surfaces as a rejected promise, not a synchronous throw. */
121
+ hold: (input: EscrowHoldInput) => Promise<EscrowHoldResult>;
122
+ /** Release a held deal to the payee (fee skimmed). `escrowId` is the `hold` result's `id`. */
123
+ release: (input: {
124
+ escrowId: string;
125
+ }) => Promise<EscrowResolveResult>;
126
+ /** Refund a held deal to the payer (100%, untaxed). `escrowId` is the `hold` result's `id`. */
127
+ refund: (input: {
128
+ escrowId: string;
129
+ }) => Promise<EscrowResolveResult>;
130
+ /** Verify a deal's on-chain state (reconciles a `settling` hold wedged by a crash). */
131
+ get: (escrowId: string) => Promise<EscrowResolveResult>;
132
+ };
133
+ /**
134
+ * `marketplace` — list an item, and the seller is paid ONLY when it's bought (Phase 3a, off-chain items).
135
+ * Built on `escrow`: `buy` locks the buyer's USDC on-chain, `confirm` (after your game server delivers the
136
+ * item) pays the seller, and a no-delivery/timeout `refund`s the buyer. `deliver: true` on `buy` collapses
137
+ * lock+pay into one call when your server delivers synchronously. On-chain items are Phase 3b.
138
+ */
139
+ readonly marketplace: {
140
+ /** List an off-chain item for sale. No money moves. Idempotent on `idempotencyKey`. */
141
+ list: (input: MarketplaceListInput) => Promise<Listing>;
142
+ /** Buy a listing: lock the buyer's USDC in escrow. Pass `deliver: true` to also pay the seller in one call. */
143
+ buy: (input: {
144
+ listingId: string;
145
+ buyer?: `0x${string}`;
146
+ deliver?: boolean;
147
+ }) => Promise<MarketplaceSaleResult>;
148
+ /** Confirm delivery → the seller is paid (release), fee skimmed. */
149
+ confirm: (input: {
150
+ listingId: string;
151
+ }) => Promise<MarketplaceSaleResult>;
152
+ /** Refund the buyer 100% (seller couldn't deliver / dispute / timeout). */
153
+ refund: (input: {
154
+ listingId: string;
155
+ }) => Promise<MarketplaceSaleResult>;
156
+ /** Delist an unsold listing (pure DB, no tx). */
157
+ cancel: (input: {
158
+ listingId: string;
159
+ }) => Promise<Listing>;
160
+ /** Fetch a listing + its on-chain-verified sale status. */
161
+ get: (listingId: string) => Promise<MarketplaceGetResult>;
245
162
  };
246
163
  constructor(config: PlaymosConfig);
247
164
  /** Connect the player's wallet and return their address. */
@@ -276,6 +193,37 @@ declare class Playmos {
276
193
  pay(input: PayInput): Promise<Payment>;
277
194
  /** Playmos-owned skill-game entry (10%, 60/30/10 prize pool). Closes #343. */
278
195
  enterRound(input: EnterRoundInput): Promise<Payment>;
196
+ /**
197
+ * `transfers` — read-back / confirmation for a prior `transfer()` (issue #27).
198
+ * Poll when POST returned `status: "settling"`; reconciles against chain (incl. gasless agent path).
199
+ */
200
+ readonly transfers: {
201
+ get: (transferId: string) => Promise<{
202
+ id: string;
203
+ status: string;
204
+ txHash: string | null;
205
+ to: string;
206
+ amount: string;
207
+ verifiedVia: "chain" | "cache" | "degraded";
208
+ }>;
209
+ };
210
+ /**
211
+ * `transfer` — the value-movement base primitive (Phase 1a): move USDC from one wallet to another,
212
+ * with a per-call fee. The GAME LOGIC is the authority — you already decided the move is valid — so
213
+ * this is a direct, unconditional push (use `escrow`/`marketplace` when a trust boundary needs fair
214
+ * exchange). The service settles it through the protocol-agnostic settlement core (idempotent,
215
+ * reserve-before-broadcast) and the on-chain PlaymosTransfer / PlaymosTransferAuth contracts.
216
+ *
217
+ * Fee is per-call: `feeBps` 0–10000 (+ `feeSink`). `feeBps: 0` is an untaxed reward/faucet transfer.
218
+ * Retries are safe: pass the same `idempotencyKey` and a re-call NEVER broadcasts a second tx —
219
+ * it returns the cached result (`idempotentReplay: true`).
220
+ *
221
+ * Confirmation: treat `status === "settled" && txHash` as final. If `settling`, call
222
+ * `playmos.transfers.get(id)` until settled/failed. BaseScan: `https://sepolia.basescan.org/tx/<txHash>`.
223
+ *
224
+ * NPC `from` requires `sk_test_` and settles gaslessly (NPC signs; service relays).
225
+ */
226
+ transfer(input: TransferInput): Promise<TransferResult>;
279
227
  /** Verify a payment by the service's on-chain read (spec §6.1). Idempotent. */
280
228
  verify(paymentId: string): Promise<VerifyResult>;
281
229
  /**
@@ -302,6 +250,37 @@ declare function previewIapSplit(amount: string): {
302
250
  fee: string;
303
251
  net: string;
304
252
  };
253
+ /**
254
+ * Local preview of a `transfer` fee split without any network — handy for UIs and quotes. Mirrors the
255
+ * on-chain floor math: `fee = floor(amount * feeBps / 10000)`, `net = amount − fee` (dust → payee).
256
+ */
257
+ declare function previewTransferSplit(amount: string, feeBps: number): {
258
+ amount: string;
259
+ fee: string;
260
+ net: string;
261
+ feeBps: number;
262
+ };
263
+ /**
264
+ * Local preview of what an escrow `release` would pay out, without any network. Mirrors the on-chain
265
+ * floor math: `fee = floor(amount * feeBps / 10000)`, `net = amount − fee` (dust → payee). A `refund`
266
+ * returns the full `amount` with no fee, so it needs no preview.
267
+ */
268
+ declare function previewEscrowFee(amount: string, feeBps: number): {
269
+ amount: string;
270
+ fee: string;
271
+ net: string;
272
+ feeBps: number;
273
+ };
274
+ /**
275
+ * Local preview of a marketplace sale's payout, without any network. Mirrors the on-chain floor math
276
+ * (`fee = floor(price * feeBps / 10000)`, `net = price − fee`, dust → seller). A no-sale/refund pays 0.
277
+ */
278
+ declare function previewMarketplaceSplit(price: string, feeBps: number): {
279
+ price: string;
280
+ fee: string;
281
+ net: string;
282
+ feeBps: number;
283
+ };
305
284
  /** Local preview of the 60/30/10 entry split without any network. */
306
285
  declare function previewPoolSplit(amount: string): {
307
286
  amount: string;
@@ -311,74 +290,38 @@ declare function previewPoolSplit(amount: string): {
311
290
  };
312
291
 
313
292
  /**
314
- * Typed, actionable errors the Stripe bar (spec §11).
315
- *
316
- * Every error carries a stable machine-readable `code` and is thrown at the
317
- * EARLIEST possible layer: input errors fire client-side before any network or
318
- * chain call, so a studio never pays gas to discover a typo.
319
- */
320
- type PlaymosErrorCode = "invalid_amount" | "missing_field" | "insufficient_gas" | "wallet_connection" | "payment_failed" | "auth" | "api_error" | "config";
321
- declare class PlaymosError extends Error {
322
- readonly code: PlaymosErrorCode;
323
- /** Optional machine context (e.g. the offending field, the http status). */
324
- readonly detail?: Record<string, unknown>;
325
- constructor(code: PlaymosErrorCode, message: string, detail?: Record<string, unknown>);
326
- }
327
- /** amount ≤ 0, non-numeric, empty, or more than 2 decimal places. */
328
- declare class InvalidAmountError extends PlaymosError {
329
- constructor(amount: unknown);
330
- }
331
- /** A required field (sku, playerId, gameId, roundId, agentId…) was empty. */
332
- declare class MissingFieldError extends PlaymosError {
333
- constructor(field: string);
334
- }
335
- /** gas mode "player" and the player's ETH is too low to cover gas (pre-check, §7). */
336
- declare class InsufficientGasError extends PlaymosError {
337
- constructor(detail?: Record<string, unknown>);
338
- }
339
- /** The player closed or failed the wallet sheet, or no provider is available. */
340
- declare class WalletConnectionError extends PlaymosError {
341
- constructor(message?: string, detail?: Record<string, unknown>);
342
- }
343
- /** The on-chain settlement reverted, was cancelled, or timed out. */
344
- declare class PaymentFailedError extends PlaymosError {
345
- constructor(message?: string, detail?: Record<string, unknown>);
346
- }
347
- /** Bad, missing, or wrong-environment API key (e.g. a pk_test_ key on a live route). */
348
- declare class AuthError extends PlaymosError {
349
- constructor(message?: string, detail?: Record<string, unknown>);
350
- }
351
- /** The Playmos service returned a non-2xx we don't have a more specific error for. */
352
- declare class ApiError extends PlaymosError {
353
- constructor(message: string, detail?: Record<string, unknown>);
354
- }
355
- /** SDK misconfiguration (e.g. a missing contract address for on-chain mode). */
356
- declare class ConfigError extends PlaymosError {
357
- constructor(message: string, detail?: Record<string, unknown>);
358
- }
359
-
360
- /**
361
- * Webhook signature verification (spec §6.2) — server-side only.
293
+ * payout.ts pure payout math for skill/contest settlement (issue #13).
362
294
  *
363
- * Signature scheme (mirrors the Playmos service byte-for-byte):
364
- * header `X-Playmos-Signature: t=<unixSeconds>,v1=<hex>`
365
- * where <hex> = HMAC_SHA256(secret, `${t}.${rawBody}`)
295
+ * Scores NEVER enter this module. The studio ranks wallets (best-first) from its
296
+ * own leaderboard; we only turn that ranking + a payout rule into integer USDC
297
+ * base-unit amounts that sum EXACTLY to the payable pool.
366
298
  *
367
- * Verify BEFORE trusting an event. Uses node:crypto (constant-time compare) and
368
- * rejects stale timestamps to blunt replay. Throws on any mismatch.
299
+ * No network, no chain fully unit-testable.
369
300
  */
370
-
371
- declare class WebhookSignatureError extends PlaymosError {
301
+ type PayoutRule = {
302
+ kind: "winner-take-all";
303
+ } | {
304
+ kind: "top-n";
305
+ splitsBps: number[];
306
+ } | {
307
+ kind: "custom";
308
+ amounts: string[];
309
+ };
310
+ declare class PayoutError extends Error {
311
+ code: "payout_invalid";
372
312
  constructor(message: string);
373
313
  }
374
314
  /**
375
- * Verify a webhook and return the parsed event. `rawBody` MUST be the exact raw
376
- * request bytes (use express.raw / fastify rawBody) — a re-serialized JSON body
377
- * will not match the signature.
315
+ * Apply the studio's payout rule to a payable pool and ranking (best-first).
316
+ *
317
+ * - Integer math only (bigint micro-USDC).
318
+ * - Sum of amounts == pool exactly; any remainder from floor division goes to rank 1.
319
+ * - Rejects empty ranking, bad splits, more winners than ranking, zero pool.
378
320
  */
379
- declare function verifyWebhook(rawBody: string | Buffer, signatureHeader: string | string[] | undefined, secret: string, opts?: {
380
- toleranceSeconds?: number;
381
- }): WebhookEvent;
321
+ declare function computePayout(pool: bigint, ranking: `0x${string}`[], rule: PayoutRule): {
322
+ wallet: `0x${string}`;
323
+ amount: bigint;
324
+ }[];
382
325
 
383
326
  /**
384
327
  * Money math — exact, integer-only, in USDC micro-units (6 decimals).
@@ -435,4 +378,132 @@ declare function ulid(seedTime?: number): string;
435
378
  /** `${prefix}_${ulid()}` — e.g. `pay_01J…`, `entry_01J…`, `idem_01J…`. */
436
379
  declare function prefixedId(prefix: string): string;
437
380
 
438
- export { type AgentEconomyConfig, ApiError, AuthError, CHAIN_ID, ConfigError, type ContractConfig, DEFAULT_API_BASE_URL, type Eip1193Provider, type EnterRoundInput, type GasConfig, type GasMode, InsufficientGasError, InvalidAmountError, MICRO_PER_USDC, MissingFieldError, type Network, type PayInput, type Payment, PaymentFailedError, type PaymentStatus, Playmos, type PlaymosConfig, PlaymosError, type PlaymosErrorCode, USDC_ADDRESS, USDC_DECIMALS, type VerifyResult, type WalletConfig, WalletConnectionError, type WalletConnector, type WebhookEvent, type WebhookEventType, WebhookSignatureError, computeIapSplit, computePoolSplit, formatMicroToUsd, parseUsdToMicro, prefixedId, previewIapSplit, previewPoolSplit, ulid, verifyWebhook };
381
+ /**
382
+ * settlement.ts — the x402-ready settlement CORE contract (Phase 0).
383
+ *
384
+ * Two shared shapes every value-movement primitive (`transfer`, `escrow`,
385
+ * `marketplace`) AND the future x402 adapter build on. The whole point of
386
+ * naming them now is that x402 later becomes a thin HTTP-402 adapter with zero
387
+ * rework — see `docs/design/transfer-escrow-marketplace-spec.md` §"Architecture:
388
+ * x402-ready by design".
389
+ *
390
+ * PaymentRequirement — WHAT must be paid. A plain, JSON-safe object. A direct
391
+ * SDK call creates one and satisfies it immediately; an x402 "402 Payment
392
+ * Required" challenge is literally this object serialized onto the wire. So
393
+ * it is designed to round-trip to/from an HTTP 402 body with zero loss —
394
+ * `parsePaymentRequirement(serializePaymentRequirement(req))` is `req`.
395
+ *
396
+ * Authorization — HOW a settlement is authorized. A discriminated union so the
397
+ * settlement core never bakes in "direct SDK signature": today a
398
+ * `wallet-signature` (implemented), tomorrow an `x402-payload` (declared,
399
+ * not settled yet). This union is the seam that keeps x402 out of the core.
400
+ *
401
+ * Nothing here touches the network or the chain — these are the shared data
402
+ * shapes the SDK produces and the service's protocol-agnostic settlement core
403
+ * consumes.
404
+ */
405
+
406
+ /** The only settlement asset in V1. Named so an x402 challenge carries it verbatim. */
407
+ type SettlementAsset = "USDC";
408
+ /**
409
+ * The shared payment contract — WHAT must be paid, independent of HOW it was
410
+ * requested (direct SDK call today, x402 challenge tomorrow).
411
+ *
412
+ * Every field is a JSON primitive so the object is byte-stable across an HTTP
413
+ * 402 body: mint it once, and both the direct path and the x402 path settle the
414
+ * exact same requirement, idempotent on `id`.
415
+ */
416
+ interface PaymentRequirement {
417
+ /** The anchor id (`preq_<ulid>`). Settlement idempotency key + x402 challenge id. */
418
+ id: string;
419
+ /** The recipient wallet (the payee). Checksummed or lowercase 0x-address. */
420
+ payTo: `0x${string}`;
421
+ /** USD decimal string ("5.00") — same money convention as `pay()`/`enterRound()`. */
422
+ amount: string;
423
+ /** Only USDC in V1. */
424
+ asset: SettlementAsset;
425
+ /** Which chain settles this requirement. */
426
+ network: Network;
427
+ /** Optional free-form terms/memo, echoed on the receipt and in the 402 body. */
428
+ terms?: string;
429
+ /** ISO-8601 expiry. A challenge/authorization presented after this is rejected. */
430
+ expiresAt: string;
431
+ }
432
+ interface CreatePaymentRequirementInput {
433
+ /** Recipient wallet. */
434
+ payTo: `0x${string}`;
435
+ /** USD decimal string ("5.00"). */
436
+ amount: string;
437
+ /** Settlement chain. */
438
+ network: Network;
439
+ /** Defaults to "USDC". */
440
+ asset?: SettlementAsset;
441
+ /** Optional terms/memo. */
442
+ terms?: string;
443
+ /** Absolute ISO expiry. Overrides `expiresInMs`. Defaults to now + 15 min. */
444
+ expiresAt?: string;
445
+ /** Relative TTL from now, in ms. Ignored if `expiresAt` is set. */
446
+ expiresInMs?: number;
447
+ /** Supply for a deterministic id (e.g. to reuse an upstream id); else a `preq_<ulid>` is minted. */
448
+ id?: string;
449
+ /** Injectable clock for deterministic tests. */
450
+ now?: () => Date;
451
+ }
452
+ /**
453
+ * Build a validated {@link PaymentRequirement}. This is the single producer the
454
+ * direct primitives (`transfer`/`escrow`/`marketplace`) and — later — the x402
455
+ * adapter both call, so every requirement on the wire is shaped identically.
456
+ * Pure: no network, no chain, no id collisions (monotonic ULID).
457
+ */
458
+ declare function createPaymentRequirement(input: CreatePaymentRequirementInput): PaymentRequirement;
459
+ /**
460
+ * Serialize a requirement to a plain, JSON-safe object — the exact body an x402
461
+ * "402 Payment Required" response carries. Undefined optionals are omitted so
462
+ * the shape is stable across `JSON.stringify` → `JSON.parse` → re-parse.
463
+ */
464
+ declare function serializePaymentRequirement(req: PaymentRequirement): Record<string, unknown>;
465
+ /**
466
+ * Parse + validate an untrusted object (an HTTP 402 body, a queue message, a
467
+ * direct call) back into a {@link PaymentRequirement}. This is the exact decoder
468
+ * the x402 adapter reuses — it must reject anything malformed with a typed error.
469
+ */
470
+ declare function parsePaymentRequirement(input: unknown): PaymentRequirement;
471
+ /**
472
+ * A settlement authorized by a wallet — the ONLY variant implemented in Phase 0.
473
+ * Covers both signer models the primitives support:
474
+ * - server-held NPC/agent wallets (the service signs) and
475
+ * - player Base Accounts (the client signs) —
476
+ * carrying either the settlement `txHash` the client already broadcast, or a
477
+ * `signature` + `payload` (e.g. EIP-3009 / EIP-5792 batch params) the service
478
+ * submits. Phase 0 only READS `txHash`; the signed-submit path lands in Phase 1.
479
+ */
480
+ interface WalletSignatureAuthorization {
481
+ kind: "wallet-signature";
482
+ /** The wallet that authorized the move (payer / signer). */
483
+ from: `0x${string}`;
484
+ /** A settlement tx the client already broadcast (client-signed path). */
485
+ txHash?: `0x${string}`;
486
+ /** An off-chain signature the service submits on the payer's behalf. */
487
+ signature?: `0x${string}`;
488
+ /** Opaque protocol-specific authorization data (EIP-5792 calls, EIP-3009, …). */
489
+ payload?: Record<string, unknown>;
490
+ }
491
+ /**
492
+ * A settlement authorized by an x402 payment payload (the `X-PAYMENT` header).
493
+ * DECLARED for the Phase 4 HTTP-402 adapter so the core's type surface is final
494
+ * now — but NOT settled in Phase 0: `settle()` throws a clear "not implemented
495
+ * yet" for this variant. This is the seam that keeps x402 out of the core.
496
+ */
497
+ interface X402PayloadAuthorization {
498
+ kind: "x402-payload";
499
+ /** The raw, opaque x402 payment payload decoded from the request. */
500
+ payload: Record<string, unknown>;
501
+ }
502
+ /** How a settlement is authorized — protocol-agnostic by construction. */
503
+ type Authorization = WalletSignatureAuthorization | X402PayloadAuthorization;
504
+ /** Narrow to the implemented wallet-signature variant. */
505
+ declare function isWalletSignatureAuthorization(auth: Authorization): auth is WalletSignatureAuthorization;
506
+ /** Narrow to the declared-but-unimplemented x402 variant. */
507
+ declare function isX402PayloadAuthorization(auth: Authorization): auth is X402PayloadAuthorization;
508
+
509
+ export { AgentFundResult, AgentWallet, type Authorization, CHAIN_ID, type CreatePaymentRequirementInput, DEFAULT_API_BASE_URL, EnterRoundInput, EscrowHoldInput, EscrowHoldResult, EscrowResolveResult, Listing, MICRO_PER_USDC, MarketplaceGetResult, MarketplaceListInput, MarketplaceSaleResult, Network, PayInput, Payment, type PaymentRequirement, PayoutError, type PayoutRule as PayoutRuleCompute, Playmos, PlaymosConfig, RoundOpenInput, RoundSettleInput, RoundState, SettleRoundResult, type SettlementAsset, TransferInput, TransferResult, USDC_ADDRESS, USDC_DECIMALS, VerifyResult, type WalletSignatureAuthorization, WebhookEvent, type X402PayloadAuthorization, computeIapSplit, computePayout, computePoolSplit, createPaymentRequirement, formatMicroToUsd, isWalletSignatureAuthorization, isX402PayloadAuthorization, parsePaymentRequirement, parseUsdToMicro, prefixedId, previewEscrowFee, previewIapSplit, previewMarketplaceSplit, previewPoolSplit, previewTransferSplit, serializePaymentRequirement, ulid };