@playmos/sdk 0.3.10 → 0.3.11

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/server.js CHANGED
@@ -1,5 +1,7 @@
1
- import { PlaymosError } from './chunk-UZECDT6F.js';
1
+ import { PlaymosError, ApiError, toBytes32 } from './chunk-35WJANW4.js';
2
+ export { toBytes32 as enterPathToBytes32 } from './chunk-35WJANW4.js';
2
3
  import { timingSafeEqual, createHmac } from 'crypto';
4
+ import { encodeFunctionData, decodeFunctionResult } from 'viem';
3
5
 
4
6
  var WebhookSignatureError = class extends PlaymosError {
5
7
  constructor(message) {
@@ -41,5 +43,111 @@ function verifyWebhook(rawBody, signatureHeader, secret, opts = {}) {
41
43
  }
42
44
  return JSON.parse(body);
43
45
  }
46
+ var hasEnteredAbi = [
47
+ {
48
+ type: "function",
49
+ name: "hasEntered",
50
+ stateMutability: "view",
51
+ inputs: [
52
+ { name: "roundId", type: "bytes32" },
53
+ { name: "identity", type: "bytes32" }
54
+ ],
55
+ outputs: [{ type: "bool" }]
56
+ }
57
+ ];
58
+ var HAS_ENTERED_DEFAULT_TIMEOUT_MS = 8e3;
59
+ async function hasEntered(args) {
60
+ const rpcUrl = typeof args.rpcUrl === "string" ? args.rpcUrl.trim() : "";
61
+ if (!rpcUrl) {
62
+ throw new ApiError('hasEntered requires a non-empty "rpcUrl" (server-side only \u2014 no wallet provider)');
63
+ }
64
+ if (!args.prizePool || !/^0x[0-9a-fA-F]{40}$/.test(args.prizePool)) {
65
+ throw new ApiError("hasEntered requires prizePool as a 0x-prefixed 20-byte address");
66
+ }
67
+ if (!args.roundKey?.trim() || !args.identity?.trim()) {
68
+ throw new ApiError("hasEntered requires non-empty roundKey and identity (plain strings, same as enterRound)");
69
+ }
70
+ const roundKey = args.roundKey.trim();
71
+ const identity = args.identity.trim();
72
+ if (/^0x[0-9a-fA-F]{64}$/.test(roundKey) || /^0x[0-9a-fA-F]{64}$/.test(identity)) {
73
+ throw new ApiError(
74
+ "hasEntered requires plain-string roundKey/identity (not 0x+64 hex). Pre-hashed pins diverge from enterRound and from service GET /entered. Pass the same plain pins used at enter.",
75
+ { prizePool: args.prizePool, roundKey }
76
+ );
77
+ }
78
+ const data = encodeFunctionData({
79
+ abi: hasEnteredAbi,
80
+ functionName: "hasEntered",
81
+ args: [toBytes32(roundKey), toBytes32(identity)]
82
+ });
83
+ const timeoutMs = typeof args.timeoutMs === "number" && Number.isFinite(args.timeoutMs) && args.timeoutMs > 0 ? args.timeoutMs : HAS_ENTERED_DEFAULT_TIMEOUT_MS;
84
+ const controller = new AbortController();
85
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
86
+ const onExternalAbort = () => controller.abort();
87
+ if (args.signal) {
88
+ if (args.signal.aborted) controller.abort();
89
+ else args.signal.addEventListener("abort", onExternalAbort, { once: true });
90
+ }
91
+ let res;
92
+ try {
93
+ res = await fetch(rpcUrl, {
94
+ method: "POST",
95
+ headers: { "content-type": "application/json" },
96
+ body: JSON.stringify({
97
+ jsonrpc: "2.0",
98
+ id: 1,
99
+ method: "eth_call",
100
+ params: [{ to: args.prizePool, data }, "latest"]
101
+ }),
102
+ signal: controller.signal
103
+ });
104
+ } catch (e) {
105
+ const msg = e.message ?? String(e);
106
+ const aborted = controller.signal.aborted || e.name === "AbortError" || /abort/i.test(msg);
107
+ throw new ApiError(
108
+ aborted ? `hasEntered RPC timed out after ${timeoutMs}ms (or was aborted)` : `hasEntered RPC request failed: ${msg}`,
109
+ { prizePool: args.prizePool, roundKey }
110
+ );
111
+ } finally {
112
+ clearTimeout(timer);
113
+ if (args.signal) args.signal.removeEventListener("abort", onExternalAbort);
114
+ }
115
+ if (!res.ok) {
116
+ throw new ApiError(`hasEntered RPC HTTP ${res.status}`, {
117
+ prizePool: args.prizePool,
118
+ roundKey
119
+ });
120
+ }
121
+ let json;
122
+ try {
123
+ json = await res.json();
124
+ } catch (e) {
125
+ throw new ApiError(`hasEntered RPC response not JSON: ${e.message}`);
126
+ }
127
+ if (json.error || json.result == null || json.result === "") {
128
+ throw new ApiError(
129
+ `hasEntered eth_call failed: ${json.error?.message ?? `HTTP ${res.status}`}`,
130
+ { prizePool: args.prizePool, roundKey }
131
+ );
132
+ }
133
+ if (json.result === "0x" || json.result === "0x0") {
134
+ throw new ApiError(
135
+ `hasEntered eth_call returned empty data for pool ${args.prizePool} \u2014 wrong address or no contract code`,
136
+ { prizePool: args.prizePool, roundKey }
137
+ );
138
+ }
139
+ try {
140
+ return decodeFunctionResult({
141
+ abi: hasEnteredAbi,
142
+ functionName: "hasEntered",
143
+ data: json.result
144
+ });
145
+ } catch (e) {
146
+ throw new ApiError(
147
+ `hasEntered eth_call result malformed: ${e.message}`,
148
+ { prizePool: args.prizePool, roundKey }
149
+ );
150
+ }
151
+ }
44
152
 
45
- export { WebhookSignatureError, verifyWebhook };
153
+ export { WebhookSignatureError, hasEntered, verifyWebhook };
package/package.json CHANGED
@@ -1,17 +1,12 @@
1
1
  {
2
2
  "name": "@playmos/sdk",
3
- "version": "0.3.10",
3
+ "version": "0.3.11",
4
4
  "description": "Playmos SDK — stablecoin payments for games on Base. One SDK for IAP (1%), skill-game prize-pool entries (10%, 60/30/10), and agent economies. USD in, USDC on-chain, no crypto UX for players.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
7
- "homepage": "https://playmos-docs-public.vercel.app/docs",
7
+ "homepage": "https://playmos-docs-public.vercel.app/",
8
8
  "bugs": {
9
- "url": "https://playmos-docs-public.vercel.app/docs"
10
- },
11
- "repository": {
12
- "type": "git",
13
- "url": "git+https://github.com/playmos-labs/playmos-sdk.git",
14
- "directory": "sdk"
9
+ "url": "https://playmos-docs-public.vercel.app/"
15
10
  },
16
11
  "publishConfig": {
17
12
  "access": "public"
@@ -38,6 +33,7 @@
38
33
  "dist",
39
34
  "!dist/**/*.map",
40
35
  "README.md",
36
+ "CHANGELOG.md",
41
37
  "package.json"
42
38
  ],
43
39
  "scripts": {
@@ -1,83 +0,0 @@
1
- // src/errors.ts
2
- var PlaymosError = class extends Error {
3
- constructor(code, message, detail) {
4
- super(message);
5
- this.name = new.target.name;
6
- this.code = code;
7
- this.detail = detail;
8
- Object.setPrototypeOf(this, new.target.prototype);
9
- }
10
- };
11
- var InvalidAmountError = class extends PlaymosError {
12
- constructor(amount) {
13
- super(
14
- "invalid_amount",
15
- `Invalid amount: ${JSON.stringify(amount)}. Provide a positive USD decimal string with at most 2 decimals, e.g. "4.99".`,
16
- { amount }
17
- );
18
- }
19
- };
20
- var MissingFieldError = class extends PlaymosError {
21
- constructor(field) {
22
- super("missing_field", `Missing required field: "${field}".`, { field });
23
- }
24
- };
25
- var InsufficientGasError = class extends PlaymosError {
26
- constructor(detail) {
27
- super(
28
- "insufficient_gas",
29
- `The player's wallet has too little ETH to pay gas. Ask them to add a little ETH, or switch to gas.mode: "sponsored".`,
30
- detail
31
- );
32
- }
33
- };
34
- var WalletConnectionError = class extends PlaymosError {
35
- constructor(message = "Could not connect the player's wallet.", detail) {
36
- super("wallet_connection", message, detail);
37
- }
38
- };
39
- var WalletTimeoutError = class extends PlaymosError {
40
- constructor(message = "The wallet did not respond in time. Ask the player to approve the prompt, or retry.", detail) {
41
- super("wallet_timeout", message, detail);
42
- }
43
- };
44
- var PaymentFailedError = class extends PlaymosError {
45
- constructor(message = "The on-chain payment did not complete.", detail) {
46
- super("payment_failed", message, detail);
47
- }
48
- };
49
- var AlreadyEnteredError = class extends PlaymosError {
50
- constructor(detail) {
51
- super(
52
- "already_entered",
53
- "This identity already entered this round on-chain. For pay-per-play, pass a unique identity per attempt (not just the wallet address).",
54
- detail
55
- );
56
- }
57
- };
58
- var AuthError = class extends PlaymosError {
59
- constructor(message = "Invalid or missing API key.", detail) {
60
- super("auth", message, detail);
61
- }
62
- };
63
- var ApiError = class extends PlaymosError {
64
- constructor(message, detail) {
65
- super("api_error", message, detail);
66
- }
67
- };
68
- var ConfigError = class extends PlaymosError {
69
- constructor(message, detail) {
70
- super("config", message, detail);
71
- }
72
- };
73
- var NothingToWithdrawError = class extends PlaymosError {
74
- constructor(detail) {
75
- super(
76
- "nothing_to_withdraw",
77
- "Nothing to withdraw \u2014 this wallet has no credited prize balance on this PrizePool (round not settled for them, or already claimed).",
78
- detail
79
- );
80
- }
81
- };
82
-
83
- export { AlreadyEnteredError, ApiError, AuthError, ConfigError, InsufficientGasError, InvalidAmountError, MissingFieldError, NothingToWithdrawError, PaymentFailedError, PlaymosError, WalletConnectionError, WalletTimeoutError };