@block52/poker-vm-sdk 1.4.0 → 1.4.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
@@ -56167,6 +56167,14 @@ exports.NonPlayerActionType = void 0;
56167
56167
  // so each has a single responsibility. Sets WAITING_FOR_BIG_BLIND. (#2139)
56168
56168
  NonPlayerActionType["SIT_IN_AND_WAIT"] = "sit-in-and-wait";
56169
56169
  NonPlayerActionType["SIT_OUT"] = "sit-out";
56170
+ // Action clock enforcement (poker-vm#2523): submitted by ANY address once the
56171
+ // next-to-act player's turn deadline (last recorded action timestamp +
56172
+ // gameOptions.timeout seconds) has passed. The engine plays that player's
56173
+ // forced default action on their behalf — CHECK if legal else FOLD in a
56174
+ // betting round, the owed blind post or DEAL in ANTE, MUCK if legal else
56175
+ // SHOW at showdown — and records THAT action; no "timeout" turn is ever
56176
+ // written to previousActions. Rejected while the turn has time remaining.
56177
+ NonPlayerActionType["TIMEOUT"] = "timeout";
56170
56178
  NonPlayerActionType["TOP_UP"] = "top-up";
56171
56179
  })(exports.NonPlayerActionType || (exports.NonPlayerActionType = {}));
56172
56180
  const AllPlayerActions = { ...exports.PlayerActionType, ...exports.NonPlayerActionType };
