@suigar/sdk 2.0.0-beta.0 → 2.0.0-beta.10

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/README.md CHANGED
@@ -2,73 +2,95 @@
2
2
 
3
3
  TypeScript SDK for building Suigar v2 game transactions on Sui.
4
4
 
5
- The published package entrypoint currently exposes:
6
-
7
- - `suigar`
8
- - `SuigarClient`
9
-
10
- It does not currently export the individual game builder functions from the package root.
11
-
12
5
  ## Installation
13
6
 
14
7
  ```bash
15
- npm install @suigar/sdk
8
+ npm install --save @suigar/sdk @mysten/sui @mysten/bcs
16
9
  ```
17
10
 
18
11
  Runtime requirements:
19
12
 
20
13
  - Node.js `>=22`
14
+ - ESM project configuration (`"type": "module"`)
15
+ - `@mysten/sui` v2
16
+ - `@mysten/bcs` v2
17
+
18
+ This SDK targets Sui TypeScript SDK 2.0+ only. Follow the official [Sui 2.0 migration guide](https://sdk.mystenlabs.com/sui/migrations/sui-2.0) if your app still uses the pre-2.0 client API.
19
+
20
+ ## What This Package Exposes
21
21
 
22
- ## Public API
22
+ The package ships three public entrypoints:
23
+
24
+ - `@suigar/sdk` for the extension factory and runtime client class
25
+ - `@suigar/sdk/games` for game-specific public types
26
+ - `@suigar/sdk/utils` for public parser, constants, and numeric helpers
27
+
28
+ The package root exposes the extension factory and client class:
23
29
 
24
30
  ```ts
25
- import { suigar } from '@suigar/sdk';
31
+ import { suigar, SuigarClient } from '@suigar/sdk';
26
32
  ```
27
33
 
28
- ### `suigar(options?)`
34
+ It does not export the individual transaction builders from the package root.
35
+ Those stay on the registered extension instance under `client.suigar.tx`.
29
36
 
30
- Creates a named Sui client extension. By default, it registers under `client.suigar`.
37
+ Utility exports are available from the utils subpath:
31
38
 
32
39
  ```ts
33
- const client = new SuiClient({ url }).$extend(suigar());
34
- client.suigar;
40
+ import {
41
+ DEFAULT_GAS_BUDGET_MIST,
42
+ DEFAULT_LIMBO_MULTIPLIER_SCALE,
43
+ DEFAULT_RANGE_SCALE,
44
+ fromMoveFloat,
45
+ fromMoveI64,
46
+ parseCoinType,
47
+ parseGameDetails,
48
+ RANGE_POINT_LIMIT,
49
+ toBigInt,
50
+ toU8,
51
+ } from '@suigar/sdk/utils';
35
52
  ```
36
53
 
37
- You can rename the extension:
54
+ Game-specific type exports are available from the dedicated `games` subpath:
38
55
 
39
56
  ```ts
40
- const client = new SuiClient({ url }).$extend(suigar({ name: 'casino' }));
41
-
42
- client.casino;
57
+ import type {
58
+ BuildCoinflipTransactionOptions,
59
+ BuildCreatePvPCoinflipTransactionOptions,
60
+ CoinSide,
61
+ PvPCoinflipAction,
62
+ } from '@suigar/sdk/games';
43
63
  ```
44
64
 
45
- ### `SuigarClient`
65
+ Current game-type subpath exports:
66
+
67
+ - `@suigar/sdk/games`: `CoinSide`, `PvPCoinflipAction`, `BuildCoinflipTransactionOptions`, `BuildLimboTransactionOptions`, `BuildPlinkoTransactionOptions`, `BuildRangeTransactionOptions`, `BuildWheelTransactionOptions`, `BuildCreatePvPCoinflipTransactionOptions`, `BuildJoinPvPCoinflipTransactionOptions`, `BuildCancelPvPCoinflipTransactionOptions`
68
+
69
+ What you actually use at runtime is the registered extension instance:
46
70
 
47
- The registered extension instance exposes:
71
+ ```ts
72
+ const client = new SuiGrpcClient({ baseUrl, network }).$extend(suigar());
48
73
 
49
- - `serializeTransactionToBase64(transaction)`
50
- - `bcs.BetResultEvent`
51
- - `tx.createBetTransaction(gameId, options)`
74
+ client.suigar.serializeTransactionToBase64(...);
75
+ client.suigar.getConfig();
76
+ client.suigar.getPvPCoinflipGames(...);
77
+ client.suigar.bcs;
78
+ client.suigar.tx;
79
+ ```
52
80
 
53
- ## Quick start
81
+ ## Quick Start
54
82
 
55
83
  ```ts
56
- import { SuiClient, getFullnodeUrl } from '@mysten/sui/client';
84
+ import { SuiGrpcClient } from '@mysten/sui/grpc';
57
85
  import { suigar } from '@suigar/sdk';
58
86
 
59
- const client = new SuiClient({
60
- url: getFullnodeUrl('testnet'),
61
- }).$extend(
62
- suigar({
63
- pyth: {
64
- suiPriceInfoObjectId: '0xPYTH_SUI_PRICE_INFO',
65
- usdcPriceInfoObjectId: '0xPYTH_USDC_PRICE_INFO',
66
- },
67
- }),
68
- );
87
+ const client = new SuiGrpcClient({
88
+ baseUrl: 'https://fullnode.testnet.sui.io:443',
89
+ network: 'testnet',
90
+ }).$extend(suigar());
69
91
 
70
92
  const tx = client.suigar.tx.createBetTransaction('coinflip', {
71
- owner: '0x123',
93
+ playerAddress: '0x123',
72
94
  coinType: '0x2::sui::SUI',
73
95
  stake: 1_000_000_000n,
74
96
  side: 'heads',
@@ -77,9 +99,142 @@ const tx = client.suigar.tx.createBetTransaction('coinflip', {
77
99
  const base64 = await client.suigar.serializeTransactionToBase64(tx);
78
100
  ```
79
101
 
80
- ## Game transactions
102
+ ## Extension Registration
103
+
104
+ ### `suigar(options?)`
105
+
106
+ Creates a named Sui client extension. By default, it registers under `client.suigar`.
107
+
108
+ ### Partner Setup
109
+
110
+ > **Important:** `partner` is the partner wallet address. Configure it once
111
+ > when you register the extension so the SDK can prepend that wallet address to
112
+ > supported bet metadata automatically.
113
+
114
+ ```ts
115
+ const client = new SuiGrpcClient({ baseUrl, network }).$extend(
116
+ suigar({ partner: '0xpartner_wallet_address' }),
117
+ );
118
+
119
+ client.suigar;
120
+ ```
121
+
122
+ Do not pass a partner slug, label, or display name here. Use the wallet
123
+ address that should receive partner attribution onchain.
124
+
125
+ You can rename the extension:
126
+
127
+ ```ts
128
+ const client = new SuiGrpcClient({ baseUrl, network }).$extend(
129
+ suigar({ name: 'games' }),
130
+ );
131
+
132
+ client.games.tx;
133
+ client.games.bcs;
134
+ ```
135
+
136
+ ## Config
137
+
138
+ `suigar(options?)` resolves config from:
139
+
140
+ - internal package ids by network
141
+ - internal supported coin types by network
142
+ - internal price info object ids by network
143
+ - the connected client network
144
+ - the extension name
145
+
146
+ Supported override areas:
147
+
148
+ - `name`
149
+ - `partner`
150
+
151
+ If `partner` is configured, the SDK automatically writes that partner wallet
152
+ address into the onchain metadata vec-map. Transaction builder options may also
153
+ include `metadata`, but reserved keys such as `partner` and `referrer` are
154
+ ignored with a warning when provided manually.
155
+
156
+ ## Runtime Surface
157
+
158
+ The registered extension instance exposes the main runtime surface:
159
+
160
+ - `getConfig()`
161
+ - `serializeTransactionToBase64(transaction, options?)`
162
+ - `getPvPCoinflipGames(options?)`
163
+ - `bcs`
164
+ - `tx`
165
+
166
+ ### `getConfig()`
167
+
168
+ Returns the resolved SDK configuration for the connected network.
169
+
170
+ This is intended mainly for debugging and inspection, for example to verify the
171
+ resolved package ids or supported coin mappings for the active client network.
172
+
173
+ It includes:
174
+
175
+ - `packageIds`
176
+ - `registryIds`
177
+ - `coinTypes`
178
+ - `priceInfoObjectIds`
179
+
180
+ ```ts
181
+ const config = client.suigar.getConfig();
182
+ console.log(config.packageIds);
183
+ ```
184
+
185
+ ### `serializeTransactionToBase64(transaction, options?)`
186
+
187
+ Builds a transaction with the configured Sui client and returns base64-encoded transaction bytes.
188
+
189
+ Use this when you need a transport-safe payload for a wallet, API, or external signer.
190
+
191
+ ```ts
192
+ const base64 = await client.suigar.serializeTransactionToBase64(tx);
193
+ ```
194
+
195
+ ### `getPvPCoinflipGames(options?)`
196
+
197
+ Lists unresolved PvP coinflip games from the configured PvP registry.
198
+
199
+ This reads the registry dynamic fields for the active network and resolves each
200
+ entry into parsed game state through a bulk `client.core.getObjects()` lookup.
201
+ Registry membership is the unresolved-state signal: once a match is joined and
202
+ resolved, the Move flow removes it from the registry and deletes the live
203
+ `Game` object.
204
+
205
+ Use this when a product needs the current set of open PvP coinflip matches for
206
+ browsing or lobby views.
207
+
208
+ By default, per-object fetch or parse failures are skipped so one broken or
209
+ already-deleted registry entry does not reject the full lookup. Pass
210
+ `throwOnError: true` if you want the call to reject instead.
211
+
212
+ Any supported `listDynamicFields()` options such as `limit`, `cursor`, or
213
+ `signal` can be passed through `options`.
214
+
215
+ ```ts
216
+ const games = await client.suigar.getPvPCoinflipGames({ limit: 20 });
217
+
218
+ for (const game of games) {
219
+ console.log(game.id);
220
+ console.log(game.coinType);
221
+ }
222
+ ```
223
+
224
+ ```ts
225
+ const games = await client.suigar.getPvPCoinflipGames({
226
+ limit: 20,
227
+ throwOnError: true,
228
+ });
229
+ ```
230
+
231
+ ## `tx`
232
+
233
+ Transaction builders live under `client.suigar.tx`.
234
+
235
+ ### Standard Games
81
236
 
82
- `tx.createBetTransaction(gameId, options)` supports these game ids:
237
+ Use `createBetTransaction(gameId, options)` for:
83
238
 
84
239
  - `coinflip`
85
240
  - `limbo`
@@ -87,183 +242,206 @@ const base64 = await client.suigar.serializeTransactionToBase64(tx);
87
242
  - `range`
88
243
  - `wheel`
89
244
 
90
- All games share this base option shape:
245
+ ```ts
246
+ const tx = client.suigar.tx.createBetTransaction('coinflip', {
247
+ playerAddress: '0x123',
248
+ coinType: '0x2::sui::SUI',
249
+ stake: 1_000_000_000n,
250
+ side: 'tails',
251
+ });
252
+ ```
253
+
254
+ Shared option shape:
91
255
 
92
- - `owner: string`
256
+ - `playerAddress: string`
93
257
  - `coinType: string`
94
258
  - `stake: number | bigint`
95
259
  - `cashStake?: number | bigint`
96
260
  - `betCount?: number | bigint`
97
261
  - `metadata?: Record<string, string | number | boolean | bigint | Uint8Array | number[] | null | undefined>`
98
262
  - `gasBudget?: number | bigint`
99
- - `sender?: string`
100
263
  - `allowGasCoinShortcut?: boolean`
101
264
 
102
265
  Shared behavior:
103
266
 
104
- - `stake` is the logical game stake passed into the Move call
105
- - `cashStake` is the actual coin balance withdrawn into the bet coin and defaults to `stake`
267
+ - `stake` is the logical stake passed into the Move call
268
+ - `cashStake` controls the withdrawn balance and defaults to `stake`
106
269
  - `betCount` defaults to `1`
107
- - `sender` overrides the transaction sender
108
270
  - `metadata` is encoded into `keys` and `values` byte arrays
109
- - the SDK resolves the Pyth price object from the configured coin mapping
110
- - the built reward object is transferred back to the owner
271
+ - `partner` configured via `suigar({ partner })` is prepended automatically to metadata as the partner wallet address
272
+ - `metadata.partner` and `metadata.referrer` are reserved and ignored with a warning
273
+ - the SDK resolves the price info object from the configured supported-coin mapping
274
+ - the reward object is transferred back to `playerAddress`
111
275
 
112
- ### Coinflip
276
+ Per-game options:
113
277
 
114
- Additional options:
278
+ - `coinflip`: `side: 'heads' | 'tails'`
279
+ - `limbo`: `targetMultiplier: number`, `scale?: number`
280
+ - `plinko`: `configId: number`
281
+ - `range`: `leftPoint: number`, `rightPoint: number`, `outOfRange?: boolean`, `scale?: number`
282
+ - `wheel`: `configId: number`
115
283
 
116
- - `side: 'heads' | 'tails'`
117
-
118
- Example:
284
+ Examples:
119
285
 
120
286
  ```ts
121
- const tx = client.suigar.tx.createBetTransaction('coinflip', {
122
- owner: '0x123',
287
+ const limboTx = client.suigar.tx.createBetTransaction('limbo', {
288
+ playerAddress: '0x123',
123
289
  coinType: '0x2::sui::SUI',
124
290
  stake: 1_000_000_000n,
125
- side: 'tails',
291
+ targetMultiplier: 2.5,
126
292
  });
127
- ```
128
-
129
- ### Limbo
130
-
131
- Additional options:
132
293
 
133
- - `targetMultiplier: number`
134
- - `scale?: number`
135
-
136
- Behavior:
137
-
138
- - `scale` defaults to the SDK limbo multiplier scale
139
- - `targetMultiplier` is converted with `Math.round(targetMultiplier * scale)`
140
-
141
- Example:
142
-
143
- ```ts
144
- const tx = client.suigar.tx.createBetTransaction('limbo', {
145
- owner: '0x123',
294
+ const rangeTx = client.suigar.tx.createBetTransaction('range', {
295
+ playerAddress: '0x123',
146
296
  coinType: '0x2::sui::SUI',
147
297
  stake: 1_000_000_000n,
148
- targetMultiplier: 2.5,
298
+ leftPoint: 25,
299
+ rightPoint: 75,
300
+ outOfRange: false,
149
301
  });
150
302
  ```
151
303
 
152
- ### Plinko
304
+ > **Note:**
305
+ >
306
+ > - limbo converts `targetMultiplier` with `Math.round(targetMultiplier * scale)`
307
+ > - with the default limbo scale `100`, exposed as `DEFAULT_LIMBO_MULTIPLIER_SCALE`, a target multiplier of `2.5` becomes `250` onchain
308
+ > - range converts each point with `Math.round(value * scale)`
309
+ > - range points are bounded by the contract limit exposed as `RANGE_POINT_LIMIT`
310
+ > - with the default range scale `1_000_000`, exposed as `DEFAULT_RANGE_SCALE`, valid UI values are `0` to `100`
311
+ > - plinko and wheel `configId` must fit in `u8`
153
312
 
154
- Additional options:
313
+ > **Tip:**
314
+ >
315
+ > - if you set `scale` to `10_000_000`, valid UI values become `0` to `10`
316
+ > - do not pre-scale range points before passing them to the SDK; pass the human value and let the SDK scale it once
155
317
 
156
- - `configId: number`
318
+ ### PvP Coinflip
157
319
 
158
- Behavior:
320
+ Use `createPvPCoinflipTransaction(action, options)` for PvP coinflip flows:
159
321
 
160
- - `configId` must fit in `u8`
322
+ - `create`
323
+ - `join`
324
+ - `cancel`
161
325
 
162
- Example:
326
+ Create:
163
327
 
164
328
  ```ts
165
- const tx = client.suigar.tx.createBetTransaction('plinko', {
166
- owner: '0x123',
329
+ const tx = client.suigar.tx.createPvPCoinflipTransaction('create', {
330
+ playerAddress: '0x123',
167
331
  coinType: '0x2::sui::SUI',
168
332
  stake: 1_000_000_000n,
169
- configId: 3,
333
+ side: 'heads',
334
+ isPrivate: false,
170
335
  });
171
336
  ```
172
337
 
173
- ### Range
174
-
175
- Additional options:
176
-
177
- - `leftPoint: number`
178
- - `rightPoint: number`
179
- - `outOfRange?: boolean`
180
- - `scale?: number`
338
+ Join:
181
339
 
182
- Behavior:
183
-
184
- - `scale` defaults to the SDK fixed-point range scale
185
- - `leftPoint` and `rightPoint` are converted with `Math.round(value * scale)`
186
- - `outOfRange` is coerced with `Boolean(...)`
340
+ ```ts
341
+ const tx = client.suigar.tx.createPvPCoinflipTransaction('join', {
342
+ playerAddress: '0x123',
343
+ coinType: '0x2::sui::SUI',
344
+ gameId: '0xGAME_ID',
345
+ });
346
+ ```
187
347
 
188
- Example:
348
+ Cancel:
189
349
 
190
350
  ```ts
191
- const tx = client.suigar.tx.createBetTransaction('range', {
192
- owner: '0x123',
351
+ const tx = client.suigar.tx.createPvPCoinflipTransaction('cancel', {
352
+ playerAddress: '0x123',
193
353
  coinType: '0x2::sui::SUI',
194
- stake: 1_000_000_000n,
195
- leftPoint: 0.95,
196
- rightPoint: 1.05,
197
- outOfRange: false,
354
+ gameId: '0xGAME_ID',
198
355
  });
199
356
  ```
200
357
 
201
- ### Wheel
358
+ Join derives the stake from `gameId` and uses the configured price info object
359
+ id for `coinType`.
202
360
 
203
- Additional options:
361
+ PvP shared options:
204
362
 
205
- - `configId: number`
363
+ - `playerAddress: string`
364
+ - `coinType: string`
365
+ - `metadata?: Record<string, string | number | boolean | bigint | Uint8Array | number[] | null | undefined>`
366
+ - `gasBudget?: number | bigint`
367
+ - `allowGasCoinShortcut?: boolean`
206
368
 
207
- Behavior:
369
+ Action-specific options:
208
370
 
209
- - `configId` must fit in `u8`
371
+ - `create`: `stake`, `side`, `isPrivate?`
372
+ - `join`: `gameId`
373
+ - `cancel`: `gameId`
210
374
 
211
- Example:
375
+ ## `bcs`
212
376
 
213
- ```ts
214
- const tx = client.suigar.tx.createBetTransaction('wheel', {
215
- owner: '0x123',
216
- coinType: '0x2::sui::SUI',
217
- stake: 1_000_000_000n,
218
- configId: 1,
219
- });
220
- ```
377
+ BCS helpers live under `client.suigar.bcs`.
221
378
 
222
- ## BCS helpers
379
+ Current exposed helpers:
223
380
 
224
- The client extension also exposes a generated MoveStruct helper for Suigar bet result data:
381
+ - `PvPCoinflipGame`
382
+ - `BetResultEvent`
383
+ - `PvPCoinflipGameCreatedEvent`
384
+ - `PvPCoinflipGameResolvedEvent`
385
+ - `PvPCoinflipGameCancelledEvent`
225
386
 
226
- - `client.suigar.bcs.BetResultEvent`
387
+ These are generated Move event decoders. Use them to parse Suigar event payloads from transaction results. The `@suigar/sdk/utils` subpath also exposes parser helpers for generated BCS values:
227
388
 
228
- Use generated BCS types to parse onchain object data. Fetch the object with `include: { content: true }` and pass `object.content` to the generated type's `.parse()` method.
389
+ - `PvPCoinflipGame` parses a PvP coinflip game object's `content`
390
+ - `fromMoveI64(float.exp)` converts a generated Move `i64` exponent to a JavaScript number
391
+ - `fromMoveFloat(float)` converts a generated Move `Float` struct to a JavaScript number
392
+ - `parseCoinType(type)` extracts the normalized coin type from generic Move object type strings such as PvP coinflip `Game<T>`
393
+ - `parseGameDetails(game_details)` decodes `BetResultEvent.game_details` entries into the expected string, number, and boolean values
229
394
 
230
- Always use `content`, not `objectBcs`, when parsing with generated types. The `objectBcs` field contains a full object envelope with additional metadata and is not the payload expected by the generated struct parser.
395
+ ### Parse PvP Coinflip Game Object Data
231
396
 
232
- ### Parse onchain object content
397
+ Use the generated BCS helper when you want to fetch and parse a game object:
233
398
 
234
399
  ```ts
235
- async function readBetResult(client: ClientWithCoreApi, id: string) {
236
- const { object } = await client.core.getObject({
237
- objectId: id,
238
- include: {
239
- content: true,
240
- },
241
- });
242
-
243
- const parsed = client.suigar.bcs.BetResultEvent.parse(object.content);
244
-
245
- console.log(parsed.player);
246
- console.log(parsed.coin_type.name);
247
- console.log(parsed.stake_amount);
248
- console.log(parsed.outcome_amount);
249
-
250
- return parsed;
251
- }
252
- ```
400
+ const game = await client.suigar.bcs.PvPCoinflipGame.get({
401
+ client,
402
+ objectId: '0xGAME_ID',
403
+ });
253
404
 
254
- ### Using the generated helper directly
405
+ console.log(game.json);
406
+ ```
255
407
 
256
- If you already have a content-bearing object response, parse `object.content` directly:
408
+ ### Parse Standard Bet Result Data
257
409
 
258
410
  ```ts
259
- const { object } = await client.core.getObject({
260
- objectId: '0xOBJECT_ID',
411
+ const executeResult = await client.core.executeTransaction({
412
+ transaction: transactionBytes,
413
+ signatures: [signature],
261
414
  include: {
262
- content: true,
415
+ events: true,
263
416
  },
264
417
  });
265
418
 
266
- const decoded = client.suigar.bcs.BetResultEvent.parse(object.content);
419
+ const finalResult = await client.core.waitForTransaction({
420
+ result: executeResult,
421
+ include: {
422
+ effects: true,
423
+ events: true,
424
+ },
425
+ });
426
+
427
+ if (finalResult.$kind === 'FailedTransaction') {
428
+ throw new Error(finalResult.FailedTransaction.status.error?.message);
429
+ }
430
+
431
+ console.log(finalResult.Transaction.digest);
432
+
433
+ const transactionResult = finalResult.Transaction;
434
+
435
+ const betResults = [];
436
+
437
+ for (const event of transactionResult.events ?? []) {
438
+ try {
439
+ const decoded = client.suigar.bcs.BetResultEvent.parse(event.bcs);
440
+ betResults.push(decoded);
441
+ } catch {
442
+ // Ignore non-BetResultEvent payloads.
443
+ }
444
+ }
267
445
  ```
268
446
 
269
447
  Parsed fields include:
@@ -277,75 +455,68 @@ Parsed fields include:
277
455
  - `game_details`
278
456
  - `metadata`
279
457
 
280
- For `game_details` and `metadata`, the decoded value is a `VecMap<string, vector<u8>>`-shaped structure, so values come back as byte arrays.
281
-
282
- Example conversion to strings:
458
+ `game_details` and `metadata` decode as `VecMap<string, vector<u8>>`-shaped data, so values come back as byte arrays. Use `parseGameDetails` from `@suigar/sdk/utils` to decode `game_details` with the SDK's known game-detail schemas.
283
459
 
284
460
  ```ts
285
- const decoded = client.suigar.bcs.BetResultEvent.parse(object.data.content);
286
- const textDecoder = new TextDecoder();
287
-
288
- const metadata = new Map(
289
- decoded.metadata.contents.map(({ key, value }) => [
290
- key,
291
- textDecoder.decode(new Uint8Array(value)),
292
- ]),
293
- );
461
+ import { parseGameDetails } from '@suigar/sdk/utils';
462
+
463
+ const decoded = client.suigar.bcs.BetResultEvent.parse(event.bcs);
464
+ const gameDetails = parseGameDetails(decoded.game_details);
294
465
  ```
295
466
 
296
- ## Config
467
+ `parseGameDetails` preserves the onchain keys and only changes the value representation. For example, coinflip details keep keys such as `player_bet` and `coin_outcome`; range details keep keys such as `roll_value`, `win`, and `payout_multiplier`.
297
468
 
298
- `suigar(options?)` resolves config from:
469
+ When the extension is configured with `partner`, decoded event `metadata` will
470
+ contain that partner wallet address under the `partner` entry.
299
471
 
300
- - the connected Sui network
301
- - internal default package ids
302
- - internal default SweetHouse package id by network
303
- - default coin types for `SUI`, `USDC`, and FlowX `USDC`
304
- - user overrides
472
+ > **Important:**
473
+ >
474
+ > - execute or wait for the transaction with `include: { events: true }`
475
+ > - unwrap the core API union with `result.$kind`, `result.Transaction`, and `result.FailedTransaction`
476
+ > - parse emitted events from the unwrapped transaction result
477
+ > - use `event.bcs` for consistent decoding across transports
478
+ > - use `parseGameDetails(decoded.game_details)` instead of hand-decoding standard game detail byte arrays
305
479
 
306
- Supported override areas:
480
+ > **Tip:**
481
+ >
482
+ > - `waitForTransaction({ result, include: { effects: true, events: true } })` is useful when you want the finalized transaction result before decoding
483
+ > - these helpers decode the event payload itself, not a full transaction response
307
484
 
308
- - `name`
309
- - `sweetHousePackageId`
310
- - `coinTypes.sui`
311
- - `coinTypes.usdc`
312
- - `coinTypes.usdcFlowx`
313
- - `gamesPackageId.coinflip`
314
- - `gamesPackageId.limbo`
315
- - `gamesPackageId.plinko`
316
- - `gamesPackageId['pvp-coinflip']`
317
- - `gamesPackageId.range`
318
- - `gamesPackageId.wheel`
319
- - `pyth.packageId`
320
- - `pyth.suiPriceInfoObjectId`
321
- - `pyth.usdcPriceInfoObjectId`
322
- - `pyth.priceInfoObjectIds[coinType]`
323
-
324
- Example:
485
+ ### Parse PvP Coinflip Event Data
325
486
 
326
- ```ts
327
- const client = new SuiClient({ url }).$extend(
328
- suigar({
329
- sweetHousePackageId: '0xsweethouse',
330
- pyth: {
331
- suiPriceInfoObjectId: '0xsui',
332
- usdcPriceInfoObjectId: '0xusdc',
333
- priceInfoObjectIds: {
334
- '0x123::custom::TOKEN': '0xprice',
335
- },
336
- },
337
- gamesPackageId: {
338
- coinflip: '0xcoinflip',
339
- wheel: '0xwheel',
340
- },
341
- }),
342
- );
343
- ```
487
+ Use the matching helper for each PvP coinflip event payload found in `transactionResult.events`:
488
+
489
+ - `client.suigar.bcs.PvPCoinflipGameCreatedEvent`
490
+ - `client.suigar.bcs.PvPCoinflipGameResolvedEvent`
491
+ - `client.suigar.bcs.PvPCoinflipGameCancelledEvent`
344
492
 
345
493
  ## Development
346
494
 
347
495
  ```bash
348
- npm run build
349
- npm run typecheck
350
- npm test
496
+ pnpm --dir packages/sdk build
497
+ pnpm --dir packages/sdk typecheck
498
+ pnpm --dir packages/sdk test
499
+ ```
500
+
501
+ ## Example App
502
+
503
+ This repository includes a Next.js integration playground in [apps/playground](../../apps/playground).
504
+
505
+ It demonstrates:
506
+
507
+ - standard game transactions through `client.suigar.tx.createBetTransaction(...)`
508
+ - PvP coinflip create, join, and cancel flows through `client.suigar.tx.createPvPCoinflipTransaction(...)`, exposed in the example through a PvP coinflip action selector
509
+ - unresolved PvP lobby browsing through `client.suigar.getPvPCoinflipGames(...)`, including public join cards while disconnected, an optional private-lobby join toggle, and connected-wallet filtering for cancel
510
+ - wallet connection and execution with `@mysten/dapp-kit-core` and `@mysten/dapp-kit-react`
511
+ - supported coin selection from `client.suigar.getConfig()`
512
+ - connected-wallet balance display for each supported coin in the example app
513
+ - privacy badges and copyable PvP game ids in the lobby UI
514
+ - decoding `BetResultEvent` and PvP events into a persistent event log
515
+ - parsing `BetResultEvent.game_details` with `parseGameDetails`
516
+
517
+ Run it from the repo root with:
518
+
519
+ ```bash
520
+ pnpm install
521
+ pnpm turbo run dev --filter='./apps/playground'
351
522
  ```