@playmos/sdk 0.3.0 → 0.3.1
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/{chunk-B7SHFZYY.js → chunk-TMBBEGIF.js} +12 -3
- package/dist/chunk-TMBBEGIF.js.map +1 -0
- package/dist/{errors-BVESr920.d.cts → errors-DMtMpbR6.d.cts} +48 -4
- package/dist/{errors-BVESr920.d.ts → errors-DMtMpbR6.d.ts} +48 -4
- package/dist/index.cjs +193 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -3
- package/dist/index.d.ts +32 -3
- package/dist/index.js +184 -3
- package/dist/index.js.map +1 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-B7SHFZYY.js.map +0 -1
|
@@ -56,7 +56,16 @@ var ConfigError = class extends PlaymosError {
|
|
|
56
56
|
super("config", message, detail);
|
|
57
57
|
}
|
|
58
58
|
};
|
|
59
|
+
var NothingToWithdrawError = class extends PlaymosError {
|
|
60
|
+
constructor(detail) {
|
|
61
|
+
super(
|
|
62
|
+
"nothing_to_withdraw",
|
|
63
|
+
"Nothing to withdraw \u2014 this wallet has no credited prize balance on this PrizePool (round not settled for them, or already claimed).",
|
|
64
|
+
detail
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
59
68
|
|
|
60
|
-
export { ApiError, AuthError, ConfigError, InsufficientGasError, InvalidAmountError, MissingFieldError, PaymentFailedError, PlaymosError, WalletConnectionError };
|
|
61
|
-
//# sourceMappingURL=chunk-
|
|
62
|
-
//# sourceMappingURL=chunk-
|
|
69
|
+
export { ApiError, AuthError, ConfigError, InsufficientGasError, InvalidAmountError, MissingFieldError, NothingToWithdrawError, PaymentFailedError, PlaymosError, WalletConnectionError };
|
|
70
|
+
//# sourceMappingURL=chunk-TMBBEGIF.js.map
|
|
71
|
+
//# sourceMappingURL=chunk-TMBBEGIF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts"],"names":[],"mappings":";AAmBO,IAAM,YAAA,GAAN,cAA2B,KAAA,CAAM;AAAA,EAKtC,WAAA,CAAY,IAAA,EAAwB,OAAA,EAAiB,MAAA,EAAkC;AACrF,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,OAAO,GAAA,CAAA,MAAA,CAAW,IAAA;AACvB,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAEd,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,GAAA,CAAA,MAAA,CAAW,SAAS,CAAA;AAAA,EAClD;AACF;AAGO,IAAM,kBAAA,GAAN,cAAiC,YAAA,CAAa;AAAA,EACnD,YAAY,MAAA,EAAiB;AAC3B,IAAA,KAAA;AAAA,MACE,gBAAA;AAAA,MACA,CAAA,gBAAA,EAAmB,IAAA,CAAK,SAAA,CAAU,MAAM,CAAC,CAAA,6EAAA,CAAA;AAAA,MACzC,EAAE,MAAA;AAAO,KACX;AAAA,EACF;AACF;AAGO,IAAM,iBAAA,GAAN,cAAgC,YAAA,CAAa;AAAA,EAClD,YAAY,KAAA,EAAe;AACzB,IAAA,KAAA,CAAM,iBAAiB,CAAA,yBAAA,EAA4B,KAAK,CAAA,EAAA,CAAA,EAAM,EAAE,OAAO,CAAA;AAAA,EACzE;AACF;AAGO,IAAM,oBAAA,GAAN,cAAmC,YAAA,CAAa;AAAA,EACrD,YAAY,MAAA,EAAkC;AAC5C,IAAA,KAAA;AAAA,MACE,kBAAA;AAAA,MACA,CAAA,oHAAA,CAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AACF;AAGO,IAAM,qBAAA,GAAN,cAAoC,YAAA,CAAa;AAAA,EACtD,WAAA,CAAY,OAAA,GAAU,wCAAA,EAA0C,MAAA,EAAkC;AAChG,IAAA,KAAA,CAAM,mBAAA,EAAqB,SAAS,MAAM,CAAA;AAAA,EAC5C;AACF;AAGO,IAAM,kBAAA,GAAN,cAAiC,YAAA,CAAa;AAAA,EACnD,WAAA,CAAY,OAAA,GAAU,wCAAA,EAA0C,MAAA,EAAkC;AAChG,IAAA,KAAA,CAAM,gBAAA,EAAkB,SAAS,MAAM,CAAA;AAAA,EACzC;AACF;AAGO,IAAM,SAAA,GAAN,cAAwB,YAAA,CAAa;AAAA,EAC1C,WAAA,CAAY,OAAA,GAAU,6BAAA,EAA+B,MAAA,EAAkC;AACrF,IAAA,KAAA,CAAM,MAAA,EAAQ,SAAS,MAAM,CAAA;AAAA,EAC/B;AACF;AAGO,IAAM,QAAA,GAAN,cAAuB,YAAA,CAAa;AAAA,EACzC,WAAA,CAAY,SAAiB,MAAA,EAAkC;AAC7D,IAAA,KAAA,CAAM,WAAA,EAAa,SAAS,MAAM,CAAA;AAAA,EACpC;AACF;AAGO,IAAM,WAAA,GAAN,cAA0B,YAAA,CAAa;AAAA,EAC5C,WAAA,CAAY,SAAiB,MAAA,EAAkC;AAC7D,IAAA,KAAA,CAAM,QAAA,EAAU,SAAS,MAAM,CAAA;AAAA,EACjC;AACF;AAMO,IAAM,sBAAA,GAAN,cAAqC,YAAA,CAAa;AAAA,EACvD,YAAY,MAAA,EAAkC;AAC5C,IAAA,KAAA;AAAA,MACE,qBAAA;AAAA,MACA,0IAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AACF","file":"chunk-TMBBEGIF.js","sourcesContent":["/**\n * Typed, actionable errors — the Stripe bar (spec §11).\n *\n * Every error carries a stable machine-readable `code` and is thrown at the\n * EARLIEST possible layer: input errors fire client-side before any network or\n * chain call, so a studio never pays gas to discover a typo.\n */\n\nexport type PlaymosErrorCode =\n | \"invalid_amount\"\n | \"missing_field\"\n | \"insufficient_gas\"\n | \"wallet_connection\"\n | \"payment_failed\"\n | \"auth\"\n | \"api_error\"\n | \"config\"\n | \"nothing_to_withdraw\";\n\nexport class PlaymosError extends Error {\n readonly code: PlaymosErrorCode;\n /** Optional machine context (e.g. the offending field, the http status). */\n readonly detail?: Record<string, unknown>;\n\n constructor(code: PlaymosErrorCode, message: string, detail?: Record<string, unknown>) {\n super(message);\n this.name = new.target.name;\n this.code = code;\n this.detail = detail;\n // Restore prototype chain for `instanceof` across transpile targets.\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** amount ≤ 0, non-numeric, empty, or more than 2 decimal places. */\nexport class InvalidAmountError extends PlaymosError {\n constructor(amount: unknown) {\n super(\n \"invalid_amount\",\n `Invalid amount: ${JSON.stringify(amount)}. Provide a positive USD decimal string with at most 2 decimals, e.g. \"4.99\".`,\n { amount },\n );\n }\n}\n\n/** A required field (sku, playerId, gameId, roundId, agentId…) was empty. */\nexport class MissingFieldError extends PlaymosError {\n constructor(field: string) {\n super(\"missing_field\", `Missing required field: \"${field}\".`, { field });\n }\n}\n\n/** gas mode \"player\" and the player's ETH is too low to cover gas (pre-check, §7). */\nexport class InsufficientGasError extends PlaymosError {\n constructor(detail?: Record<string, unknown>) {\n super(\n \"insufficient_gas\",\n \"The player's wallet has too little ETH to pay gas. Ask them to add a little ETH, or switch to gas.mode: \\\"sponsored\\\".\",\n detail,\n );\n }\n}\n\n/** The player closed or failed the wallet sheet, or no provider is available. */\nexport class WalletConnectionError extends PlaymosError {\n constructor(message = \"Could not connect the player's wallet.\", detail?: Record<string, unknown>) {\n super(\"wallet_connection\", message, detail);\n }\n}\n\n/** The on-chain settlement reverted, was cancelled, or timed out. */\nexport class PaymentFailedError extends PlaymosError {\n constructor(message = \"The on-chain payment did not complete.\", detail?: Record<string, unknown>) {\n super(\"payment_failed\", message, detail);\n }\n}\n\n/** Bad, missing, or wrong-environment API key (e.g. a pk_test_ key on a live route). */\nexport class AuthError extends PlaymosError {\n constructor(message = \"Invalid or missing API key.\", detail?: Record<string, unknown>) {\n super(\"auth\", message, detail);\n }\n}\n\n/** The Playmos service returned a non-2xx we don't have a more specific error for. */\nexport class ApiError extends PlaymosError {\n constructor(message: string, detail?: Record<string, unknown>) {\n super(\"api_error\", message, detail);\n }\n}\n\n/** SDK misconfiguration (e.g. a missing contract address for on-chain mode). */\nexport class ConfigError extends PlaymosError {\n constructor(message: string, detail?: Record<string, unknown>) {\n super(\"config\", message, detail);\n }\n}\n\n/**\n * PrizePool.withdraw() would transfer 0 (NothingToWithdraw) — pre-check or on-chain.\n * Issue #41: typed surface so studios never show a raw contract revert to winners.\n */\nexport class NothingToWithdrawError extends PlaymosError {\n constructor(detail?: Record<string, unknown>) {\n super(\n \"nothing_to_withdraw\",\n \"Nothing to withdraw — this wallet has no credited prize balance on this PrizePool (round not settled for them, or already claimed).\",\n detail,\n );\n }\n}\n"]}
|
|
@@ -398,6 +398,16 @@ interface Payment {
|
|
|
398
398
|
* server's `hasEntered`.
|
|
399
399
|
*/
|
|
400
400
|
identity?: string;
|
|
401
|
+
/**
|
|
402
|
+
* Prize-pool entries only (issue #41): the PrizePool contract this entry hit.
|
|
403
|
+
* Keep it — winners need it later to read claimable balance and call `withdraw()`.
|
|
404
|
+
*/
|
|
405
|
+
prizePoolAddress?: `0x${string}`;
|
|
406
|
+
/**
|
|
407
|
+
* Prize-pool entries only (issue #41): the exact on-chain round key string used
|
|
408
|
+
* at enter (may equal `roundId`). Surface it so claim flow doesn't re-derive.
|
|
409
|
+
*/
|
|
410
|
+
roundKey?: string;
|
|
401
411
|
/** True only when produced by the labeled `mock: true` helper. */
|
|
402
412
|
mock?: boolean;
|
|
403
413
|
}
|
|
@@ -469,15 +479,42 @@ interface RoundState {
|
|
|
469
479
|
roundKey: string;
|
|
470
480
|
status: RoundStatus;
|
|
471
481
|
entryAmount: string;
|
|
472
|
-
/** Payable pool in USD from chain after reconcile; **`null` until reconciled** (never a fabricated "0.00"). */
|
|
482
|
+
/** Payable pool in USD from chain after reconcile; **`null` until reconciled** (never a fabricated "0.00"). Issue #42. */
|
|
473
483
|
pool: string | null;
|
|
474
|
-
/** Entrant count from chain after reconcile; **`null` until reconciled**. */
|
|
484
|
+
/** Entrant count from chain after reconcile; **`null` until reconciled**. Issue #42. */
|
|
475
485
|
entrants: number | null;
|
|
476
486
|
payout: PayoutRule;
|
|
477
487
|
closeAt?: string;
|
|
478
488
|
openTxHash?: `0x${string}`;
|
|
479
489
|
lockTxHash?: `0x${string}`;
|
|
480
490
|
settleTxHash?: `0x${string}`;
|
|
491
|
+
/**
|
|
492
|
+
* PrizePool contract for this round's game (issue #41). Needed to claim winnings.
|
|
493
|
+
* `null` only if the game has no contract address configured.
|
|
494
|
+
*/
|
|
495
|
+
prizePoolAddress?: `0x${string}` | null;
|
|
496
|
+
}
|
|
497
|
+
/** What `rounds.prize({ … })` returns — claimable pull-payment balance (issue #41). */
|
|
498
|
+
interface PrizeBalance {
|
|
499
|
+
roundId: string;
|
|
500
|
+
prizePoolAddress: `0x${string}`;
|
|
501
|
+
/** Winner wallet whose claimable balance was read. */
|
|
502
|
+
wallet: `0x${string}`;
|
|
503
|
+
/** USD decimal string (full micro precision). */
|
|
504
|
+
claimable: string;
|
|
505
|
+
/** Integer micro-USDC. */
|
|
506
|
+
claimableMicro: string;
|
|
507
|
+
/** Round status when known from `rounds.get` (optional). */
|
|
508
|
+
roundStatus?: RoundStatus;
|
|
509
|
+
}
|
|
510
|
+
/** What `rounds.withdraw({ … })` returns after a successful claim (issue #41). */
|
|
511
|
+
interface WithdrawResult {
|
|
512
|
+
prizePoolAddress: `0x${string}`;
|
|
513
|
+
/** Amount withdrawn in USD (from pre-check claimable; on-chain transfer matches). */
|
|
514
|
+
amount: string;
|
|
515
|
+
amountMicro: string;
|
|
516
|
+
txHash?: `0x${string}`;
|
|
517
|
+
status: "confirmed" | "failed" | "pending";
|
|
481
518
|
}
|
|
482
519
|
interface RoundSettleInput {
|
|
483
520
|
roundId: string;
|
|
@@ -509,7 +546,7 @@ interface SettleRoundResult {
|
|
|
509
546
|
* EARLIEST possible layer: input errors fire client-side before any network or
|
|
510
547
|
* chain call, so a studio never pays gas to discover a typo.
|
|
511
548
|
*/
|
|
512
|
-
type PlaymosErrorCode = "invalid_amount" | "missing_field" | "insufficient_gas" | "wallet_connection" | "payment_failed" | "auth" | "api_error" | "config";
|
|
549
|
+
type PlaymosErrorCode = "invalid_amount" | "missing_field" | "insufficient_gas" | "wallet_connection" | "payment_failed" | "auth" | "api_error" | "config" | "nothing_to_withdraw";
|
|
513
550
|
declare class PlaymosError extends Error {
|
|
514
551
|
readonly code: PlaymosErrorCode;
|
|
515
552
|
/** Optional machine context (e.g. the offending field, the http status). */
|
|
@@ -548,5 +585,12 @@ declare class ApiError extends PlaymosError {
|
|
|
548
585
|
declare class ConfigError extends PlaymosError {
|
|
549
586
|
constructor(message: string, detail?: Record<string, unknown>);
|
|
550
587
|
}
|
|
588
|
+
/**
|
|
589
|
+
* PrizePool.withdraw() would transfer 0 (NothingToWithdraw) — pre-check or on-chain.
|
|
590
|
+
* Issue #41: typed surface so studios never show a raw contract revert to winners.
|
|
591
|
+
*/
|
|
592
|
+
declare class NothingToWithdrawError extends PlaymosError {
|
|
593
|
+
constructor(detail?: Record<string, unknown>);
|
|
594
|
+
}
|
|
551
595
|
|
|
552
|
-
export { type AgentWallet as A,
|
|
596
|
+
export { type AgentWallet as A, MissingFieldError as B, ConfigError as C, NothingToWithdrawError as D, type EscrowHoldInput as E, PaymentFailedError as F, type GasConfig as G, type PaymentStatus as H, InsufficientGasError as I, type PayoutRule as J, PlaymosError as K, type Listing as L, type MarketplaceListInput as M, type Network as N, type PlaymosErrorCode as O, type PlaymosConfig as P, type RetryOptions as Q, type RoundOpenInput as R, type SettleRoundResult as S, type TransferResult as T, type RoundStatus as U, type VerifyResult as V, type WebhookEvent as W, type WalletConfig as X, WalletConnectionError as Y, type WalletConnector as Z, type WebhookEventType as _, type RoundState as a, type RoundSettleInput as b, type PrizeBalance as c, type WithdrawResult as d, type AgentFundResult as e, type EscrowHoldResult as f, type EscrowResolveResult as g, type MarketplaceSaleResult as h, type MarketplaceGetResult as i, type PayInput as j, type Payment as k, type EnterRoundInput as l, type TransferReconcile as m, type WaitOptions as n, type TransferInput as o, type TransferConfirmOptions as p, type AgentEconomyConfig as q, ApiError as r, AuthError as s, type ContractConfig as t, type Eip1193Provider as u, type GasMode as v, InvalidAmountError as w, type ListingStatus as x, type MarketplaceItem as y, type MarketplaceSale as z };
|
|
@@ -398,6 +398,16 @@ interface Payment {
|
|
|
398
398
|
* server's `hasEntered`.
|
|
399
399
|
*/
|
|
400
400
|
identity?: string;
|
|
401
|
+
/**
|
|
402
|
+
* Prize-pool entries only (issue #41): the PrizePool contract this entry hit.
|
|
403
|
+
* Keep it — winners need it later to read claimable balance and call `withdraw()`.
|
|
404
|
+
*/
|
|
405
|
+
prizePoolAddress?: `0x${string}`;
|
|
406
|
+
/**
|
|
407
|
+
* Prize-pool entries only (issue #41): the exact on-chain round key string used
|
|
408
|
+
* at enter (may equal `roundId`). Surface it so claim flow doesn't re-derive.
|
|
409
|
+
*/
|
|
410
|
+
roundKey?: string;
|
|
401
411
|
/** True only when produced by the labeled `mock: true` helper. */
|
|
402
412
|
mock?: boolean;
|
|
403
413
|
}
|
|
@@ -469,15 +479,42 @@ interface RoundState {
|
|
|
469
479
|
roundKey: string;
|
|
470
480
|
status: RoundStatus;
|
|
471
481
|
entryAmount: string;
|
|
472
|
-
/** Payable pool in USD from chain after reconcile; **`null` until reconciled** (never a fabricated "0.00"). */
|
|
482
|
+
/** Payable pool in USD from chain after reconcile; **`null` until reconciled** (never a fabricated "0.00"). Issue #42. */
|
|
473
483
|
pool: string | null;
|
|
474
|
-
/** Entrant count from chain after reconcile; **`null` until reconciled**. */
|
|
484
|
+
/** Entrant count from chain after reconcile; **`null` until reconciled**. Issue #42. */
|
|
475
485
|
entrants: number | null;
|
|
476
486
|
payout: PayoutRule;
|
|
477
487
|
closeAt?: string;
|
|
478
488
|
openTxHash?: `0x${string}`;
|
|
479
489
|
lockTxHash?: `0x${string}`;
|
|
480
490
|
settleTxHash?: `0x${string}`;
|
|
491
|
+
/**
|
|
492
|
+
* PrizePool contract for this round's game (issue #41). Needed to claim winnings.
|
|
493
|
+
* `null` only if the game has no contract address configured.
|
|
494
|
+
*/
|
|
495
|
+
prizePoolAddress?: `0x${string}` | null;
|
|
496
|
+
}
|
|
497
|
+
/** What `rounds.prize({ … })` returns — claimable pull-payment balance (issue #41). */
|
|
498
|
+
interface PrizeBalance {
|
|
499
|
+
roundId: string;
|
|
500
|
+
prizePoolAddress: `0x${string}`;
|
|
501
|
+
/** Winner wallet whose claimable balance was read. */
|
|
502
|
+
wallet: `0x${string}`;
|
|
503
|
+
/** USD decimal string (full micro precision). */
|
|
504
|
+
claimable: string;
|
|
505
|
+
/** Integer micro-USDC. */
|
|
506
|
+
claimableMicro: string;
|
|
507
|
+
/** Round status when known from `rounds.get` (optional). */
|
|
508
|
+
roundStatus?: RoundStatus;
|
|
509
|
+
}
|
|
510
|
+
/** What `rounds.withdraw({ … })` returns after a successful claim (issue #41). */
|
|
511
|
+
interface WithdrawResult {
|
|
512
|
+
prizePoolAddress: `0x${string}`;
|
|
513
|
+
/** Amount withdrawn in USD (from pre-check claimable; on-chain transfer matches). */
|
|
514
|
+
amount: string;
|
|
515
|
+
amountMicro: string;
|
|
516
|
+
txHash?: `0x${string}`;
|
|
517
|
+
status: "confirmed" | "failed" | "pending";
|
|
481
518
|
}
|
|
482
519
|
interface RoundSettleInput {
|
|
483
520
|
roundId: string;
|
|
@@ -509,7 +546,7 @@ interface SettleRoundResult {
|
|
|
509
546
|
* EARLIEST possible layer: input errors fire client-side before any network or
|
|
510
547
|
* chain call, so a studio never pays gas to discover a typo.
|
|
511
548
|
*/
|
|
512
|
-
type PlaymosErrorCode = "invalid_amount" | "missing_field" | "insufficient_gas" | "wallet_connection" | "payment_failed" | "auth" | "api_error" | "config";
|
|
549
|
+
type PlaymosErrorCode = "invalid_amount" | "missing_field" | "insufficient_gas" | "wallet_connection" | "payment_failed" | "auth" | "api_error" | "config" | "nothing_to_withdraw";
|
|
513
550
|
declare class PlaymosError extends Error {
|
|
514
551
|
readonly code: PlaymosErrorCode;
|
|
515
552
|
/** Optional machine context (e.g. the offending field, the http status). */
|
|
@@ -548,5 +585,12 @@ declare class ApiError extends PlaymosError {
|
|
|
548
585
|
declare class ConfigError extends PlaymosError {
|
|
549
586
|
constructor(message: string, detail?: Record<string, unknown>);
|
|
550
587
|
}
|
|
588
|
+
/**
|
|
589
|
+
* PrizePool.withdraw() would transfer 0 (NothingToWithdraw) — pre-check or on-chain.
|
|
590
|
+
* Issue #41: typed surface so studios never show a raw contract revert to winners.
|
|
591
|
+
*/
|
|
592
|
+
declare class NothingToWithdrawError extends PlaymosError {
|
|
593
|
+
constructor(detail?: Record<string, unknown>);
|
|
594
|
+
}
|
|
551
595
|
|
|
552
|
-
export { type AgentWallet as A,
|
|
596
|
+
export { type AgentWallet as A, MissingFieldError as B, ConfigError as C, NothingToWithdrawError as D, type EscrowHoldInput as E, PaymentFailedError as F, type GasConfig as G, type PaymentStatus as H, InsufficientGasError as I, type PayoutRule as J, PlaymosError as K, type Listing as L, type MarketplaceListInput as M, type Network as N, type PlaymosErrorCode as O, type PlaymosConfig as P, type RetryOptions as Q, type RoundOpenInput as R, type SettleRoundResult as S, type TransferResult as T, type RoundStatus as U, type VerifyResult as V, type WebhookEvent as W, type WalletConfig as X, WalletConnectionError as Y, type WalletConnector as Z, type WebhookEventType as _, type RoundState as a, type RoundSettleInput as b, type PrizeBalance as c, type WithdrawResult as d, type AgentFundResult as e, type EscrowHoldResult as f, type EscrowResolveResult as g, type MarketplaceSaleResult as h, type MarketplaceGetResult as i, type PayInput as j, type Payment as k, type EnterRoundInput as l, type TransferReconcile as m, type WaitOptions as n, type TransferInput as o, type TransferConfirmOptions as p, type AgentEconomyConfig as q, ApiError as r, AuthError as s, type ContractConfig as t, type Eip1193Provider as u, type GasMode as v, InvalidAmountError as w, type ListingStatus as x, type MarketplaceItem as y, type MarketplaceSale as z };
|
package/dist/index.cjs
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
var viem = require('viem');
|
|
4
4
|
|
|
5
|
+
// src/client.ts
|
|
6
|
+
|
|
5
7
|
// src/errors.ts
|
|
6
8
|
var PlaymosError = class extends Error {
|
|
7
9
|
constructor(code, message, detail) {
|
|
@@ -60,6 +62,15 @@ var ConfigError = class extends PlaymosError {
|
|
|
60
62
|
super("config", message, detail);
|
|
61
63
|
}
|
|
62
64
|
};
|
|
65
|
+
var NothingToWithdrawError = class extends PlaymosError {
|
|
66
|
+
constructor(detail) {
|
|
67
|
+
super(
|
|
68
|
+
"nothing_to_withdraw",
|
|
69
|
+
"Nothing to withdraw \u2014 this wallet has no credited prize balance on this PrizePool (round not settled for them, or already claimed).",
|
|
70
|
+
detail
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
63
74
|
|
|
64
75
|
// src/config.ts
|
|
65
76
|
var CHAIN_ID = {
|
|
@@ -462,6 +473,13 @@ var prizePoolAbi = [
|
|
|
462
473
|
],
|
|
463
474
|
outputs: [{ type: "bool" }]
|
|
464
475
|
},
|
|
476
|
+
{
|
|
477
|
+
type: "function",
|
|
478
|
+
name: "withdrawable",
|
|
479
|
+
stateMutability: "view",
|
|
480
|
+
inputs: [{ name: "account", type: "address" }],
|
|
481
|
+
outputs: [{ type: "uint256" }]
|
|
482
|
+
},
|
|
465
483
|
{
|
|
466
484
|
type: "function",
|
|
467
485
|
name: "withdraw",
|
|
@@ -549,6 +567,14 @@ function buildEntryCalls(args) {
|
|
|
549
567
|
{ to: args.prizePool, data: enterData }
|
|
550
568
|
];
|
|
551
569
|
}
|
|
570
|
+
function buildWithdrawCall(prizePool) {
|
|
571
|
+
const data = viem.encodeFunctionData({
|
|
572
|
+
abi: prizePoolAbi,
|
|
573
|
+
functionName: "withdraw",
|
|
574
|
+
args: []
|
|
575
|
+
});
|
|
576
|
+
return { to: prizePool, data };
|
|
577
|
+
}
|
|
552
578
|
|
|
553
579
|
// src/mock.ts
|
|
554
580
|
var MOCK_TX = "0x000000000000000000000000000000000000000000000000000000000000mock";
|
|
@@ -593,6 +619,9 @@ function mockEntryPayment(args) {
|
|
|
593
619
|
},
|
|
594
620
|
gameId: args.gameId,
|
|
595
621
|
roundId: args.roundId,
|
|
622
|
+
roundKey: args.roundId,
|
|
623
|
+
// Deterministic mock pool address so claim-flow unit tests can assert shape (#41).
|
|
624
|
+
prizePoolAddress: "0x0000000000000000000000000000000000000001",
|
|
596
625
|
playerId: args.playerId,
|
|
597
626
|
txHash: MOCK_TX,
|
|
598
627
|
chain: args.chain,
|
|
@@ -705,6 +734,112 @@ var Playmos = class {
|
|
|
705
734
|
`/rounds/${encodeURIComponent(input.roundId)}`
|
|
706
735
|
);
|
|
707
736
|
return round;
|
|
737
|
+
},
|
|
738
|
+
/**
|
|
739
|
+
* Read a wallet's **claimable** prize balance for a round (issue #41).
|
|
740
|
+
* Prefers the service chain read (`GET /v1/rounds/:id/prize?wallet=`); falls
|
|
741
|
+
* back to a direct eth_call when `prizePoolAddress` is supplied and no wallet
|
|
742
|
+
* connector is needed for the read path… actually uses service first, then
|
|
743
|
+
* on-chain via the player's provider if the service is unavailable.
|
|
744
|
+
*/
|
|
745
|
+
prize: async (input) => {
|
|
746
|
+
requireField(input?.roundId, "roundId");
|
|
747
|
+
const wallet = requireAddressField(input?.wallet, "wallet");
|
|
748
|
+
try {
|
|
749
|
+
const res = await this.http.get(
|
|
750
|
+
`/rounds/${encodeURIComponent(input.roundId)}/prize?wallet=${encodeURIComponent(wallet)}`
|
|
751
|
+
);
|
|
752
|
+
return res.prize;
|
|
753
|
+
} catch (e) {
|
|
754
|
+
if (!input.prizePoolAddress && !(e instanceof ApiError)) throw e;
|
|
755
|
+
if (!input.prizePoolAddress) throw e;
|
|
756
|
+
}
|
|
757
|
+
const prizePoolAddress = requireAddressField(input.prizePoolAddress, "prizePoolAddress");
|
|
758
|
+
const claimableMicro = await this.readWithdrawable(prizePoolAddress, wallet);
|
|
759
|
+
return {
|
|
760
|
+
roundId: input.roundId,
|
|
761
|
+
prizePoolAddress,
|
|
762
|
+
wallet,
|
|
763
|
+
claimable: formatMicroToUsd(claimableMicro),
|
|
764
|
+
claimableMicro: claimableMicro.toString()
|
|
765
|
+
};
|
|
766
|
+
},
|
|
767
|
+
/**
|
|
768
|
+
* Winner **claim** — call PrizePool.withdraw() from the player's wallet (issue #41).
|
|
769
|
+
* Pull-payment: credits from settle live in `withdrawable[msg.sender]`. No approve needed.
|
|
770
|
+
* Pass either `prizePoolAddress` (from enterRound / rounds.get) or `roundId` (service resolves).
|
|
771
|
+
*/
|
|
772
|
+
withdraw: async (input) => {
|
|
773
|
+
if (this.config.mock) {
|
|
774
|
+
return {
|
|
775
|
+
prizePoolAddress: input.prizePoolAddress ?? "0x0000000000000000000000000000000000000001",
|
|
776
|
+
amount: "0.00",
|
|
777
|
+
amountMicro: "0",
|
|
778
|
+
txHash: "0x000000000000000000000000000000000000000000000000000000000000mock",
|
|
779
|
+
status: "confirmed"
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
let prizePool = input.prizePoolAddress ? requireAddressField(input.prizePoolAddress, "prizePoolAddress") : void 0;
|
|
783
|
+
if (!prizePool) {
|
|
784
|
+
requireField(input?.roundId, "roundId");
|
|
785
|
+
const round = await this.rounds.get({ roundId: input.roundId });
|
|
786
|
+
if (!round.prizePoolAddress) {
|
|
787
|
+
throw new ConfigError(
|
|
788
|
+
"rounds.get did not return prizePoolAddress \u2014 pass prizePoolAddress from enterRound or configure contracts.prizePool"
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
prizePool = round.prizePoolAddress;
|
|
792
|
+
}
|
|
793
|
+
const provider = resolveProvider(this.config.wallet);
|
|
794
|
+
if (this.config.gas?.mode === "player") {
|
|
795
|
+
const from0 = await getAccount(provider);
|
|
796
|
+
await assertEnoughGas(provider, from0);
|
|
797
|
+
}
|
|
798
|
+
const from = await getAccount(provider);
|
|
799
|
+
let amountMicro = 0n;
|
|
800
|
+
if (input.checkBalance !== false) {
|
|
801
|
+
amountMicro = await this.readWithdrawable(prizePool, from);
|
|
802
|
+
if (amountMicro === 0n) {
|
|
803
|
+
throw new NothingToWithdrawError({ prizePoolAddress: prizePool, wallet: from });
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
const call = buildWithdrawCall(prizePool);
|
|
807
|
+
const paymasterUrl = this.config.gas?.mode === "sponsored" ? this.config.gas.paymasterUrl : void 0;
|
|
808
|
+
let txHash;
|
|
809
|
+
let status = "pending";
|
|
810
|
+
try {
|
|
811
|
+
const { id: callsId } = await sendCalls(
|
|
812
|
+
provider,
|
|
813
|
+
from,
|
|
814
|
+
this.env.chainId,
|
|
815
|
+
[call],
|
|
816
|
+
paymasterUrl
|
|
817
|
+
);
|
|
818
|
+
const waited = await waitForCalls(provider, callsId);
|
|
819
|
+
txHash = waited.txHash;
|
|
820
|
+
status = waited.status === "CONFIRMED" ? "confirmed" : waited.status === "FAILED" ? "failed" : "pending";
|
|
821
|
+
} catch (e) {
|
|
822
|
+
const msg = e?.message ?? String(e);
|
|
823
|
+
if (/NothingToWithdraw|nothing to withdraw/i.test(msg)) {
|
|
824
|
+
throw new NothingToWithdrawError({ prizePoolAddress: prizePool, wallet: from, cause: msg });
|
|
825
|
+
}
|
|
826
|
+
if (e instanceof NothingToWithdrawError || e instanceof PaymentFailedError) throw e;
|
|
827
|
+
throw new PaymentFailedError("PrizePool.withdraw() failed.", { cause: msg });
|
|
828
|
+
}
|
|
829
|
+
if (status === "failed") {
|
|
830
|
+
throw new PaymentFailedError("PrizePool.withdraw() did not confirm on-chain.", {
|
|
831
|
+
prizePoolAddress: prizePool,
|
|
832
|
+
txHash
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
if (amountMicro === 0n && input.checkBalance === false) ;
|
|
836
|
+
return {
|
|
837
|
+
prizePoolAddress: prizePool,
|
|
838
|
+
amount: formatMicroToUsd(amountMicro),
|
|
839
|
+
amountMicro: amountMicro.toString(),
|
|
840
|
+
txHash,
|
|
841
|
+
status
|
|
842
|
+
};
|
|
708
843
|
}
|
|
709
844
|
};
|
|
710
845
|
/**
|
|
@@ -1034,6 +1169,9 @@ var Playmos = class {
|
|
|
1034
1169
|
);
|
|
1035
1170
|
const payment2 = this.mapServerPayment(res.payment, "entry", amountMicro);
|
|
1036
1171
|
if (input.identity) payment2.identity = input.identity;
|
|
1172
|
+
const pool = res.clientParams?.contractAddress ?? this.config.contracts?.prizePool;
|
|
1173
|
+
if (pool) payment2.prizePoolAddress = pool.toLowerCase();
|
|
1174
|
+
payment2.roundKey = res.clientParams?.roundKey ?? res.clientParams?.roundId ?? payment2.roundId ?? input.roundId;
|
|
1037
1175
|
return payment2;
|
|
1038
1176
|
}
|
|
1039
1177
|
const intent = await this.http.post(
|
|
@@ -1061,6 +1199,8 @@ var Playmos = class {
|
|
|
1061
1199
|
const { txHash } = await waitForCalls(provider, callsId);
|
|
1062
1200
|
const payment = await this.settle(intent.payment.id, txHash);
|
|
1063
1201
|
payment.identity = identity;
|
|
1202
|
+
payment.prizePoolAddress = prizePool.toLowerCase();
|
|
1203
|
+
payment.roundKey = roundKey;
|
|
1064
1204
|
return payment;
|
|
1065
1205
|
}
|
|
1066
1206
|
/**
|
|
@@ -1170,6 +1310,58 @@ var Playmos = class {
|
|
|
1170
1310
|
const fromServer = intent.clientParams?.amountUnits ?? intent.clientParams?.amountMicro;
|
|
1171
1311
|
return fromServer ? BigInt(fromServer) : local;
|
|
1172
1312
|
}
|
|
1313
|
+
/**
|
|
1314
|
+
* Read PrizePool.withdrawable[wallet] via eth_call (issue #41).
|
|
1315
|
+
* Uses the wallet provider when present; otherwise a public Base Sepolia RPC in test.
|
|
1316
|
+
*/
|
|
1317
|
+
async readWithdrawable(prizePool, wallet) {
|
|
1318
|
+
const data = viem.encodeFunctionData({
|
|
1319
|
+
abi: prizePoolAbi,
|
|
1320
|
+
functionName: "withdrawable",
|
|
1321
|
+
args: [wallet]
|
|
1322
|
+
});
|
|
1323
|
+
let raw;
|
|
1324
|
+
try {
|
|
1325
|
+
if (await walletAvailable(this.config.wallet)) {
|
|
1326
|
+
const provider = resolveProvider(this.config.wallet);
|
|
1327
|
+
raw = await provider.request({
|
|
1328
|
+
method: "eth_call",
|
|
1329
|
+
params: [{ to: prizePool, data }, "latest"]
|
|
1330
|
+
});
|
|
1331
|
+
} else {
|
|
1332
|
+
const rpc = this.env.network === "base" ? "https://mainnet.base.org" : "https://sepolia.base.org";
|
|
1333
|
+
const res = await fetch(rpc, {
|
|
1334
|
+
method: "POST",
|
|
1335
|
+
headers: { "content-type": "application/json" },
|
|
1336
|
+
body: JSON.stringify({
|
|
1337
|
+
jsonrpc: "2.0",
|
|
1338
|
+
id: 1,
|
|
1339
|
+
method: "eth_call",
|
|
1340
|
+
params: [{ to: prizePool, data }, "latest"]
|
|
1341
|
+
})
|
|
1342
|
+
});
|
|
1343
|
+
const json = await res.json();
|
|
1344
|
+
if (!json.result) {
|
|
1345
|
+
throw new ApiError(
|
|
1346
|
+
`eth_call withdrawable failed: ${json.error?.message ?? "no result"}`,
|
|
1347
|
+
{ prizePool, wallet }
|
|
1348
|
+
);
|
|
1349
|
+
}
|
|
1350
|
+
raw = json.result;
|
|
1351
|
+
}
|
|
1352
|
+
} catch (e) {
|
|
1353
|
+
if (e instanceof ApiError) throw e;
|
|
1354
|
+
throw new ApiError(`Could not read withdrawable balance: ${e.message}`, {
|
|
1355
|
+
prizePool,
|
|
1356
|
+
wallet
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1359
|
+
return viem.decodeFunctionResult({
|
|
1360
|
+
abi: prizePoolAbi,
|
|
1361
|
+
functionName: "withdrawable",
|
|
1362
|
+
data: raw
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1173
1365
|
sponsorUrl(intent) {
|
|
1174
1366
|
if (this.config.gas?.mode === "player") return void 0;
|
|
1175
1367
|
return this.config.gas?.paymasterUrl ?? intent.clientParams?.paymasterUrl;
|
|
@@ -1457,6 +1649,7 @@ exports.InsufficientGasError = InsufficientGasError;
|
|
|
1457
1649
|
exports.InvalidAmountError = InvalidAmountError;
|
|
1458
1650
|
exports.MICRO_PER_USDC = MICRO_PER_USDC;
|
|
1459
1651
|
exports.MissingFieldError = MissingFieldError;
|
|
1652
|
+
exports.NothingToWithdrawError = NothingToWithdrawError;
|
|
1460
1653
|
exports.PaymentFailedError = PaymentFailedError;
|
|
1461
1654
|
exports.PayoutError = PayoutError;
|
|
1462
1655
|
exports.Playmos = Playmos;
|