@@ -61156,6 +61164,79 @@ function requireTx () {
61156
61164
 
61157
61165
  var txExports = requireTx();
61158
61166
 
61167
+ /**
61168
+ * Thrown when a transaction was broadcast and included in a block but its
61169
+ * EXECUTION failed (nonzero `DeliverTxResponse.code`). The hash, code and raw
61170
+ * log are retained so the failure can be looked up and shown. (#2609)
61171
+ */
61172
+ class TxExecutionError extends Error {
61173
+ constructor(operation, result) {
61174
+ super(`Failed to ${operation}: transaction ${result.transactionHash} failed with code ${result.code}` +
61175
+ (result.rawLog ? ` — ${result.rawLog}` : ""));
61176
+ this.name = "TxExecutionError";
61177
+ this.transactionHash = result.transactionHash;
61178
+ this.code = result.code;
61179
+ this.rawLog = result.rawLog;
61180
+ }
61181
+ }
61182
+ /**
61183
+ * Assert that a broadcast transaction actually EXECUTED successfully.
61184
+ *
61185
+ * CosmJS resolves `signAndBroadcast` normally when execution fails — a nonzero
61186
+ * `DeliverTxResponse.code` is the only failure signal, and it is easy to miss
61187
+ * because nothing throws. Returning `result.transactionHash` without checking
61188
+ * reports "insufficient funds" and "invalid request" to the caller as success.
61189
+ *
61190
+ * Every SDK method that broadcasts and waits for inclusion goes through this.
61191
+ * The exception is `performActionSync`, which returns a CheckTx-only hash by
61192
+ * design and whose caller waits on a WebSocket push for the committed result —
61193
+ * see the comment there before adding a check to it.
61194
+ */
61195
+ function assertExecuted(operation, result) {
61196
+ // A real DeliverTxResponse always carries `code`. If it is missing we cannot
61197
+ // tell success from failure, so fail closed and say why — reporting it as
61198
+ // "code undefined" would send someone hunting a chain error that does not
61199
+ // exist, when the actual problem is a malformed response or a partial mock.
61200
+ if (typeof result?.code !== "number") {
61201
+ throw new Error(`Failed to ${operation}: broadcast response has no execution code, so success cannot be confirmed ` +
61202
+ `(transaction ${result?.transactionHash ?? "unknown"})`);
61203
+ }
61204
+ if (result.code !== 0) {
61205
+ throw new TxExecutionError(operation, result);
61206
+ }
61207
+ }
61208
+ /**
61209
+ * Classify an error thrown by an SDK broadcast method. Returns `undefined` for
61210
+ * errors raised before any broadcast (argument validation, no wallet, signing)
61211
+ * or by another layer (REST queries, network) — those carry no transaction.
61212
+ */
61213
+ function txFailurePhase(error) {
61214
+ if (error instanceof TxExecutionError) {
61215
+ return "execution-failed";
61216
+ }
61217
+ if (error instanceof stargate.BroadcastTxError) {
61218
+ return "not-broadcast";
61219
+ }
61220
+ if (error instanceof stargate.TimeoutError) {
61221
+ return "unknown";
61222
+ }
61223
+ return undefined;
61224
+ }
61225
+ /**
61226
+ * The hash of the transaction behind a failed broadcast call, when one exists:
61227
+ * the included-but-failed tx for `"execution-failed"`, the pending tx for
61228
+ * `"unknown"`. `"not-broadcast"` and pre-broadcast errors have none.
61229
+ */
61230
+ function txFailureHash(error) {
61231
+ if (error instanceof TxExecutionError) {
61232
+ return error.transactionHash;
61233
+ }
61234
+ if (error instanceof stargate.TimeoutError) {
61235
+ return error.txId;
61236
+ }
61237
+ return undefined;
61238
+ }
61239
+
61159
61240
  /**
61160
61241
  * Default gas limit for all transactions
61161
61242
  */
@@ -61164,6 +61245,18 @@ const DEFAULT_GAS_LIMIT = 1000000;
61164
61245
  // hand-building an unordered TxBody (signUnordered). Module-level so it's built
61165
61246
  // once and shared with the stargate client's registry semantics.
61166
61247
  const pokerRegistry = new protoSigning.Registry(msgTypes$u);
61248
+ // The chain dedups unordered txs on (signer, timeout_timestamp), so two txs
61249
+ // from one account must never share a timeout. Date has millisecond resolution
61250
+ // and a client fires blind + deal + action back to back, so track the last
61251
+ // timeout handed out per signer and keep it strictly increasing. Module-level:
61252
+ // callers rebuild the client (e.g. after a transport error) mid-session.
61253
+ const lastUnorderedTimeoutMs = new Map();
61254
+ function nextUnorderedTimeoutMs(signer, ttlSeconds) {
61255
+ const wanted = Date.now() + ttlSeconds * 1000;
61256
+ const timeoutMs = Math.max(wanted, (lastUnorderedTimeoutMs.get(signer) ?? 0) + 1);
61257
+ lastUnorderedTimeoutMs.set(signer, timeoutMs);
61258
+ return timeoutMs;
61259
+ }
61167
61260
  /**
61168
61261
  * Create a gasless fee object with specified gas limit
61169
61262
  * Chain is configured with min-gas-prices = "0stake" so no fee tokens required
@@ -61189,6 +61282,7 @@ class SigningCosmosClient extends CosmosClient {
61189
61282
  gasPrice: typeof config.gasPrice === 'string' ? config.gasPrice : (config.gasPrice?.toString() || COSMOS_CONSTANTS.DEFAULT_GAS_PRICE)
61190
61283
  };
61191
61284
  super(baseConfig);
61285
+ this.accountNumbers = new Map();
61192
61286
  this.wallet = config.wallet;
61193
61287
  // Handle gasPrice - convert string to GasPrice if needed
61194
61288
  if (typeof config.gasPrice === 'string') {
@@ -61246,13 +61340,16 @@ class SigningCosmosClient extends CosmosClient {
61246
61340
  const coins = [{ denom: denom, amount: amount.toString() }];
61247
61341
  const fee = gaslessFee();
61248
61342
  const result = await this.signingClient.sendTokens(fromAddress, toAddress, coins, fee, memo || "");
61343
+ assertExecuted("send tokens", result);
61249
61344
  return result.transactionHash;
61250
61345
  }
61251
61346
  /**
61252
61347
  * Send B52USDC tokens
61253
61348
  */
61254
61349
  async sendB52USDC(fromAddress, toAddress, amount, memo) {
61255
- return this.sendTokens(fromAddress, toAddress, amount, memo);
61350
+ // `denom` is the 4th parameter — passing `memo` here put the memo text
61351
+ // into the coin denom and always sent an empty memo.
61352
+ return this.sendTokens(fromAddress, toAddress, amount, undefined, memo);
61256
61353
  }
61257
61354
  /**
61258
61355
  * Create a new poker game
@@ -61342,6 +61439,7 @@ class SigningCosmosClient extends CosmosClient {
61342
61439
  });
61343
61440
  try {
61344
61441
  const result = await this.signingClient.signAndBroadcast(creator, [msg], fee, memo);
61442
+ assertExecuted("create game", result);
61345
61443
  console.log("✅ Game creation transaction successful:", result.transactionHash);
61346
61444
  return result.transactionHash;
61347
61445
  }
@@ -61379,6 +61477,7 @@ class SigningCosmosClient extends CosmosClient {
61379
61477
  console.log("🪑 Joining game:", { gameId, seat, buyInAmount: buyInAmount.toString() });
61380
61478
  try {
61381
61479
  const result = await this.signingClient.signAndBroadcast(player, [msg], fee, memo);
61480
+ assertExecuted("join game", result);
61382
61481
  console.log("✅ Join game transaction successful:", result.transactionHash);
61383
61482
  return result.transactionHash;
61384
61483
  }
@@ -61426,6 +61525,7 @@ class SigningCosmosClient extends CosmosClient {
61426
61525
  console.log("🚪 Leaving game:", { gameId });
61427
61526
  try {
61428
61527
  const result = await this.signingClient.signAndBroadcast(creator, [msg], fee, memo);
61528
+ assertExecuted("leave game", result);
61429
61529
  console.log("✅ Leave game transaction successful:", result.transactionHash);
61430
61530
  return result.transactionHash;
61431
61531
  }
@@ -61458,6 +61558,7 @@ class SigningCosmosClient extends CosmosClient {
61458
61558
  const memo = "Record hand end via SDK";
61459
61559
  try {
61460
61560
  const result = await this.signingClient.signAndBroadcast(creator, [msg], fee, memo);
61561
+ assertExecuted("record hand end", result);
61461
61562
  return result.transactionHash;
61462
61563
  }
61463
61564
  catch (error) {
@@ -61491,6 +61592,7 @@ class SigningCosmosClient extends CosmosClient {
61491
61592
  console.log("🗑️ Deleting game:", { gameId });
61492
61593
  try {
61493
61594
  const result = await this.signingClient.signAndBroadcast(creator, [msg], fee, memo);
61595
+ assertExecuted("delete game", result);
61494
61596
  console.log("✅ Delete game transaction successful:", result.transactionHash);
61495
61597
  return result.transactionHash;
61496
61598
  }
@@ -61530,6 +61632,7 @@ class SigningCosmosClient extends CosmosClient {
61530
61632
  console.log("🛑 Force-closing game:", { gameId });
61531
61633
  try {
61532
61634
  const result = await this.signingClient.signAndBroadcast(creator, [msg], fee, memo);
61635
+ assertExecuted("force close game", result);
61533
61636
  console.log("✅ Force-close transaction successful:", result.transactionHash);
61534
61637
  return result.transactionHash;
61535
61638
  }
@@ -61578,6 +61681,7 @@ class SigningCosmosClient extends CosmosClient {
61578
61681
  });
61579
61682
  try {
61580
61683
  const result = await this.signingClient.signAndBroadcast(player, [msg], fee, memo);
61684
+ assertExecuted("perform action", result);
61581
61685
  console.log("✅ Action transaction successful:", result.transactionHash);
61582
61686
  return result.transactionHash;
61583
61687
  }
@@ -61610,6 +61714,11 @@ class SigningCosmosClient extends CosmosClient {
61610
61714
  *
61611
61715
  * Returns the tx hash on CheckTx success; throws if CheckTx
61612
61716
  * rejects (invalid signature, malformed msg, insufficient gas).
61717
+ *
61718
+ * Deliberately NOT routed through `assertExecuted` (#2609): there is no
61719
+ * DeliverTx result to check at this point — the hash is a CheckTx receipt,
61720
+ * and the execution outcome arrives over the WebSocket. Every other
61721
+ * broadcasting method in this class waits for inclusion and must check.
61613
61722
  */
61614
61723
  async performActionSync(gameId, action, amount = 0n, data) {
61615
61724
  await this.initializeSigningClient();
@@ -61618,26 +61727,30 @@ class SigningCosmosClient extends CosmosClient {
61618
61727
  }
61619
61728
  const [account] = await this.wallet.getAccounts();
61620
61729
  const player = account.address;
61621
- // Same msg shape as performAction — only the broadcast mode differs.
61622
- const msgPerformAction = {
61623
- player,
61624
- gameId,
61625
- action,
61626
- amount: Long.fromString(amount.toString(), true),
61627
- data: data || ""
61628
- };
61730
+ // Same msg shape as performAction — only signing + broadcast mode differ.
61629
61731
  const msg = {
61630
61732
  typeUrl: "/pokerchain.poker.v1.MsgPerformAction",
61631
- value: msgPerformAction
61733
+ value: {
61734
+ player,
61735
+ gameId,
61736
+ action,
61737
+ amount: Long.fromString(amount.toString(), true),
61738
+ data: data || ""
61739
+ }
61632
61740
  };
61633
- const fee = gaslessFee();
61634
- const memo = `Poker action (sync): ${action}`;
61635
61741
  try {
61636
- // signAndBroadcastSync returns just the txhash, no DeliverTx
61637
- // result. The caller waits on the WS push for the
61742
+ // UNORDERED (#2619): gameplay txs carry no account sequence. Sync
61743
+ // broadcast returns at CheckTx, not at commit, so an ordered tx
61744
+ // signed before the previous one commits re-reads the same
61745
+ // committed sequence and is rejected with code 32 — blind + deal +
61746
+ // action from one account inside a block did exactly that. This is
61747
+ // the chain-direct twin of signPerformAction's fix (#247, ui#520).
61748
+ const { base64 } = await this.signUnordered(msg);
61749
+ // broadcastTxSync returns just the txhash, no DeliverTx result, and
61750
+ // throws BroadcastTxError when CheckTx rejects — the same contract
61751
+ // signAndBroadcastSync had. The caller waits on the WS push for the
61638
61752
  // authoritative outcome.
61639
- const txHash = await this.signingClient.signAndBroadcastSync(player, [msg], fee, memo);
61640
- return txHash;
61753
+ return await this.signingClient.broadcastTxSync(encoding.fromBase64(base64));
61641
61754
  }
61642
61755
  catch (error) {
61643
61756
  console.error("❌ Action (sync) failed at CheckTx:", error);
@@ -61747,8 +61860,7 @@ class SigningCosmosClient extends CosmosClient {
61747
61860
  const player = account.address;
61748
61861
  // account_number + chainId are still needed for the sign doc; the
61749
61862
  // SEQUENCE is not — unordered txs must sign with sequence 0.
61750
- const accountInfo = await this.getAccount(player);
61751
- const accountNumber = Number(accountInfo.account.account_number);
61863
+ const accountNumber = await this.unorderedAccountNumber(player);
61752
61864
  const chainId = this.config.chainId;
61753
61865
  // Body with unordered=true + timeout_timestamp, messages packed as Any
61754
61866
  // via the poker registry. timeoutHeight stays 0 (we gate on timestamp).
@@ -61757,7 +61869,7 @@ class SigningCosmosClient extends CosmosClient {
61757
61869
  memo: "",
61758
61870
  timeoutHeight: Long.UZERO,
61759
61871
  unordered: true,
61760
- timeoutTimestamp: new Date(Date.now() + ttlSeconds * 1000)
61872
+ timeoutTimestamp: new Date(nextUnorderedTimeoutMs(player, ttlSeconds))
61761
61873
  })).finish();
61762
61874
  // AuthInfo with sequence 0 (required for unordered). CosmJS builds this
61763
61875
  // — the pubkey/fee encoding is unchanged from an ordered tx.
@@ -61773,6 +61885,22 @@ class SigningCosmosClient extends CosmosClient {
61773
61885
  })).finish());
