@block52/poker-vm-sdk 1.2.16 → 1.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/index.js CHANGED
@@ -6,6 +6,7 @@ var events = require('events');
6
6
  var crypto = require('@cosmjs/crypto');
7
7
  var crypto$1 = require('crypto');
8
8
  var encoding = require('@cosmjs/encoding');
9
+ var amino = require('@cosmjs/amino');
9
10
 
10
11
  /// <reference path="./types.d.ts" />
11
12
  const defaultFee$q = {
@@ -57319,6 +57320,109 @@ Deck.SUIT_MAP = {
57319
57320
  [exports.SUIT.SPADES]: "S"
57320
57321
  };
57321
57322
 
57323
+ /**
57324
+ * The community board for a Texas Hold'em hand.
57325
+ *
57326
+ * Encapsulates the two community-card arrays the engine currently keeps — the
57327
+ * cards dealt street-by-street (`street`, persisted to the DTO) and the five
57328
+ * pre-dealt up front at `deal()` (`preDealt`) — behind one type. This is a pure
57329
+ * refactor: every accessor returns exactly what the inline engine logic did, so
57330
+ * behaviour is unchanged. Collapsing the two arrays into a single canonical
57331
+ * board is a separate, consensus-affecting change (issue #2299).
57332
+ */
57333
+ class Board {
57334
+ constructor() {
57335
+ // Dealt one street at a time via dealCommunityCards() (was `_communityCards`).
57336
+ // This is the board persisted to the game DTO and rehydrated from JSON.
57337
+ this.street = [];
57338
+ // All five dealt up front at deal() (was `_communityCards2`).
57339
+ this.preDealt = [];
57340
+ }
57341
+ /**
57342
+ * Append cards dealt for the current street (was `_communityCards.push`).
57343
+ */
57344
+ addStreet(cards) {
57345
+ this.street.push(...cards);
57346
+ }
57347
+ /**
57348
+ * Record the five cards pre-dealt at deal() (was `_communityCards2.push`).
57349
+ */
57350
+ setPreDealt(cards) {
57351
+ this.preDealt.push(...cards);
57352
+ }
57353
+ /**
57354
+ * Number of street-dealt cards (was `_communityCards.length`).
57355
+ */
57356
+ get streetLength() {
57357
+ return this.street.length;
57358
+ }
57359
+ /**
57360
+ * Number of pre-dealt cards (was `_communityCards2.length`).
57361
+ */
57362
+ get preDealtLength() {
57363
+ return this.preDealt.length;
57364
+ }
57365
+ /**
57366
+ * The street-dealt community cards (was `[...this._communityCards]`).
57367
+ */
57368
+ get cards() {
57369
+ return [...this.street];
57370
+ }
57371
+ /**
57372
+ * The community cards visible at a given round, taken from the pre-dealt
57373
+ * board — matching the previous `getCommunityCards()` exactly:
57374
+ * FLOP -> preDealt[0..3], TURN -> preDealt[0..4], RIVER -> preDealt[0..5].
57375
+ */
57376
+ forRound(round) {
57377
+ const cards = [];
57378
+ if (round === exports.TexasHoldemRound.FLOP) {
57379
+ cards.push(...this.preDealt.slice(0, 3));
57380
+ }
57381
+ if (round === exports.TexasHoldemRound.TURN) {
57382
+ cards.push(...this.preDealt.slice(0, 4));
57383
+ }
57384
+ if (round === exports.TexasHoldemRound.RIVER) {
57385
+ cards.push(...this.preDealt.slice(0, 5));
57386
+ }
57387
+ return cards;
57388
+ }
57389
+ /**
57390
+ * The board `findWinners` falls back to when `forRound` is short: the
57391
+ * pre-dealt board for live games, else the street board rehydrated from JSON.
57392
+ * Preserves the previous inline selection verbatim.
57393
+ */
57394
+ forEvaluation(count) {
57395
+ return this.preDealt.length >= count
57396
+ ? this.preDealt.slice(0, count)
57397
+ : this.street.slice(0, count);
57398
+ }
57399
+ /**
57400
+ * The board `calculateWinner` falls back to at an early showdown: the fully
57401
+ * dealt street board if available, else the pre-dealt board. Preserves the
57402
+ * previous inline selection verbatim.
57403
+ */
57404
+ forShowdown() {
57405
+ return this.street.length >= Board.MAX_COMMUNITY_CARDS
57406
+ ? this.street.slice(0, Board.MAX_COMMUNITY_CARDS)
57407
+ : this.preDealt.slice(0, Board.MAX_COMMUNITY_CARDS);
57408
+ }
57409
+ /**
57410
+ * Clear both boards for a new hand (was two `.length = 0` assignments).
57411
+ */
57412
+ clear() {
57413
+ this.street.length = 0;
57414
+ this.preDealt.length = 0;
57415
+ }
57416
+ /**
57417
+ * Serialize the street board to mnemonics, matching
57418
+ * `TexasHoldemStateDTO.communityCards` (was `_communityCards.map(...)`).
57419
+ */
57420
+ toJson() {
57421
+ return this.street.map(card => card.mnemonic);
57422
+ }
57423
+ }
57424
+ Board.MAX_COMMUNITY_CARDS = 5;
57425
+
57322
57426
  /**
57323
57427
  * Example integration of custom poker solver with game engine
57324
57428
  * Demonstrates how to replace pokersolver with our custom implementation
@@ -60837,6 +60941,10 @@ var txExports = requireTx();
60837
60941
  * Default gas limit for all transactions
60838
60942
  */
60839
60943
  const DEFAULT_GAS_LIMIT = 1000000;
60944
+ // Registry with the poker message types, used to pack a Msg into an Any when
60945
+ // hand-building an unordered TxBody (signUnordered). Module-level so it's built
60946
+ // once and shared with the stargate client's registry semantics.
60947
+ const pokerRegistry = new protoSigning.Registry(msgTypes$u);
60840
60948
  /**
60841
60949
  * Create a gasless fee object with specified gas limit
60842
60950
  * Chain is configured with min-gas-prices = "0stake" so no fee tokens required
@@ -61327,36 +61435,31 @@ class SigningCosmosClient extends CosmosClient {
61327
61435
  * Requires the player account to EXIST on-chain (funded). Same gasless
61328
61436
  * fee + msg shape as {@link performActionSync}.
61329
61437
  */
61330
- async signPerformAction(gameId, action, amount = 0n, data, signerData) {
61331
- await this.initializeSigningClient();
61332
- if (!this.signingClient || !this.wallet) {
61333
- throw new Error("Signing client not initialized");
61438
+ async signPerformAction(gameId, action, amount = 0n, data, _signerData) {
61439
+ // Unordered signing needs only the wallet + REST getAccount — no RPC
61440
+ // stargate client, so this works without a reachable RPC endpoint.
61441
+ if (!this.wallet) {
61442
+ throw new Error("No wallet provided for signing");
61334
61443
  }
61335
61444
  const [account] = await this.wallet.getAccounts();
61336
61445
  const player = account.address;
61337
- const msgPerformAction = {
61338
- player,
61339
- gameId,
61340
- action,
61341
- amount: Long.fromString(amount.toString(), true),
61342
- data: data || ""
61343
- };
61344
61446
  const msg = {
61345
61447
  typeUrl: "/pokerchain.poker.v1.MsgPerformAction",
61346
- value: msgPerformAction
61448
+ value: {
61449
+ player,
61450
+ gameId,
61451
+ action,
61452
+ amount: Long.fromString(amount.toString(), true),
61453
+ data: data || ""
61454
+ }
61347
61455
  };
61348
- let explicit = signerData;
61349
- if (!explicit) {
61350
- const accountInfo = await this.getAccount(player);
61351
- explicit = {
61352
- accountNumber: Number(accountInfo.account.account_number),
61353
- sequence: Number(accountInfo.account.sequence),
61354
- chainId: this.config.chainId
61355
- };
61356
- }
61357
- const txRaw = await this.signingClient.sign(player, [msg], gaslessFee(), `Poker action (relay): ${action}`, explicit);
61358
- const base64 = encoding.toBase64(txExports.TxRaw.encode(txRaw).finish());
61359
- return { base64, sequence: explicit.sequence, accountNumber: explicit.accountNumber };
61456
+ // Settlement txs are UNORDERED (#247): no account sequence, so optimistic
61457
+ // play can never desync the chain sequence (the #2413 cascade). The
61458
+ // `signerData`/sequence arg is retained for signature compatibility but
61459
+ // ignored — unordered txs carry sequence 0.
61460
+ const { base64, accountNumber } = await this.signUnordered(msg);
61461
+ // sequence is meaningless for unordered txs; return 0 for shape compat.
61462
+ return { base64, sequence: 0, accountNumber };
61360
61463
  }
61361
61464
  /**
61362
61465
  * Resolves the signer data (account number + sequence + chain id) for a
@@ -61392,6 +61495,59 @@ class SigningCosmosClient extends CosmosClient {
61392
61495
  const base64 = encoding.toBase64(txExports.TxRaw.encode(txRaw).finish());
61393
61496
  return { base64, sequence: explicit.sequence, accountNumber: explicit.accountNumber };
61394
61497
  }
61498
+ /**
61499
+ * Sign a single cosmos message as an UNORDERED transaction (SDK 0.53): the
61500
+ * TxBody carries `unordered: true` + a `timeout_timestamp` and NO account
61501
+ * sequence (sequence MUST be 0). The chain admits it regardless of order and
61502
+ * dedups replay on (signer, timeout) — so settlement txs no longer carry a
61503
+ * client-guessed sequence and can never cascade (poker-vm#247, replaces the
61504
+ * sequence-tracking that caused #2413).
61505
+ *
61506
+ * Hand-built because CosmJS 0.32's TxBody proto predates the unordered
61507
+ * fields; we use the SDK's own cosmos.tx proto (which has them) for the body
61508
+ * and CosmJS only for AuthInfo + the direct signature.
61509
+ *
61510
+ * @param ttlSeconds - how far ahead the timeout_timestamp is set. Must be
61511
+ * within the chain's max unordered TTL (SDK default 10m). Also bounds how
61512
+ * long a replay of this exact tx is rejected.
61513
+ */
61514
+ async signUnordered(msg, ttlSeconds = 120) {
61515
+ // No RPC signing client needed: unordered signing is a direct signature
61516
+ // over locally-built bytes. Only the wallet (for signDirect) and a REST
61517
+ // getAccount (for account_number) are required.
61518
+ if (!this.wallet) {
61519
+ throw new Error("No wallet provided for signing");
61520
+ }
61521
+ const [account] = await this.wallet.getAccounts();
61522
+ const player = account.address;
61523
+ // account_number + chainId are still needed for the sign doc; the
61524
+ // SEQUENCE is not — unordered txs must sign with sequence 0.
61525
+ const accountInfo = await this.getAccount(player);
61526
+ const accountNumber = Number(accountInfo.account.account_number);
61527
+ const chainId = this.config.chainId;
61528
+ // Body with unordered=true + timeout_timestamp, messages packed as Any
61529
+ // via the poker registry. timeoutHeight stays 0 (we gate on timestamp).
61530
+ const bodyBytes = TxBody.encode(TxBody.fromPartial({
61531
+ messages: [pokerRegistry.encodeAsAny(msg)],
61532
+ memo: "",
61533
+ timeoutHeight: Long.UZERO,
61534
+ unordered: true,
61535
+ timeoutTimestamp: new Date(Date.now() + ttlSeconds * 1000)
61536
+ })).finish();
61537
+ // AuthInfo with sequence 0 (required for unordered). CosmJS builds this
61538
+ // — the pubkey/fee encoding is unchanged from an ordered tx.
61539
+ const pubkey = protoSigning.encodePubkey(amino.encodeSecp256k1Pubkey(account.pubkey));
61540
+ const gas = gaslessFee();
61541
+ const authInfoBytes = protoSigning.makeAuthInfoBytes([{ pubkey, sequence: 0 }], gas.amount, Number(gas.gas), undefined, undefined);
61542
+ const signDoc = protoSigning.makeSignDoc(bodyBytes, authInfoBytes, chainId, accountNumber);
61543
+ const { signature, signed } = await this.wallet.signDirect(player, signDoc);
61544
+ const base64 = encoding.toBase64(TxRaw.encode(TxRaw.fromPartial({
61545
+ bodyBytes: signed.bodyBytes,
61546
+ authInfoBytes: signed.authInfoBytes,
61547
+ signatures: [encoding.fromBase64(signature.signature)]
61548
+ })).finish());
61549
+ return { base64, accountNumber };
61550
+ }
61395
61551
  /**
61396
61552
  * Sign-only MsgJoinGame: builds and signs the player's buy-in tx WITHOUT
61397
61553
  * broadcasting, returning the base64 TxRaw for the gateway to relay
@@ -61865,6 +62021,7 @@ const registry = new protoSigning.Registry([
61865
62021
 
61866
62022
  exports.AllPlayerActions = AllPlayerActions;
61867
62023
  exports.BLOCK52_HD_PATH = BLOCK52_HD_PATH;
62024
+ exports.Board = Board;
61868
62025
  exports.COSMOS_CONSTANTS = COSMOS_CONSTANTS;
61869
62026
  exports.Client = Client;
61870
62027
  exports.CosmosClient = CosmosClient;