61774
61886
  return { base64, accountNumber };
61775
61887
  }
61888
+ /**
61889
+ * account_number for the unordered sign doc. It is assigned once when the
61890
+ * account is created and never changes, so look it up once per signer —
61891
+ * performActionSync signs on every player action and shouldn't pay a REST
61892
+ * round trip (or inherit its failure modes) each time.
61893
+ */
61894
+ async unorderedAccountNumber(player) {
61895
+ const cached = this.accountNumbers.get(player);
61896
+ if (cached !== undefined) {
61897
+ return cached;
61898
+ }
61899
+ const accountInfo = await this.getAccount(player);
61900
+ const accountNumber = Number(accountInfo.account.account_number);
61901
+ this.accountNumbers.set(player, accountNumber);
61902
+ return accountNumber;
61903
+ }
61776
61904
  /**
61777
61905
  * Sign-only MsgJoinGame: builds and signs the player's buy-in tx WITHOUT
61778
61906
  * broadcasting, returning the base64 TxRaw for the gateway to relay
@@ -61871,6 +61999,7 @@ class SigningCosmosClient extends CosmosClient {
61871
61999
  });
61872
62000
  try {
61873
62001
  const result = await this.signingClient.signAndBroadcast(player, [msg], fee, memo);
62002
+ assertExecuted("top up", result);
61874
62003
  console.log("✅ Top-up transaction successful:", result.transactionHash);
61875
62004
  return result.transactionHash;
61876
62005
  }
@@ -61908,6 +62037,7 @@ class SigningCosmosClient extends CosmosClient {
61908
62037
  });
61909
62038
  try {
61910
62039
  const result = await this.signingClient.signAndBroadcast(creator, [msg], fee, memo);
62040
+ assertExecuted("process deposit", result);
61911
62041
  console.log("✅ Process deposit transaction successful:", result.transactionHash);
61912
62042
  return result.transactionHash;
61913
62043
  }
@@ -61947,6 +62077,7 @@ class SigningCosmosClient extends CosmosClient {
61947
62077
  });
61948
62078
  try {
61949
62079
  const result = await this.signingClient.signAndBroadcast(creator, [msg], fee, memo);
62080
+ assertExecuted("initiate withdrawal", result);
61950
62081
  console.log("✅ Withdrawal initiation successful:", result.transactionHash);
61951
62082
  return result.transactionHash;
61952
62083
  }
@@ -61983,6 +62114,7 @@ class SigningCosmosClient extends CosmosClient {
61983
62114
  });
61984
62115
  try {
61985
62116
  const result = await this.signingClient.signAndBroadcast(signer, [msg], fee, memo);
62117
+ assertExecuted("sign withdrawal", result);
61986
62118
  console.log("✅ Withdrawal signing successful:", result.transactionHash);
61987
62119
  return result.transactionHash;
61988
62120
  }
@@ -62063,6 +62195,7 @@ class SigningCosmosClient extends CosmosClient {
62063
62195
  });
62064
62196
  try {
62065
62197
  const result = await this.signingClient.signAndBroadcast(creator, [msg], fee, memo);
62198
+ assertExecuted("register NFT avatar", result);
62066
62199
  console.log("✅ NFT avatar registration successful:", result.transactionHash);
62067
62200
  return result.transactionHash;
62068
62201
  }
@@ -62263,8 +62396,10 @@ exports.SIT_IN_METHOD_POST_NOW = SIT_IN_METHOD_POST_NOW;
62263
62396
  exports.SIT_OUT_METHOD_NEXT_BB = SIT_OUT_METHOD_NEXT_BB;
62264
62397
  exports.SIT_OUT_METHOD_NEXT_HAND = SIT_OUT_METHOD_NEXT_HAND;
62265
62398
  exports.SigningCosmosClient = SigningCosmosClient;
62399
+ exports.TxExecutionError = TxExecutionError;
62266
62400
  exports.ValidatorBond = ValidatorBond;
62267
62401
  exports.WithdrawalRequest = WithdrawalRequest;
62402
+ exports.assertExecuted = assertExecuted;
62268
62403
  exports.computeGameNameFee = computeGameNameFee;
62269
62404
  exports.createSigningClientFromMnemonic = createSigningClientFromMnemonic;
62270
62405
  exports.createWalletFromMnemonic = createWalletFromMnemonic;
@@ -62288,4 +62423,6 @@ exports.isValidPlayerAddress = isValidPlayerAddress;
62288
62423
  exports.parseGameFormat = parseGameFormat;
62289
62424
  exports.parseGameVariant = parseGameVariant;
62290
62425
  exports.registry = registry;
62426
+ exports.txFailureHash = txFailureHash;
62427
+ exports.txFailurePhase = txFailurePhase;
62291
62428
  //# sourceMappingURL=index.js.map