@dimcool/dimclaw 0.1.29 → 0.1.31
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 +1213 -152
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -32839,6 +32839,16 @@ var Auth = class {
|
|
|
32839
32839
|
this.logger.debug("Token stored in storage", { tokenKey: TOKEN_KEY });
|
|
32840
32840
|
return response;
|
|
32841
32841
|
}
|
|
32842
|
+
async loginWithExternalSignature(address, signedMessage, options) {
|
|
32843
|
+
const response = await this.http.post("/auth/login-wallet", {
|
|
32844
|
+
signedMessage,
|
|
32845
|
+
address,
|
|
32846
|
+
referralCode: options?.referralCode,
|
|
32847
|
+
walletMeta: options?.walletMeta
|
|
32848
|
+
});
|
|
32849
|
+
this.storage.set(TOKEN_KEY, response.access_token);
|
|
32850
|
+
return response;
|
|
32851
|
+
}
|
|
32842
32852
|
logout() {
|
|
32843
32853
|
this.logger.debug("Auth.logout called");
|
|
32844
32854
|
const hadToken = this.storage.get(TOKEN_KEY) !== null;
|
|
@@ -32852,6 +32862,10 @@ var Auth = class {
|
|
|
32852
32862
|
});
|
|
32853
32863
|
return isAuth;
|
|
32854
32864
|
}
|
|
32865
|
+
async getSessionStats() {
|
|
32866
|
+
this.logger.debug("Auth.getSessionStats called");
|
|
32867
|
+
return this.http.get("/auth/sessions/stats");
|
|
32868
|
+
}
|
|
32855
32869
|
async getLatestSessions(limit) {
|
|
32856
32870
|
this.logger.debug("Auth.getLatestSessions called", { limit });
|
|
32857
32871
|
const params = new URLSearchParams();
|
|
@@ -32870,6 +32884,9 @@ var Admin = class {
|
|
|
32870
32884
|
this.http = http2;
|
|
32871
32885
|
this.logger = logger2;
|
|
32872
32886
|
}
|
|
32887
|
+
async getInternalBots() {
|
|
32888
|
+
return this.http.get("/admin/internal-bots");
|
|
32889
|
+
}
|
|
32873
32890
|
async getUserById(id) {
|
|
32874
32891
|
return this.http.get(`/admin/users/${id}`);
|
|
32875
32892
|
}
|
|
@@ -33075,11 +33092,25 @@ var Users = class {
|
|
|
33075
33092
|
async removeFriend(userId) {
|
|
33076
33093
|
return this.http.delete(`/friends/${userId}`);
|
|
33077
33094
|
}
|
|
33078
|
-
async getIncomingFriendRequests() {
|
|
33079
|
-
|
|
33095
|
+
async getIncomingFriendRequests(opts) {
|
|
33096
|
+
const params = new URLSearchParams();
|
|
33097
|
+
if (opts?.limit !== void 0)
|
|
33098
|
+
params.append("limit", opts.limit.toString());
|
|
33099
|
+
if (opts?.cursor !== void 0) params.append("cursor", opts.cursor);
|
|
33100
|
+
const qs = params.toString();
|
|
33101
|
+
return this.http.get(
|
|
33102
|
+
qs ? `/friends/requests/incoming?${qs}` : "/friends/requests/incoming"
|
|
33103
|
+
);
|
|
33080
33104
|
}
|
|
33081
|
-
async getOutgoingFriendRequests() {
|
|
33082
|
-
|
|
33105
|
+
async getOutgoingFriendRequests(opts) {
|
|
33106
|
+
const params = new URLSearchParams();
|
|
33107
|
+
if (opts?.limit !== void 0)
|
|
33108
|
+
params.append("limit", opts.limit.toString());
|
|
33109
|
+
if (opts?.cursor !== void 0) params.append("cursor", opts.cursor);
|
|
33110
|
+
const qs = params.toString();
|
|
33111
|
+
return this.http.get(
|
|
33112
|
+
qs ? `/friends/requests/outgoing?${qs}` : "/friends/requests/outgoing"
|
|
33113
|
+
);
|
|
33083
33114
|
}
|
|
33084
33115
|
async acceptFriendRequest(userId) {
|
|
33085
33116
|
return this.http.post(
|
|
@@ -33166,6 +33197,9 @@ var FeatureFlags = class {
|
|
|
33166
33197
|
this.loaded = true;
|
|
33167
33198
|
return this.flags;
|
|
33168
33199
|
}
|
|
33200
|
+
async getAdminFeatureFlags() {
|
|
33201
|
+
return this.http.get("/feature-flags/admin");
|
|
33202
|
+
}
|
|
33169
33203
|
isEnabledFlag(name) {
|
|
33170
33204
|
const flag = this.flags.find((f) => f.name === name);
|
|
33171
33205
|
return flag?.enabled ?? false;
|
|
@@ -33185,10 +33219,11 @@ var FeatureFlags = class {
|
|
|
33185
33219
|
}
|
|
33186
33220
|
return flag;
|
|
33187
33221
|
}
|
|
33188
|
-
async createFeatureFlag(name, enabled) {
|
|
33222
|
+
async createFeatureFlag(name, enabled, description) {
|
|
33189
33223
|
const flag = await this.http.post("/feature-flags", {
|
|
33190
33224
|
name,
|
|
33191
|
-
enabled
|
|
33225
|
+
enabled,
|
|
33226
|
+
...description !== void 0 && { description }
|
|
33192
33227
|
});
|
|
33193
33228
|
this.flags.push(flag);
|
|
33194
33229
|
return flag;
|
|
@@ -33199,11 +33234,17 @@ var Lobbies = class {
|
|
|
33199
33234
|
this.http = http2;
|
|
33200
33235
|
this.logger = logger2;
|
|
33201
33236
|
}
|
|
33237
|
+
/** Called by SDK after the store is created so mutations can update state directly. */
|
|
33238
|
+
setLobbyStore(store) {
|
|
33239
|
+
this.lobbyStore = store;
|
|
33240
|
+
}
|
|
33202
33241
|
async createLobby(gameType, betAmount) {
|
|
33203
33242
|
return this.http.post("/lobbies", { gameType, betAmount });
|
|
33204
33243
|
}
|
|
33205
33244
|
async getLobby(lobbyId) {
|
|
33206
|
-
|
|
33245
|
+
const lobby = await this.http.get(`/lobbies/${lobbyId}`);
|
|
33246
|
+
this.lobbyStore?.setBaseState([lobby]);
|
|
33247
|
+
return lobby;
|
|
33207
33248
|
}
|
|
33208
33249
|
async inviteFriend(lobbyId, friendId) {
|
|
33209
33250
|
return this.http.post(`/lobbies/${lobbyId}/invite`, {
|
|
@@ -33214,7 +33255,9 @@ var Lobbies = class {
|
|
|
33214
33255
|
return this.http.post(`/lobbies/${lobbyId}/accept-invite`, {});
|
|
33215
33256
|
}
|
|
33216
33257
|
async joinLobby(lobbyId) {
|
|
33217
|
-
|
|
33258
|
+
const lobby = await this.http.post(`/lobbies/${lobbyId}/join`, {});
|
|
33259
|
+
this.lobbyStore?.setBaseState([lobby]);
|
|
33260
|
+
return lobby;
|
|
33218
33261
|
}
|
|
33219
33262
|
async removePlayer(lobbyId, userId) {
|
|
33220
33263
|
return this.http.delete(
|
|
@@ -33231,12 +33274,17 @@ var Lobbies = class {
|
|
|
33231
33274
|
return this.http.post(`/lobbies/${lobbyId}/join-queue`, {});
|
|
33232
33275
|
}
|
|
33233
33276
|
async cancelQueue(lobbyId) {
|
|
33234
|
-
|
|
33277
|
+
const lobby = await this.http.delete(`/lobbies/${lobbyId}/queue`);
|
|
33278
|
+
this.lobbyStore?.setBaseState([lobby]);
|
|
33279
|
+
return lobby;
|
|
33235
33280
|
}
|
|
33236
33281
|
async updateBetAmount(lobbyId, betAmount) {
|
|
33237
|
-
|
|
33238
|
-
|
|
33239
|
-
|
|
33282
|
+
const lobby = await this.http.patch(
|
|
33283
|
+
`/lobbies/${lobbyId}/bet-amount`,
|
|
33284
|
+
{ betAmount }
|
|
33285
|
+
);
|
|
33286
|
+
this.lobbyStore?.setBaseState([lobby]);
|
|
33287
|
+
return lobby;
|
|
33240
33288
|
}
|
|
33241
33289
|
async playSound(lobbyId, sound) {
|
|
33242
33290
|
return this.http.post(`/lobbies/${lobbyId}/sound`, {
|
|
@@ -33275,6 +33323,12 @@ var Games = class {
|
|
|
33275
33323
|
this.wallet = wallet;
|
|
33276
33324
|
this.logger = logger2;
|
|
33277
33325
|
}
|
|
33326
|
+
setGameStore(store) {
|
|
33327
|
+
this.gameStore = store;
|
|
33328
|
+
}
|
|
33329
|
+
setGameActionsStore(store) {
|
|
33330
|
+
this.gameActionsStore = store;
|
|
33331
|
+
}
|
|
33278
33332
|
async getAvailableGames() {
|
|
33279
33333
|
return this.http.get("/games/available");
|
|
33280
33334
|
}
|
|
@@ -33287,7 +33341,11 @@ var Games = class {
|
|
|
33287
33341
|
return this.http.get("/games/metrics");
|
|
33288
33342
|
}
|
|
33289
33343
|
async getGame(gameId) {
|
|
33290
|
-
|
|
33344
|
+
const existing = this.gameStore?.store.getState().gamesById[gameId];
|
|
33345
|
+
if (existing?.status === "completed") return existing;
|
|
33346
|
+
const game = await this.http.get(`/games/${gameId}`);
|
|
33347
|
+
this.gameStore?.setBaseState([game]);
|
|
33348
|
+
return game;
|
|
33291
33349
|
}
|
|
33292
33350
|
/**
|
|
33293
33351
|
* Get list of currently active (live) games. Public endpoint for spectating.
|
|
@@ -33314,7 +33372,13 @@ var Games = class {
|
|
|
33314
33372
|
* Get current game state with timer information
|
|
33315
33373
|
*/
|
|
33316
33374
|
async getGameState(gameId) {
|
|
33317
|
-
|
|
33375
|
+
const existing = this.gameActionsStore?.store.getState().statesByGameId[gameId];
|
|
33376
|
+
if (existing?.status === "completed") return existing;
|
|
33377
|
+
const state = await this.http.get(
|
|
33378
|
+
`/games/${gameId}/state`
|
|
33379
|
+
);
|
|
33380
|
+
this.gameActionsStore?.setBaseState(gameId, state);
|
|
33381
|
+
return state;
|
|
33318
33382
|
}
|
|
33319
33383
|
/**
|
|
33320
33384
|
* Request a rematch for a completed game
|
|
@@ -33350,8 +33414,18 @@ var Games = class {
|
|
|
33350
33414
|
{ amountMinor, signedTransaction }
|
|
33351
33415
|
);
|
|
33352
33416
|
}
|
|
33417
|
+
/**
|
|
33418
|
+
* Confirm a donation that was already sent by the client (signAndSendTransaction path).
|
|
33419
|
+
*/
|
|
33420
|
+
async confirmGameDonationSignature(gameId, amountMinor, signature) {
|
|
33421
|
+
return this.http.post(
|
|
33422
|
+
`/games/${gameId}/donate/confirm-signature`,
|
|
33423
|
+
{ amountMinor, signature }
|
|
33424
|
+
);
|
|
33425
|
+
}
|
|
33353
33426
|
/**
|
|
33354
33427
|
* One-call donation flow: prepare -> sign -> submit.
|
|
33428
|
+
* Automatically uses the right signing path (embedded vs injected wallet).
|
|
33355
33429
|
*/
|
|
33356
33430
|
async sendDonation(gameId, amountMinor) {
|
|
33357
33431
|
if (!this.wallet.hasSigner()) {
|
|
@@ -33360,16 +33434,10 @@ var Games = class {
|
|
|
33360
33434
|
);
|
|
33361
33435
|
}
|
|
33362
33436
|
const prepared = await this.prepareGameDonation(gameId, amountMinor);
|
|
33363
|
-
const
|
|
33364
|
-
|
|
33365
|
-
|
|
33366
|
-
|
|
33367
|
-
);
|
|
33368
|
-
const result = await this.donateToGame(
|
|
33369
|
-
gameId,
|
|
33370
|
-
amountMinor,
|
|
33371
|
-
signedTransaction
|
|
33372
|
-
);
|
|
33437
|
+
const result = await this.wallet.signAndDispatch(prepared.transaction, {
|
|
33438
|
+
onSigned: (signedTxBase64) => this.donateToGame(gameId, amountMinor, signedTxBase64),
|
|
33439
|
+
onSignedAndSent: (sig) => this.confirmGameDonationSignature(gameId, amountMinor, sig)
|
|
33440
|
+
});
|
|
33373
33441
|
return {
|
|
33374
33442
|
...result,
|
|
33375
33443
|
escrowAddress: prepared.escrowAddress,
|
|
@@ -33462,6 +33530,9 @@ var Chat = class {
|
|
|
33462
33530
|
this.logger = logger2;
|
|
33463
33531
|
this.retryOptions = { ...DEFAULT_RETRY_OPTIONS, ...retryOptions };
|
|
33464
33532
|
}
|
|
33533
|
+
setDmThreadsStore(store) {
|
|
33534
|
+
this.dmThreadsStore = store;
|
|
33535
|
+
}
|
|
33465
33536
|
encodeContextId(id) {
|
|
33466
33537
|
return encodeURIComponent(id);
|
|
33467
33538
|
}
|
|
@@ -33543,7 +33614,9 @@ var Chat = class {
|
|
|
33543
33614
|
return response;
|
|
33544
33615
|
}
|
|
33545
33616
|
async listDmThreads() {
|
|
33546
|
-
|
|
33617
|
+
const threads = await this.http.get("/chat/dm/threads");
|
|
33618
|
+
this.dmThreadsStore?.setBaseState(threads);
|
|
33619
|
+
return threads;
|
|
33547
33620
|
}
|
|
33548
33621
|
async getDmThread(dmKey) {
|
|
33549
33622
|
return this.http.get(
|
|
@@ -33631,21 +33704,32 @@ var Tips = class {
|
|
|
33631
33704
|
);
|
|
33632
33705
|
}
|
|
33633
33706
|
const { publicKey: senderAddress } = await this.wallet.loadWallet();
|
|
33634
|
-
const
|
|
33635
|
-
const
|
|
33636
|
-
|
|
33637
|
-
|
|
33638
|
-
|
|
33639
|
-
);
|
|
33640
|
-
const transfer = await this.wallet.
|
|
33641
|
-
signedTxBase64
|
|
33642
|
-
|
|
33643
|
-
|
|
33644
|
-
|
|
33645
|
-
|
|
33646
|
-
|
|
33647
|
-
|
|
33648
|
-
|
|
33707
|
+
const supportsPresign = !this.wallet.isSignAndSendMode();
|
|
33708
|
+
const prepared = await this.prepare({
|
|
33709
|
+
recipientUsername,
|
|
33710
|
+
amount,
|
|
33711
|
+
supportsPresign
|
|
33712
|
+
});
|
|
33713
|
+
const transfer = await this.wallet.signAndDispatch(prepared.transaction, {
|
|
33714
|
+
onSigned: (signedTxBase64) => this.wallet.submitTransfer(
|
|
33715
|
+
signedTxBase64,
|
|
33716
|
+
senderAddress,
|
|
33717
|
+
prepared.recipientAddress,
|
|
33718
|
+
prepared.amount,
|
|
33719
|
+
"USDC",
|
|
33720
|
+
false,
|
|
33721
|
+
prepared.recipientUsername
|
|
33722
|
+
),
|
|
33723
|
+
onSignedAndSent: (sig) => this.wallet.confirmTransferSignature(
|
|
33724
|
+
sig,
|
|
33725
|
+
senderAddress,
|
|
33726
|
+
prepared.recipientAddress,
|
|
33727
|
+
prepared.amount,
|
|
33728
|
+
"USDC",
|
|
33729
|
+
false,
|
|
33730
|
+
prepared.recipientUsername
|
|
33731
|
+
)
|
|
33732
|
+
});
|
|
33649
33733
|
const message = await this.chat.broadcastGlobalTip(
|
|
33650
33734
|
prepared.recipientUserId,
|
|
33651
33735
|
prepared.amount
|
|
@@ -33963,13 +34047,15 @@ var Wallet = class {
|
|
|
33963
34047
|
);
|
|
33964
34048
|
}
|
|
33965
34049
|
try {
|
|
34050
|
+
const supportsPresign = !this.isSignAndSendMode();
|
|
33966
34051
|
const response = await this.http.post(
|
|
33967
34052
|
"/wallets/transfer/prepare",
|
|
33968
34053
|
{
|
|
33969
34054
|
senderAddress,
|
|
33970
34055
|
recipient,
|
|
33971
34056
|
amount: amountMinor,
|
|
33972
|
-
token
|
|
34057
|
+
token,
|
|
34058
|
+
supportsPresign
|
|
33973
34059
|
}
|
|
33974
34060
|
);
|
|
33975
34061
|
this.logger.debug("Transfer prepared", {
|
|
@@ -34053,6 +34139,29 @@ var Wallet = class {
|
|
|
34053
34139
|
throw error;
|
|
34054
34140
|
}
|
|
34055
34141
|
}
|
|
34142
|
+
/**
|
|
34143
|
+
* Sign a prepared transaction and invoke the appropriate backend callback
|
|
34144
|
+
* based on the configured signer — without exposing the mode decision to callers.
|
|
34145
|
+
* - Embedded wallet (signAndSend): signs+sends, calls onSignedAndSent(signature)
|
|
34146
|
+
* - Injected wallet (signTransaction): signs locally, calls onSigned(signedTxBase64)
|
|
34147
|
+
*/
|
|
34148
|
+
async signAndDispatch(unsignedTxBase64, handlers) {
|
|
34149
|
+
if (!this.signer) {
|
|
34150
|
+
throw new Error(
|
|
34151
|
+
"No signer configured. Call setSigner() with a WalletSigner implementation first."
|
|
34152
|
+
);
|
|
34153
|
+
}
|
|
34154
|
+
const unsignedTx = Transaction.from(base64ToBytes(unsignedTxBase64));
|
|
34155
|
+
if (this.isSignAndSendMode()) {
|
|
34156
|
+
const sig = await this.signAndSendTransaction(unsignedTx);
|
|
34157
|
+
return handlers.onSignedAndSent(sig);
|
|
34158
|
+
}
|
|
34159
|
+
const signedTx = await this.signTransaction(unsignedTx);
|
|
34160
|
+
const signedBase64 = bytesToBase64(
|
|
34161
|
+
signedTx.serialize({ requireAllSignatures: false })
|
|
34162
|
+
);
|
|
34163
|
+
return handlers.onSigned(signedBase64);
|
|
34164
|
+
}
|
|
34056
34165
|
/**
|
|
34057
34166
|
* Full transfer flow in one call: prepare -> sign -> submit
|
|
34058
34167
|
* Recipient can be username, .sol domain, or Solana address (resolved by backend).
|
|
@@ -34134,12 +34243,32 @@ var Escrow = class {
|
|
|
34134
34243
|
* initializes depositStatus, and prepares the unsigned transaction in one call.
|
|
34135
34244
|
* Eliminates one HTTP round-trip vs startDeposits() + prepareDepositTransaction().
|
|
34136
34245
|
*/
|
|
34137
|
-
async prepareAndStartDeposit(lobbyId) {
|
|
34246
|
+
async prepareAndStartDeposit(lobbyId, supportsPresign) {
|
|
34247
|
+
const presign = supportsPresign ?? !this.wallet.isSignAndSendMode();
|
|
34138
34248
|
return this.http.post(
|
|
34139
34249
|
`/escrow/lobby/${lobbyId}/deposit/prepare-and-start`,
|
|
34140
|
-
{}
|
|
34250
|
+
{ supportsPresign: presign }
|
|
34141
34251
|
);
|
|
34142
34252
|
}
|
|
34253
|
+
/**
|
|
34254
|
+
* Sign and submit a prepared (unsigned) deposit transaction using the
|
|
34255
|
+
* configured signer. Automatically picks the right path:
|
|
34256
|
+
* - Embedded wallet: signAndSendTransaction → confirmDepositSignature
|
|
34257
|
+
* - Injected wallet: signTransaction → submitDeposit
|
|
34258
|
+
* Returns the on-chain signature.
|
|
34259
|
+
*/
|
|
34260
|
+
async signAndSubmitPreparedDeposit(lobbyId, unsignedTxBase64) {
|
|
34261
|
+
return this.wallet.signAndDispatch(unsignedTxBase64, {
|
|
34262
|
+
onSigned: async (signedTxBase64) => {
|
|
34263
|
+
const result = await this.submitDeposit(lobbyId, signedTxBase64);
|
|
34264
|
+
return result.signature;
|
|
34265
|
+
},
|
|
34266
|
+
onSignedAndSent: async (signature) => {
|
|
34267
|
+
await this.confirmDepositSignature(lobbyId, signature);
|
|
34268
|
+
return signature;
|
|
34269
|
+
}
|
|
34270
|
+
});
|
|
34271
|
+
}
|
|
34143
34272
|
/**
|
|
34144
34273
|
* Submit a signed deposit transaction
|
|
34145
34274
|
* The transaction will be submitted to the Solana network and confirmed
|
|
@@ -34182,7 +34311,11 @@ var Escrow = class {
|
|
|
34182
34311
|
"No signer configured. Use sdk.wallet.setSigner(...) first."
|
|
34183
34312
|
);
|
|
34184
34313
|
}
|
|
34185
|
-
const
|
|
34314
|
+
const supportsPresign = !this.wallet.isSignAndSendMode();
|
|
34315
|
+
const { transaction } = await this.prepareAndStartDeposit(
|
|
34316
|
+
lobbyId,
|
|
34317
|
+
supportsPresign
|
|
34318
|
+
);
|
|
34186
34319
|
const unsignedTx = Transaction.from(base64ToBytes(transaction));
|
|
34187
34320
|
let signature;
|
|
34188
34321
|
if (this.wallet.isSignAndSendMode()) {
|
|
@@ -34217,8 +34350,13 @@ var Escrow = class {
|
|
|
34217
34350
|
};
|
|
34218
34351
|
}
|
|
34219
34352
|
/**
|
|
34220
|
-
*
|
|
34221
|
-
*
|
|
34353
|
+
* Deposit for a lobby and wait until the calling user's own deposit is confirmed.
|
|
34354
|
+
* Ideal for agents: returns as soon as your deposit is on-chain without waiting
|
|
34355
|
+
* for the other player. The server auto-joins the queue when all players deposit.
|
|
34356
|
+
*
|
|
34357
|
+
* Confirmation is handled asynchronously by the transaction queue processor.
|
|
34358
|
+
* This method polls the deposit status endpoint until the signature appears as
|
|
34359
|
+
* confirmed (up to 60 seconds).
|
|
34222
34360
|
*
|
|
34223
34361
|
* Automatically uses signAndSendTransaction or signTransaction based on signer capability.
|
|
34224
34362
|
*/
|
|
@@ -34228,26 +34366,45 @@ var Escrow = class {
|
|
|
34228
34366
|
"No signer configured. Use sdk.wallet.setSigner(...) first."
|
|
34229
34367
|
);
|
|
34230
34368
|
}
|
|
34231
|
-
const
|
|
34369
|
+
const supportsPresign2 = !this.wallet.isSignAndSendMode();
|
|
34370
|
+
const { transaction } = await this.prepareAndStartDeposit(
|
|
34371
|
+
lobbyId,
|
|
34372
|
+
supportsPresign2
|
|
34373
|
+
);
|
|
34232
34374
|
const unsignedTx = Transaction.from(base64ToBytes(transaction));
|
|
34233
34375
|
let signature;
|
|
34234
34376
|
if (this.wallet.isSignAndSendMode()) {
|
|
34235
34377
|
signature = await this.wallet.signAndSendTransaction(unsignedTx);
|
|
34236
|
-
await this.
|
|
34237
|
-
`/escrow/lobby/${lobbyId}/deposit/confirm-signature?awaitConfirmation=true`,
|
|
34238
|
-
{ signature }
|
|
34239
|
-
);
|
|
34378
|
+
await this.confirmDepositSignature(lobbyId, signature);
|
|
34240
34379
|
} else {
|
|
34241
34380
|
const signedTx = await this.wallet.signTransaction(unsignedTx);
|
|
34242
34381
|
const signedBase64 = bytesToBase64(
|
|
34243
34382
|
signedTx.serialize({ requireAllSignatures: false })
|
|
34244
34383
|
);
|
|
34245
|
-
const
|
|
34246
|
-
|
|
34247
|
-
|
|
34248
|
-
|
|
34384
|
+
const submitResult = await this.submitDeposit(lobbyId, signedBase64);
|
|
34385
|
+
signature = submitResult.signature;
|
|
34386
|
+
}
|
|
34387
|
+
const MAX_WAIT_MS = 6e4;
|
|
34388
|
+
const POLL_INTERVAL_MS = 1e3;
|
|
34389
|
+
const startTime = Date.now();
|
|
34390
|
+
while (Date.now() - startTime < MAX_WAIT_MS) {
|
|
34391
|
+
const status = await this.getDepositStatus(lobbyId);
|
|
34392
|
+
const myDeposit = status.deposits.find(
|
|
34393
|
+
(d) => d.transactionHash === signature
|
|
34394
|
+
);
|
|
34395
|
+
if (myDeposit?.status === "confirmed") {
|
|
34396
|
+
return {
|
|
34397
|
+
signature,
|
|
34398
|
+
status: "confirmed",
|
|
34399
|
+
canProceedToQueue: status.canProceedToQueue
|
|
34400
|
+
};
|
|
34401
|
+
}
|
|
34402
|
+
if (status.allConfirmed && status.canProceedToQueue) {
|
|
34403
|
+
return { signature, status: "confirmed", canProceedToQueue: true };
|
|
34404
|
+
}
|
|
34405
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
34249
34406
|
}
|
|
34250
|
-
return { signature, status: "
|
|
34407
|
+
return { signature, status: "pending", canProceedToQueue: false };
|
|
34251
34408
|
}
|
|
34252
34409
|
async claimLobbyDepositRefund(lobbyId, depositSignature) {
|
|
34253
34410
|
return this.http.post(
|
|
@@ -34589,9 +34746,10 @@ var Support = class {
|
|
|
34589
34746
|
}
|
|
34590
34747
|
};
|
|
34591
34748
|
var Markets = class {
|
|
34592
|
-
constructor(http2, logger2) {
|
|
34749
|
+
constructor(http2, logger2, wallet) {
|
|
34593
34750
|
this.http = http2;
|
|
34594
34751
|
this.logger = logger2;
|
|
34752
|
+
this.wallet = wallet;
|
|
34595
34753
|
}
|
|
34596
34754
|
/**
|
|
34597
34755
|
* Get the prediction market state for a game.
|
|
@@ -34633,6 +34791,35 @@ var Markets = class {
|
|
|
34633
34791
|
{ signedTransaction, outcomeId, amountMinor }
|
|
34634
34792
|
);
|
|
34635
34793
|
}
|
|
34794
|
+
/**
|
|
34795
|
+
* Confirm a buy order that was already broadcast by the client (signAndSendTransaction path).
|
|
34796
|
+
*/
|
|
34797
|
+
async confirmBuyOrderSignature(gameId, depositSignature, outcomeId, amountMinor) {
|
|
34798
|
+
return this.http.post(
|
|
34799
|
+
`/games/${gameId}/market/orders/buy/confirm-signature`,
|
|
34800
|
+
{ depositSignature, outcomeId, amountMinor }
|
|
34801
|
+
);
|
|
34802
|
+
}
|
|
34803
|
+
/**
|
|
34804
|
+
* One-call buy order: prepare → sign → submit, automatically choosing
|
|
34805
|
+
* the right signing path (embedded vs injected wallet).
|
|
34806
|
+
*/
|
|
34807
|
+
async buy(gameId, outcomeId, amountMinor) {
|
|
34808
|
+
if (!this.wallet?.hasSigner()) {
|
|
34809
|
+
throw new Error(
|
|
34810
|
+
"No signer configured. Use sdk.wallet.setSigner(...) first."
|
|
34811
|
+
);
|
|
34812
|
+
}
|
|
34813
|
+
const { transaction } = await this.prepareBuyOrder(
|
|
34814
|
+
gameId,
|
|
34815
|
+
outcomeId,
|
|
34816
|
+
amountMinor
|
|
34817
|
+
);
|
|
34818
|
+
return this.wallet.signAndDispatch(transaction, {
|
|
34819
|
+
onSigned: (signedTxBase64) => this.submitBuyOrder(gameId, signedTxBase64, outcomeId, amountMinor),
|
|
34820
|
+
onSignedAndSent: (sig) => this.confirmBuyOrderSignature(gameId, sig, outcomeId, amountMinor)
|
|
34821
|
+
});
|
|
34822
|
+
}
|
|
34636
34823
|
/**
|
|
34637
34824
|
* Sell shares back to the AMM pool.
|
|
34638
34825
|
* @param gameId - The game ID
|
|
@@ -34681,6 +34868,20 @@ var Markets = class {
|
|
|
34681
34868
|
return this.http.get(`/games/admin/markets${qs ? `?${qs}` : ""}`);
|
|
34682
34869
|
}
|
|
34683
34870
|
};
|
|
34871
|
+
var NoopAnalyticsClient = class {
|
|
34872
|
+
userLoggedIn(_user, _meta) {
|
|
34873
|
+
}
|
|
34874
|
+
userLoggedOut() {
|
|
34875
|
+
}
|
|
34876
|
+
sessionRestored(_user) {
|
|
34877
|
+
}
|
|
34878
|
+
track(_event, _properties) {
|
|
34879
|
+
}
|
|
34880
|
+
setUserProperties(_properties) {
|
|
34881
|
+
}
|
|
34882
|
+
group(_groupType, _groupKey, _properties) {
|
|
34883
|
+
}
|
|
34884
|
+
};
|
|
34684
34885
|
var BaseWsTransport = class {
|
|
34685
34886
|
constructor() {
|
|
34686
34887
|
this.roomRefs = /* @__PURE__ */ new Map();
|
|
@@ -34768,21 +34969,35 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
|
|
|
34768
34969
|
this.accessToken = null;
|
|
34769
34970
|
this.reconnectAttempts = 0;
|
|
34770
34971
|
this.reconnectTimer = null;
|
|
34771
|
-
this.
|
|
34772
|
-
this.
|
|
34773
|
-
this.reconnectDelay = 1e3;
|
|
34774
|
-
this.reconnectDelayMax = 3e4;
|
|
34972
|
+
this.needsReconnect = false;
|
|
34973
|
+
this.visibilityHandler = null;
|
|
34775
34974
|
this.wildcardHandlers = /* @__PURE__ */ new Set();
|
|
34776
34975
|
this.eventHandlers = /* @__PURE__ */ new Map();
|
|
34777
34976
|
this.registeredEvents = /* @__PURE__ */ new Set();
|
|
34778
34977
|
this.wildcardRegistered = false;
|
|
34779
34978
|
this.url = url3;
|
|
34979
|
+
if (typeof document !== "undefined") {
|
|
34980
|
+
this.visibilityHandler = () => {
|
|
34981
|
+
if (document.visibilityState === "visible" && !this.connectionState.connected) {
|
|
34982
|
+
this.reconnectImmediately();
|
|
34983
|
+
}
|
|
34984
|
+
};
|
|
34985
|
+
document.addEventListener("visibilitychange", this.visibilityHandler);
|
|
34986
|
+
}
|
|
34780
34987
|
}
|
|
34781
34988
|
connect() {
|
|
34989
|
+
if (this.reconnectTimer !== null) {
|
|
34990
|
+
clearTimeout(this.reconnectTimer);
|
|
34991
|
+
this.reconnectTimer = null;
|
|
34992
|
+
}
|
|
34993
|
+
this.reconnectAttempts = 0;
|
|
34782
34994
|
this.ensureSocket();
|
|
34783
34995
|
}
|
|
34784
34996
|
disconnect() {
|
|
34785
|
-
this.
|
|
34997
|
+
if (this.visibilityHandler && typeof document !== "undefined") {
|
|
34998
|
+
document.removeEventListener("visibilitychange", this.visibilityHandler);
|
|
34999
|
+
this.visibilityHandler = null;
|
|
35000
|
+
}
|
|
34786
35001
|
if (!this.socket) return;
|
|
34787
35002
|
this.socket.disconnect();
|
|
34788
35003
|
this.socket = null;
|
|
@@ -34881,13 +35096,13 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
|
|
|
34881
35096
|
reconnection: false
|
|
34882
35097
|
});
|
|
34883
35098
|
socket.on("connect", () => {
|
|
34884
|
-
this.clearPeriodicReconnect();
|
|
34885
35099
|
this.updateConnectionState({
|
|
34886
35100
|
connected: true,
|
|
34887
35101
|
connecting: false,
|
|
34888
35102
|
error: null
|
|
34889
35103
|
});
|
|
34890
|
-
const wasReconnect = this.
|
|
35104
|
+
const wasReconnect = this.needsReconnect;
|
|
35105
|
+
this.needsReconnect = false;
|
|
34891
35106
|
this.reconnectAttempts = 0;
|
|
34892
35107
|
this.onReconnect();
|
|
34893
35108
|
if (wasReconnect) {
|
|
@@ -34898,6 +35113,7 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
|
|
|
34898
35113
|
});
|
|
34899
35114
|
socket.on("disconnect", (reason) => {
|
|
34900
35115
|
this.updateConnectionState({ connected: false, connecting: false });
|
|
35116
|
+
this.needsReconnect = true;
|
|
34901
35117
|
this.clearSocketForReconnect();
|
|
34902
35118
|
if (reason === "io server disconnect") {
|
|
34903
35119
|
this.handleAuthFailure("io server disconnect");
|
|
@@ -34907,6 +35123,7 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
|
|
|
34907
35123
|
});
|
|
34908
35124
|
socket.on("connect_error", (error) => {
|
|
34909
35125
|
const message = error?.message || "connect_error";
|
|
35126
|
+
this.needsReconnect = true;
|
|
34910
35127
|
this.clearSocketForReconnect();
|
|
34911
35128
|
if (message.includes("unauthorized") || message.includes("401")) {
|
|
34912
35129
|
this.handleAuthFailure(message);
|
|
@@ -34934,31 +35151,31 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
|
|
|
34934
35151
|
this.socket.removeAllListeners();
|
|
34935
35152
|
this.socket = null;
|
|
34936
35153
|
}
|
|
34937
|
-
|
|
34938
|
-
if (this.
|
|
34939
|
-
|
|
34940
|
-
this.
|
|
35154
|
+
reconnectImmediately() {
|
|
35155
|
+
if (this.reconnectTimer !== null) {
|
|
35156
|
+
clearTimeout(this.reconnectTimer);
|
|
35157
|
+
this.reconnectTimer = null;
|
|
34941
35158
|
}
|
|
34942
|
-
|
|
34943
|
-
|
|
34944
|
-
if (this.periodicReconnectInterval !== null) return;
|
|
34945
|
-
this.periodicReconnectInterval = setInterval(() => {
|
|
34946
|
-
if (this.connectionState.connected) return;
|
|
34947
|
-
this.reconnectAttempts = 0;
|
|
34948
|
-
this.ensureSocket();
|
|
34949
|
-
}, _StandaloneWsTransport2.PERIODIC_RECONNECT_MS);
|
|
35159
|
+
this.reconnectAttempts = 0;
|
|
35160
|
+
this.ensureSocket();
|
|
34950
35161
|
}
|
|
34951
35162
|
scheduleReconnect() {
|
|
34952
35163
|
if (this.reconnectTimer !== null) return;
|
|
34953
|
-
|
|
34954
|
-
this.startPeriodicReconnect();
|
|
34955
|
-
return;
|
|
34956
|
-
}
|
|
34957
|
-
const delay = Math.min(
|
|
34958
|
-
this.reconnectDelay * Math.pow(2, this.reconnectAttempts),
|
|
34959
|
-
this.reconnectDelayMax
|
|
34960
|
-
);
|
|
35164
|
+
const attempt = this.reconnectAttempts;
|
|
34961
35165
|
this.reconnectAttempts += 1;
|
|
35166
|
+
let delay;
|
|
35167
|
+
if (attempt < _StandaloneWsTransport2.FAST_RETRY_LIMIT) {
|
|
35168
|
+
delay = _StandaloneWsTransport2.FAST_RETRY_DELAY_MS;
|
|
35169
|
+
} else {
|
|
35170
|
+
const backoffAttempt = attempt - _StandaloneWsTransport2.FAST_RETRY_LIMIT;
|
|
35171
|
+
delay = Math.min(
|
|
35172
|
+
_StandaloneWsTransport2.FAST_RETRY_DELAY_MS * Math.pow(2, backoffAttempt),
|
|
35173
|
+
_StandaloneWsTransport2.MAX_BACKOFF_MS
|
|
35174
|
+
);
|
|
35175
|
+
if (attempt === _StandaloneWsTransport2.FAST_RETRY_LIMIT) {
|
|
35176
|
+
this.dispatchEvent("connection:slow-retry", { timestamp: Date.now() });
|
|
35177
|
+
}
|
|
35178
|
+
}
|
|
34962
35179
|
this.reconnectTimer = setTimeout(() => {
|
|
34963
35180
|
this.reconnectTimer = null;
|
|
34964
35181
|
this.ensureSocket();
|
|
@@ -35019,7 +35236,9 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
|
|
|
35019
35236
|
this.wildcardRegistered = true;
|
|
35020
35237
|
}
|
|
35021
35238
|
};
|
|
35022
|
-
_StandaloneWsTransport.
|
|
35239
|
+
_StandaloneWsTransport.FAST_RETRY_LIMIT = 60;
|
|
35240
|
+
_StandaloneWsTransport.FAST_RETRY_DELAY_MS = 1e3;
|
|
35241
|
+
_StandaloneWsTransport.MAX_BACKOFF_MS = 3e4;
|
|
35023
35242
|
var StandaloneWsTransport = _StandaloneWsTransport;
|
|
35024
35243
|
function createSdkStore(initial) {
|
|
35025
35244
|
const store = createStore()(subscribeWithSelector(() => initial));
|
|
@@ -35040,14 +35259,16 @@ function createLobbyStore(transport) {
|
|
|
35040
35259
|
depositStatusByLobbyId: {}
|
|
35041
35260
|
});
|
|
35042
35261
|
const setBaseState = (lobbies) => {
|
|
35043
|
-
|
|
35044
|
-
|
|
35045
|
-
|
|
35046
|
-
|
|
35047
|
-
|
|
35048
|
-
|
|
35049
|
-
|
|
35050
|
-
|
|
35262
|
+
store.updateState((state) => {
|
|
35263
|
+
const lobbiesById = { ...state.lobbiesById };
|
|
35264
|
+
for (const lobby of lobbies) {
|
|
35265
|
+
const existing = lobbiesById[lobby.id];
|
|
35266
|
+
if (!existing || lobby.updatedAt >= existing.updatedAt) {
|
|
35267
|
+
lobbiesById[lobby.id] = lobby;
|
|
35268
|
+
}
|
|
35269
|
+
}
|
|
35270
|
+
return { ...state, lobbiesById };
|
|
35271
|
+
});
|
|
35051
35272
|
};
|
|
35052
35273
|
const applyWsEvent = (event) => {
|
|
35053
35274
|
switch (event.event) {
|
|
@@ -35177,12 +35398,23 @@ function createGameStore(transport) {
|
|
|
35177
35398
|
gamesById: {},
|
|
35178
35399
|
spectatorCounts: {}
|
|
35179
35400
|
});
|
|
35401
|
+
const STATUS_RANK = {
|
|
35402
|
+
active: 0,
|
|
35403
|
+
completed: 1,
|
|
35404
|
+
abandoned: 1
|
|
35405
|
+
};
|
|
35180
35406
|
const setBaseState = (games) => {
|
|
35181
|
-
|
|
35182
|
-
|
|
35183
|
-
|
|
35184
|
-
|
|
35185
|
-
|
|
35407
|
+
store.updateState((state) => {
|
|
35408
|
+
const gamesById = { ...state.gamesById };
|
|
35409
|
+
for (const game of games) {
|
|
35410
|
+
const existing = gamesById[game.gameId];
|
|
35411
|
+
if (existing && STATUS_RANK[existing.status] > STATUS_RANK[game.status]) {
|
|
35412
|
+
continue;
|
|
35413
|
+
}
|
|
35414
|
+
gamesById[game.gameId] = game;
|
|
35415
|
+
}
|
|
35416
|
+
return { ...state, gamesById };
|
|
35417
|
+
});
|
|
35186
35418
|
};
|
|
35187
35419
|
const applyWsEvent = (event) => {
|
|
35188
35420
|
switch (event.event) {
|
|
@@ -35278,8 +35510,21 @@ function createGameActionsStore(transport) {
|
|
|
35278
35510
|
});
|
|
35279
35511
|
};
|
|
35280
35512
|
const isNonRpsState = (state) => Boolean(state && !isRpsState(state));
|
|
35513
|
+
const pendingEvents = /* @__PURE__ */ new Map();
|
|
35514
|
+
function enqueue(gameId, event) {
|
|
35515
|
+
const q = pendingEvents.get(gameId) ?? [];
|
|
35516
|
+
q.push(event);
|
|
35517
|
+
pendingEvents.set(gameId, q);
|
|
35518
|
+
}
|
|
35519
|
+
function drainQueue(gameId) {
|
|
35520
|
+
const q = pendingEvents.get(gameId);
|
|
35521
|
+
if (!q?.length) return;
|
|
35522
|
+
pendingEvents.delete(gameId);
|
|
35523
|
+
for (const ev of q) applyWsEvent(ev);
|
|
35524
|
+
}
|
|
35281
35525
|
const setBaseState = (gameId, state) => {
|
|
35282
35526
|
updateState(gameId, state);
|
|
35527
|
+
drainQueue(gameId);
|
|
35283
35528
|
};
|
|
35284
35529
|
const clearState = (gameId) => {
|
|
35285
35530
|
store.updateState((state) => {
|
|
@@ -35289,6 +35534,7 @@ function createGameActionsStore(transport) {
|
|
|
35289
35534
|
const { [gameId]: _, ...rest } = state.statesByGameId;
|
|
35290
35535
|
return { ...state, statesByGameId: rest };
|
|
35291
35536
|
});
|
|
35537
|
+
pendingEvents.delete(gameId);
|
|
35292
35538
|
};
|
|
35293
35539
|
const applyWsEvent = (event) => {
|
|
35294
35540
|
switch (event.event) {
|
|
@@ -35307,7 +35553,10 @@ function createGameActionsStore(transport) {
|
|
|
35307
35553
|
}
|
|
35308
35554
|
case "game:rps:starting": {
|
|
35309
35555
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
35310
|
-
if (!current || !isRpsState(current))
|
|
35556
|
+
if (!current || !isRpsState(current)) {
|
|
35557
|
+
enqueue(event.payload.gameId, event);
|
|
35558
|
+
return;
|
|
35559
|
+
}
|
|
35311
35560
|
const betAmount = typeof event.payload.betAmount === "number" ? event.payload.betAmount : void 0;
|
|
35312
35561
|
const startedAt = typeof event.payload.startedAt === "string" ? event.payload.startedAt : current.roundState.startedAt;
|
|
35313
35562
|
const bufferEndsAt = typeof event.payload.bufferEndsAt === "string" ? event.payload.bufferEndsAt : current.roundState.selectionEndsAt;
|
|
@@ -35328,7 +35577,10 @@ function createGameActionsStore(transport) {
|
|
|
35328
35577
|
}
|
|
35329
35578
|
case "game:rps:round:started": {
|
|
35330
35579
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
35331
|
-
if (!current || !isRpsState(current))
|
|
35580
|
+
if (!current || !isRpsState(current)) {
|
|
35581
|
+
enqueue(event.payload.gameId, event);
|
|
35582
|
+
return;
|
|
35583
|
+
}
|
|
35332
35584
|
const actions = {};
|
|
35333
35585
|
const baseUsers = /* @__PURE__ */ new Set();
|
|
35334
35586
|
Object.keys(current.roundState.actions).forEach(
|
|
@@ -35362,7 +35614,10 @@ function createGameActionsStore(transport) {
|
|
|
35362
35614
|
}
|
|
35363
35615
|
case "game:rps:action:received": {
|
|
35364
35616
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
35365
|
-
if (!current || !isRpsState(current))
|
|
35617
|
+
if (!current || !isRpsState(current)) {
|
|
35618
|
+
enqueue(event.payload.gameId, event);
|
|
35619
|
+
return;
|
|
35620
|
+
}
|
|
35366
35621
|
const updated = {
|
|
35367
35622
|
...current,
|
|
35368
35623
|
roundState: {
|
|
@@ -35381,7 +35636,10 @@ function createGameActionsStore(transport) {
|
|
|
35381
35636
|
}
|
|
35382
35637
|
case "game:rps:timer:cutoff": {
|
|
35383
35638
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
35384
|
-
if (!current || !isRpsState(current))
|
|
35639
|
+
if (!current || !isRpsState(current)) {
|
|
35640
|
+
enqueue(event.payload.gameId, event);
|
|
35641
|
+
return;
|
|
35642
|
+
}
|
|
35385
35643
|
const updated = {
|
|
35386
35644
|
...current,
|
|
35387
35645
|
roundState: {
|
|
@@ -35395,7 +35653,10 @@ function createGameActionsStore(transport) {
|
|
|
35395
35653
|
}
|
|
35396
35654
|
case "game:rps:round:reveal": {
|
|
35397
35655
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
35398
|
-
if (!current || !isRpsState(current))
|
|
35656
|
+
if (!current || !isRpsState(current)) {
|
|
35657
|
+
enqueue(event.payload.gameId, event);
|
|
35658
|
+
return;
|
|
35659
|
+
}
|
|
35399
35660
|
const actions = {};
|
|
35400
35661
|
const payloadActions = event.payload.actions;
|
|
35401
35662
|
Object.keys(payloadActions || {}).forEach((userId) => {
|
|
@@ -35419,7 +35680,10 @@ function createGameActionsStore(transport) {
|
|
|
35419
35680
|
}
|
|
35420
35681
|
case "game:rps:round:completed": {
|
|
35421
35682
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
35422
|
-
if (!current || !isRpsState(current))
|
|
35683
|
+
if (!current || !isRpsState(current)) {
|
|
35684
|
+
enqueue(event.payload.gameId, event);
|
|
35685
|
+
return;
|
|
35686
|
+
}
|
|
35423
35687
|
const roundHistory = [
|
|
35424
35688
|
...current.roundHistory || [],
|
|
35425
35689
|
{
|
|
@@ -35444,7 +35708,10 @@ function createGameActionsStore(transport) {
|
|
|
35444
35708
|
}
|
|
35445
35709
|
case "game:rps:timeout": {
|
|
35446
35710
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
35447
|
-
if (!current || !isRpsState(current))
|
|
35711
|
+
if (!current || !isRpsState(current)) {
|
|
35712
|
+
enqueue(event.payload.gameId, event);
|
|
35713
|
+
return;
|
|
35714
|
+
}
|
|
35448
35715
|
const timedOutUser = event.payload.playerId;
|
|
35449
35716
|
const action = event.payload.action;
|
|
35450
35717
|
const updated = {
|
|
@@ -35469,7 +35736,10 @@ function createGameActionsStore(transport) {
|
|
|
35469
35736
|
const payload = event.payload;
|
|
35470
35737
|
const { gameId } = payload;
|
|
35471
35738
|
const current = store.getState().statesByGameId[gameId];
|
|
35472
|
-
if (!current)
|
|
35739
|
+
if (!current) {
|
|
35740
|
+
enqueue(gameId, event);
|
|
35741
|
+
return;
|
|
35742
|
+
}
|
|
35473
35743
|
const updated = isRpsCompletionPayload(payload) && isRpsState(current) ? {
|
|
35474
35744
|
...current,
|
|
35475
35745
|
status: "completed",
|
|
@@ -35518,11 +35788,15 @@ function createGameActionsStore(transport) {
|
|
|
35518
35788
|
const current = store.getState().statesByGameId[gameId];
|
|
35519
35789
|
const updated = current ? { ...current, ...incoming } : incoming;
|
|
35520
35790
|
updateState(gameId, updated);
|
|
35791
|
+
drainQueue(gameId);
|
|
35521
35792
|
break;
|
|
35522
35793
|
}
|
|
35523
35794
|
case "game:rematch:requested": {
|
|
35524
35795
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
35525
|
-
if (!current)
|
|
35796
|
+
if (!current) {
|
|
35797
|
+
enqueue(event.payload.gameId, event);
|
|
35798
|
+
return;
|
|
35799
|
+
}
|
|
35526
35800
|
const requestedBy = event.payload.requestedBy;
|
|
35527
35801
|
const userId = event.payload.userId;
|
|
35528
35802
|
const requested = new Set(
|
|
@@ -35538,7 +35812,10 @@ function createGameActionsStore(transport) {
|
|
|
35538
35812
|
}
|
|
35539
35813
|
case "game:rematch:cancelled": {
|
|
35540
35814
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
35541
|
-
if (!current)
|
|
35815
|
+
if (!current) {
|
|
35816
|
+
enqueue(event.payload.gameId, event);
|
|
35817
|
+
return;
|
|
35818
|
+
}
|
|
35542
35819
|
const requestedBy = event.payload.requestedBy ?? [];
|
|
35543
35820
|
const updated = {
|
|
35544
35821
|
...current,
|
|
@@ -35549,7 +35826,10 @@ function createGameActionsStore(transport) {
|
|
|
35549
35826
|
}
|
|
35550
35827
|
case "game:rematch:started": {
|
|
35551
35828
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
35552
|
-
if (!current)
|
|
35829
|
+
if (!current) {
|
|
35830
|
+
enqueue(event.payload.gameId, event);
|
|
35831
|
+
return;
|
|
35832
|
+
}
|
|
35553
35833
|
const updated = {
|
|
35554
35834
|
...current,
|
|
35555
35835
|
rematchRequestedBy: event.payload.playerIds ?? []
|
|
@@ -35560,7 +35840,10 @@ function createGameActionsStore(transport) {
|
|
|
35560
35840
|
case "game:pot:updated": {
|
|
35561
35841
|
const { gameId, totalPotMinor } = event.payload;
|
|
35562
35842
|
const current = store.getState().statesByGameId[gameId];
|
|
35563
|
-
if (!current)
|
|
35843
|
+
if (!current) {
|
|
35844
|
+
enqueue(gameId, event);
|
|
35845
|
+
return;
|
|
35846
|
+
}
|
|
35564
35847
|
const updated = {
|
|
35565
35848
|
...current,
|
|
35566
35849
|
totalPotMinor
|
|
@@ -35573,12 +35856,123 @@ function createGameActionsStore(transport) {
|
|
|
35573
35856
|
}
|
|
35574
35857
|
};
|
|
35575
35858
|
const joinGame = (gameId) => transport.joinRoom(`game:${gameId}`);
|
|
35859
|
+
const getCountdownDigit = (gameId, nowMs) => {
|
|
35860
|
+
const state = store.getState().statesByGameId[gameId];
|
|
35861
|
+
if (!state) return null;
|
|
35862
|
+
if (isRpsState(state)) {
|
|
35863
|
+
if (state.roundState.phase !== "starting") return null;
|
|
35864
|
+
const remaining = new Date(state.roundState.selectionEndsAt).getTime() - nowMs;
|
|
35865
|
+
if (remaining <= 0) return null;
|
|
35866
|
+
return Math.ceil(remaining / 1e3);
|
|
35867
|
+
}
|
|
35868
|
+
const bufferEndsAt = state.bufferEndsAt;
|
|
35869
|
+
if (bufferEndsAt) {
|
|
35870
|
+
const remaining = new Date(bufferEndsAt).getTime() - nowMs;
|
|
35871
|
+
if (remaining <= 0) return null;
|
|
35872
|
+
return Math.ceil(remaining / 1e3);
|
|
35873
|
+
}
|
|
35874
|
+
return null;
|
|
35875
|
+
};
|
|
35876
|
+
const getChessClockTimes = (gameId, nowMs) => {
|
|
35877
|
+
const state = store.getState().statesByGameId[gameId];
|
|
35878
|
+
if (!state || state.gameType !== "chess") return null;
|
|
35879
|
+
const s = state;
|
|
35880
|
+
let whiteMs = s.whiteTimeMs ?? 0;
|
|
35881
|
+
let blackMs = s.blackTimeMs ?? 0;
|
|
35882
|
+
if (s.status === "active" && s.currentPlayerId) {
|
|
35883
|
+
const startedAt = Date.parse(s.turnStartedAt);
|
|
35884
|
+
if (!Number.isNaN(startedAt)) {
|
|
35885
|
+
const elapsed = Math.max(0, nowMs - startedAt);
|
|
35886
|
+
if (s.currentPlayerId === s.whitePlayerId) {
|
|
35887
|
+
whiteMs = Math.max(0, whiteMs - elapsed);
|
|
35888
|
+
} else if (s.currentPlayerId === s.blackPlayerId) {
|
|
35889
|
+
blackMs = Math.max(0, blackMs - elapsed);
|
|
35890
|
+
}
|
|
35891
|
+
}
|
|
35892
|
+
}
|
|
35893
|
+
return { whiteTimeMs: whiteMs, blackTimeMs: blackMs };
|
|
35894
|
+
};
|
|
35895
|
+
const getChessCapturedPieces = (gameId) => {
|
|
35896
|
+
const state = store.getState().statesByGameId[gameId];
|
|
35897
|
+
if (!state || state.gameType !== "chess") return null;
|
|
35898
|
+
const fen = state.fen;
|
|
35899
|
+
if (!fen) return { capturedByWhite: [], capturedByBlack: [] };
|
|
35900
|
+
const placement = fen.split(" ")[0];
|
|
35901
|
+
const white = {};
|
|
35902
|
+
const black = {};
|
|
35903
|
+
for (const char of placement) {
|
|
35904
|
+
if (char === "/" || char >= "1" && char <= "8") continue;
|
|
35905
|
+
const lower = char.toLowerCase();
|
|
35906
|
+
if (char === lower) {
|
|
35907
|
+
black[lower] = (black[lower] ?? 0) + 1;
|
|
35908
|
+
} else {
|
|
35909
|
+
white[lower] = (white[lower] ?? 0) + 1;
|
|
35910
|
+
}
|
|
35911
|
+
}
|
|
35912
|
+
const INITIAL = {
|
|
35913
|
+
p: 8,
|
|
35914
|
+
r: 2,
|
|
35915
|
+
n: 2,
|
|
35916
|
+
b: 2,
|
|
35917
|
+
q: 1,
|
|
35918
|
+
k: 1
|
|
35919
|
+
};
|
|
35920
|
+
const capturedByWhite = [];
|
|
35921
|
+
const capturedByBlack = [];
|
|
35922
|
+
for (const [type2, initial] of Object.entries(INITIAL)) {
|
|
35923
|
+
const missingBlack = initial - (black[type2] ?? 0);
|
|
35924
|
+
for (let i = 0; i < missingBlack; i++) capturedByWhite.push(`b${type2}`);
|
|
35925
|
+
const missingWhite = initial - (white[type2] ?? 0);
|
|
35926
|
+
for (let i = 0; i < missingWhite; i++) capturedByBlack.push(`w${type2}`);
|
|
35927
|
+
}
|
|
35928
|
+
return { capturedByWhite, capturedByBlack };
|
|
35929
|
+
};
|
|
35930
|
+
const getTicTacToeClockTimes = (gameId, nowMs) => {
|
|
35931
|
+
const state = store.getState().statesByGameId[gameId];
|
|
35932
|
+
if (!state || state.gameType !== "tic-tac-toe") return null;
|
|
35933
|
+
const s = state;
|
|
35934
|
+
let xMs = s.xTimeMs ?? 0;
|
|
35935
|
+
let oMs = s.oTimeMs ?? 0;
|
|
35936
|
+
if (s.status === "active" && s.currentPlayerId) {
|
|
35937
|
+
const currentMark = s.playerMarks[s.currentPlayerId];
|
|
35938
|
+
const startedAt = Date.parse(s.turnStartedAt);
|
|
35939
|
+
if (!Number.isNaN(startedAt)) {
|
|
35940
|
+
const elapsed = Math.max(0, nowMs - startedAt);
|
|
35941
|
+
if (currentMark === "X") xMs = Math.max(0, xMs - elapsed);
|
|
35942
|
+
else if (currentMark === "O") oMs = Math.max(0, oMs - elapsed);
|
|
35943
|
+
}
|
|
35944
|
+
}
|
|
35945
|
+
return { xTimeMs: xMs, oTimeMs: oMs };
|
|
35946
|
+
};
|
|
35947
|
+
const getConnect4ClockTimes = (gameId, nowMs) => {
|
|
35948
|
+
const state = store.getState().statesByGameId[gameId];
|
|
35949
|
+
if (!state || state.gameType !== "connect-four") return null;
|
|
35950
|
+
const s = state;
|
|
35951
|
+
let redMs = s.redTimeMs ?? 0;
|
|
35952
|
+
let yellowMs = s.yellowTimeMs ?? 0;
|
|
35953
|
+
if (s.status === "active" && s.currentPlayerId) {
|
|
35954
|
+
const currentColor = s.playerColors[s.currentPlayerId];
|
|
35955
|
+
const startedAt = Date.parse(s.turnStartedAt);
|
|
35956
|
+
if (!Number.isNaN(startedAt)) {
|
|
35957
|
+
const elapsed = Math.max(0, nowMs - startedAt);
|
|
35958
|
+
if (currentColor === "RED") redMs = Math.max(0, redMs - elapsed);
|
|
35959
|
+
else if (currentColor === "YELLOW")
|
|
35960
|
+
yellowMs = Math.max(0, yellowMs - elapsed);
|
|
35961
|
+
}
|
|
35962
|
+
}
|
|
35963
|
+
return { redTimeMs: redMs, yellowTimeMs: yellowMs };
|
|
35964
|
+
};
|
|
35576
35965
|
return {
|
|
35577
35966
|
store,
|
|
35578
35967
|
setBaseState,
|
|
35579
35968
|
clearState,
|
|
35580
35969
|
applyWsEvent,
|
|
35581
|
-
joinGame
|
|
35970
|
+
joinGame,
|
|
35971
|
+
getCountdownDigit,
|
|
35972
|
+
getChessClockTimes,
|
|
35973
|
+
getChessCapturedPieces,
|
|
35974
|
+
getTicTacToeClockTimes,
|
|
35975
|
+
getConnect4ClockTimes
|
|
35582
35976
|
};
|
|
35583
35977
|
}
|
|
35584
35978
|
function isRpsState(state) {
|
|
@@ -35920,7 +36314,38 @@ function createNotificationsStore() {
|
|
|
35920
36314
|
appNotifications: []
|
|
35921
36315
|
}));
|
|
35922
36316
|
};
|
|
35923
|
-
|
|
36317
|
+
const markAsRead = (id) => {
|
|
36318
|
+
store.updateState((state) => ({
|
|
36319
|
+
...state,
|
|
36320
|
+
appNotifications: state.appNotifications.map(
|
|
36321
|
+
(n) => n.id === id ? { ...n, read: true } : n
|
|
36322
|
+
)
|
|
36323
|
+
}));
|
|
36324
|
+
};
|
|
36325
|
+
const markAllAsRead = () => {
|
|
36326
|
+
store.updateState((state) => ({
|
|
36327
|
+
...state,
|
|
36328
|
+
appNotifications: state.appNotifications.map((n) => ({
|
|
36329
|
+
...n,
|
|
36330
|
+
read: true
|
|
36331
|
+
}))
|
|
36332
|
+
}));
|
|
36333
|
+
};
|
|
36334
|
+
const dismiss = (id) => {
|
|
36335
|
+
store.updateState((state) => ({
|
|
36336
|
+
...state,
|
|
36337
|
+
appNotifications: state.appNotifications.filter((n) => n.id !== id)
|
|
36338
|
+
}));
|
|
36339
|
+
};
|
|
36340
|
+
return {
|
|
36341
|
+
store,
|
|
36342
|
+
applyWsEvent,
|
|
36343
|
+
setListFromApi,
|
|
36344
|
+
clear,
|
|
36345
|
+
markAsRead,
|
|
36346
|
+
markAllAsRead,
|
|
36347
|
+
dismiss
|
|
36348
|
+
};
|
|
35924
36349
|
}
|
|
35925
36350
|
function createFriendsStore() {
|
|
35926
36351
|
const store = createSdkStore({
|
|
@@ -36205,12 +36630,14 @@ var WsRouter = class {
|
|
|
36205
36630
|
}
|
|
36206
36631
|
const decoded = decodeWsEvent(eventName, payload);
|
|
36207
36632
|
if (!decoded) return;
|
|
36208
|
-
|
|
36209
|
-
|
|
36210
|
-
this.deps.
|
|
36211
|
-
this.deps.
|
|
36212
|
-
this.deps.
|
|
36213
|
-
this.deps.
|
|
36633
|
+
const serverTs = payload !== null && typeof payload === "object" ? payload._serverTs : void 0;
|
|
36634
|
+
const event = serverTs !== void 0 ? { ...decoded, _serverTs: serverTs } : decoded;
|
|
36635
|
+
this.deps.lobbyStore.applyWsEvent(event);
|
|
36636
|
+
this.deps.gameStore.applyWsEvent(event);
|
|
36637
|
+
this.deps.gameActionsStore.applyWsEvent(event);
|
|
36638
|
+
this.deps.chatStore.applyWsEvent(event);
|
|
36639
|
+
this.deps.dmThreadsStore.applyWsEvent(event);
|
|
36640
|
+
this.deps.notificationsStore.applyWsEvent(event);
|
|
36214
36641
|
});
|
|
36215
36642
|
}
|
|
36216
36643
|
stop() {
|
|
@@ -36222,12 +36649,14 @@ var WsRouter = class {
|
|
|
36222
36649
|
this.transport.subscribeEvent(eventName, (payload) => {
|
|
36223
36650
|
const decoded = decodeWsEvent(eventName, payload);
|
|
36224
36651
|
if (!decoded) return;
|
|
36225
|
-
|
|
36226
|
-
|
|
36227
|
-
this.deps.
|
|
36228
|
-
this.deps.
|
|
36229
|
-
this.deps.
|
|
36230
|
-
this.deps.
|
|
36652
|
+
const serverTs = payload !== null && typeof payload === "object" ? payload._serverTs : void 0;
|
|
36653
|
+
const event = serverTs !== void 0 ? { ...decoded, _serverTs: serverTs } : decoded;
|
|
36654
|
+
this.deps.lobbyStore.applyWsEvent(event);
|
|
36655
|
+
this.deps.gameStore.applyWsEvent(event);
|
|
36656
|
+
this.deps.gameActionsStore.applyWsEvent(event);
|
|
36657
|
+
this.deps.chatStore.applyWsEvent(event);
|
|
36658
|
+
this.deps.dmThreadsStore.applyWsEvent(event);
|
|
36659
|
+
this.deps.notificationsStore.applyWsEvent(event);
|
|
36231
36660
|
});
|
|
36232
36661
|
}
|
|
36233
36662
|
}
|
|
@@ -36267,6 +36696,7 @@ var SDK = class {
|
|
|
36267
36696
|
constructor(config) {
|
|
36268
36697
|
const baseUrl = config.baseUrl || "http://localhost:3000";
|
|
36269
36698
|
const logger2 = config.logger || logger;
|
|
36699
|
+
this.analytics = config.analytics ?? new NoopAnalyticsClient();
|
|
36270
36700
|
this.http = config.httpClient || new HttpClient(
|
|
36271
36701
|
baseUrl,
|
|
36272
36702
|
config.storage,
|
|
@@ -36299,14 +36729,18 @@ var SDK = class {
|
|
|
36299
36729
|
this.referrals = new Referrals(this.http, logger2);
|
|
36300
36730
|
this.reports = new Reports(this.http, logger2);
|
|
36301
36731
|
this.support = new Support(this.http, logger2);
|
|
36302
|
-
this.markets = new Markets(this.http, logger2);
|
|
36732
|
+
this.markets = new Markets(this.http, logger2, this.wallet);
|
|
36303
36733
|
this.wsTransport = config.wsTransport || new StandaloneWsTransport(baseUrl);
|
|
36304
36734
|
this.wsTransport.setAppId(config.appId);
|
|
36305
36735
|
this.lobbyStore = createLobbyStore(this.wsTransport);
|
|
36736
|
+
this.lobbies.setLobbyStore(this.lobbyStore);
|
|
36306
36737
|
this.gameStore = createGameStore(this.wsTransport);
|
|
36738
|
+
this.games.setGameStore(this.gameStore);
|
|
36307
36739
|
this.gameActionsStore = createGameActionsStore(this.wsTransport);
|
|
36740
|
+
this.games.setGameActionsStore(this.gameActionsStore);
|
|
36308
36741
|
this.chatStore = createChatStore(this.wsTransport);
|
|
36309
36742
|
this.dmThreadsStore = createDmThreadsStore();
|
|
36743
|
+
this.chat.setDmThreadsStore(this.dmThreadsStore);
|
|
36310
36744
|
this.notificationsStore = createNotificationsStore();
|
|
36311
36745
|
this.friendsStore = createFriendsStore();
|
|
36312
36746
|
this.notifications = new Notifications(
|
|
@@ -36336,6 +36770,29 @@ var SDK = class {
|
|
|
36336
36770
|
this.wsTransport.connect();
|
|
36337
36771
|
await this.wsTransport.waitUntilConnected(timeoutMs);
|
|
36338
36772
|
}
|
|
36773
|
+
/**
|
|
36774
|
+
* Handle the full deposit-to-queue lifecycle for a lobby.
|
|
36775
|
+
*
|
|
36776
|
+
* After the deposit is confirmed, the backend may not have processed the
|
|
36777
|
+
* `lobby.deposit.allConfirmed` event yet, so the lobby can still be in
|
|
36778
|
+
* 'preparing' status. This method re-fetches the lobby and explicitly
|
|
36779
|
+
* joins the queue if needed, then pushes the latest state to the store
|
|
36780
|
+
* so the UI transitions immediately.
|
|
36781
|
+
*/
|
|
36782
|
+
async depositAndJoinQueue(lobbyId) {
|
|
36783
|
+
const result = await this.escrow.depositForLobby(lobbyId);
|
|
36784
|
+
if (!result.canProceedToQueue) {
|
|
36785
|
+
const lobby2 = await this.lobbies.getLobby(lobbyId);
|
|
36786
|
+
this.lobbyStore.setBaseState([lobby2]);
|
|
36787
|
+
return { ...result, lobby: lobby2 };
|
|
36788
|
+
}
|
|
36789
|
+
let lobby = await this.lobbies.getLobby(lobbyId);
|
|
36790
|
+
if (lobby.status === "preparing") {
|
|
36791
|
+
lobby = await this.lobbies.joinQueue(lobbyId);
|
|
36792
|
+
}
|
|
36793
|
+
this.lobbyStore.setBaseState([lobby]);
|
|
36794
|
+
return { ...result, lobby };
|
|
36795
|
+
}
|
|
36339
36796
|
};
|
|
36340
36797
|
var import_utils14 = __toESM2(require_dist(), 1);
|
|
36341
36798
|
var export_MICRO_UNITS = import_utils14.MICRO_UNITS;
|
|
@@ -36479,10 +36936,34 @@ var esm_default4 = esm_default3(ALPHABET2);
|
|
|
36479
36936
|
|
|
36480
36937
|
// ../dim-agent-core/src/client.ts
|
|
36481
36938
|
var import_tweetnacl = __toESM(require_nacl_fast(), 1);
|
|
36939
|
+
function decodeJwtSub(token) {
|
|
36940
|
+
try {
|
|
36941
|
+
const parts2 = token.split(".");
|
|
36942
|
+
if (parts2.length < 2) return null;
|
|
36943
|
+
const base64 = parts2[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
36944
|
+
const json = Buffer.from(base64, "base64").toString("utf-8");
|
|
36945
|
+
const parsed = JSON.parse(json);
|
|
36946
|
+
return typeof parsed.sub === "string" ? parsed.sub : null;
|
|
36947
|
+
} catch {
|
|
36948
|
+
return null;
|
|
36949
|
+
}
|
|
36950
|
+
}
|
|
36951
|
+
function signatureToBytes(sig) {
|
|
36952
|
+
if (sig.includes("-") || sig.includes("_")) {
|
|
36953
|
+
const padded = sig.replace(/-/g, "+").replace(/_/g, "/") + "==".slice(sig.length % 4 === 0 ? 4 : sig.length % 4);
|
|
36954
|
+
return new Uint8Array(Buffer.from(padded, "base64"));
|
|
36955
|
+
}
|
|
36956
|
+
if (/[+/=]/.test(sig)) {
|
|
36957
|
+
return new Uint8Array(Buffer.from(sig, "base64"));
|
|
36958
|
+
}
|
|
36959
|
+
return esm_default4.decode(sig);
|
|
36960
|
+
}
|
|
36482
36961
|
var DimAgentClient = class {
|
|
36483
36962
|
sdk;
|
|
36484
36963
|
agentConfig;
|
|
36964
|
+
externalSignerMode;
|
|
36485
36965
|
keypair;
|
|
36966
|
+
_externalAddress = null;
|
|
36486
36967
|
config;
|
|
36487
36968
|
authenticated = false;
|
|
36488
36969
|
userId = null;
|
|
@@ -36496,21 +36977,35 @@ var DimAgentClient = class {
|
|
|
36496
36977
|
constructor(config) {
|
|
36497
36978
|
this.config = config;
|
|
36498
36979
|
this.agentConfig = config.agentConfig;
|
|
36499
|
-
|
|
36500
|
-
|
|
36980
|
+
this.externalSignerMode = !config.walletPrivateKey;
|
|
36981
|
+
if (config.walletPrivateKey) {
|
|
36982
|
+
const secretKeyBytes = esm_default4.decode(config.walletPrivateKey);
|
|
36983
|
+
this.keypair = Keypair.fromSecretKey(secretKeyBytes);
|
|
36984
|
+
} else {
|
|
36985
|
+
this.keypair = null;
|
|
36986
|
+
}
|
|
36987
|
+
const storage = new NodeStorage();
|
|
36988
|
+
if (config.accessToken) {
|
|
36989
|
+
storage.set(TOKEN_KEY, config.accessToken);
|
|
36990
|
+
const sub = decodeJwtSub(config.accessToken);
|
|
36991
|
+
if (sub) {
|
|
36992
|
+
this.authenticated = true;
|
|
36993
|
+
this.userId = sub;
|
|
36994
|
+
}
|
|
36995
|
+
}
|
|
36501
36996
|
this.sdk = new SDK({
|
|
36502
36997
|
appId: "dim-agents",
|
|
36503
36998
|
baseUrl: config.apiUrl || "https://api.dim.cool",
|
|
36504
|
-
storage
|
|
36999
|
+
storage,
|
|
36505
37000
|
autoPay: {
|
|
36506
|
-
enabled:
|
|
37001
|
+
enabled: !this.externalSignerMode,
|
|
36507
37002
|
maxAmountMinor: 25e3,
|
|
36508
37003
|
maxRetries: 1
|
|
36509
37004
|
}
|
|
36510
37005
|
});
|
|
36511
37006
|
}
|
|
36512
37007
|
get walletAddress() {
|
|
36513
|
-
return this.keypair
|
|
37008
|
+
return this.keypair?.publicKey.toBase58() ?? this._externalAddress ?? "";
|
|
36514
37009
|
}
|
|
36515
37010
|
get isAuthenticated() {
|
|
36516
37011
|
return this.authenticated;
|
|
@@ -36519,17 +37014,23 @@ var DimAgentClient = class {
|
|
|
36519
37014
|
return this.userId;
|
|
36520
37015
|
}
|
|
36521
37016
|
async authenticate() {
|
|
37017
|
+
if (!this.keypair) {
|
|
37018
|
+
throw new Error(
|
|
37019
|
+
"No keypair configured. Use dim_request_auth_message + dim_complete_login for external signer mode."
|
|
37020
|
+
);
|
|
37021
|
+
}
|
|
37022
|
+
const keypair = this.keypair;
|
|
36522
37023
|
this.sdk.wallet.setSigner({
|
|
36523
37024
|
address: this.walletAddress,
|
|
36524
37025
|
signMessage: async (message) => {
|
|
36525
37026
|
const signature = import_tweetnacl.default.sign.detached(
|
|
36526
37027
|
new TextEncoder().encode(message),
|
|
36527
|
-
|
|
37028
|
+
keypair.secretKey
|
|
36528
37029
|
);
|
|
36529
37030
|
return Buffer.from(signature).toString("base64");
|
|
36530
37031
|
},
|
|
36531
37032
|
signTransaction: async (transaction) => {
|
|
36532
|
-
transaction.partialSign(
|
|
37033
|
+
transaction.partialSign(keypair);
|
|
36533
37034
|
return transaction;
|
|
36534
37035
|
}
|
|
36535
37036
|
});
|
|
@@ -36583,10 +37084,45 @@ var DimAgentClient = class {
|
|
|
36583
37084
|
get pendingEventCount() {
|
|
36584
37085
|
return this.eventQueue.length;
|
|
36585
37086
|
}
|
|
36586
|
-
/** Get the Keypair for transaction signing. */
|
|
37087
|
+
/** Get the Keypair for transaction signing (keypair mode only). */
|
|
36587
37088
|
getKeypair() {
|
|
37089
|
+
if (!this.keypair) {
|
|
37090
|
+
throw new Error(
|
|
37091
|
+
"No keypair configured \u2014 running in external signer mode."
|
|
37092
|
+
);
|
|
37093
|
+
}
|
|
36588
37094
|
return this.keypair;
|
|
36589
37095
|
}
|
|
37096
|
+
/** Request the auth handshake message for a given address (external signer mode). */
|
|
37097
|
+
async requestAuthMessage(address) {
|
|
37098
|
+
return this.sdk.auth.generateHandshake(address);
|
|
37099
|
+
}
|
|
37100
|
+
/**
|
|
37101
|
+
* Complete authentication using an externally provided signature (external signer mode).
|
|
37102
|
+
* @param address - Solana wallet address (base58)
|
|
37103
|
+
* @param signature - Detached ed25519 signature in base58, base64, or base64url (from sign_solana_message)
|
|
37104
|
+
*/
|
|
37105
|
+
async completeAuth(address, signature) {
|
|
37106
|
+
const signatureBytes = signatureToBytes(signature);
|
|
37107
|
+
const signedMessage = Buffer.from(signatureBytes).toString("base64");
|
|
37108
|
+
const response = await this.sdk.auth.loginWithExternalSignature(
|
|
37109
|
+
address,
|
|
37110
|
+
signedMessage,
|
|
37111
|
+
{
|
|
37112
|
+
referralCode: this.config.referralCode,
|
|
37113
|
+
walletMeta: { type: "phantom-embedded", provider: "injected" }
|
|
37114
|
+
}
|
|
37115
|
+
);
|
|
37116
|
+
this.sdk.wsTransport.setAccessToken(response.access_token);
|
|
37117
|
+
this._externalAddress = address;
|
|
37118
|
+
this.authenticated = true;
|
|
37119
|
+
this.userId = response.user.id;
|
|
37120
|
+
return {
|
|
37121
|
+
userId: response.user.id,
|
|
37122
|
+
username: response.user.username,
|
|
37123
|
+
accessToken: response.access_token
|
|
37124
|
+
};
|
|
37125
|
+
}
|
|
36590
37126
|
// ── Game chat cursors ────────────────────────────────────────────────
|
|
36591
37127
|
/** Get the last-seen chat timestamp for a game. */
|
|
36592
37128
|
getGameChatCursor(gameId) {
|
|
@@ -36714,13 +37250,84 @@ async function login(client) {
|
|
|
36714
37250
|
};
|
|
36715
37251
|
}
|
|
36716
37252
|
}
|
|
36717
|
-
async function
|
|
37253
|
+
async function requestAuthMessage(client, args) {
|
|
36718
37254
|
try {
|
|
36719
|
-
|
|
36720
|
-
|
|
36721
|
-
|
|
36722
|
-
|
|
36723
|
-
|
|
37255
|
+
const { message } = await client.requestAuthMessage(args.address);
|
|
37256
|
+
return {
|
|
37257
|
+
data: {
|
|
37258
|
+
message,
|
|
37259
|
+
address: args.address,
|
|
37260
|
+
nextStep: 'Sign this message with sign_solana_message (networkId: "solana:mainnet"), then call dim_complete_login with the address and the signature. Accepted formats: base58, base64, or base64url (all formats returned by wallet MCPs are accepted).'
|
|
37261
|
+
}
|
|
37262
|
+
};
|
|
37263
|
+
} catch (error) {
|
|
37264
|
+
return {
|
|
37265
|
+
error: `Failed to get auth message: ${error instanceof Error ? error.message : String(error)}`,
|
|
37266
|
+
isError: true
|
|
37267
|
+
};
|
|
37268
|
+
}
|
|
37269
|
+
}
|
|
37270
|
+
async function completeLogin(client, args) {
|
|
37271
|
+
try {
|
|
37272
|
+
const result = await client.completeAuth(args.address, args.signature);
|
|
37273
|
+
client.startEventListeners();
|
|
37274
|
+
const nextSteps = [];
|
|
37275
|
+
if (result.username == null || result.username === "") {
|
|
37276
|
+
nextSteps.push("No username set \u2014 call dim_set_username to claim one");
|
|
37277
|
+
}
|
|
37278
|
+
nextSteps.push("Check your balance with dim_get_balance");
|
|
37279
|
+
nextSteps.push("Explore available games with dim_list_games");
|
|
37280
|
+
try {
|
|
37281
|
+
const summary = await client.sdk.referrals.getSummary();
|
|
37282
|
+
if (!summary.hasReferrer) {
|
|
37283
|
+
nextSteps.push(
|
|
37284
|
+
"No referrer yet \u2014 call dim_apply_referral_code for 10% fee discount"
|
|
37285
|
+
);
|
|
37286
|
+
}
|
|
37287
|
+
if (result.username) {
|
|
37288
|
+
nextSteps.push(
|
|
37289
|
+
`Share your referral code "${result.username}" with other users/agents \u2014 you earn 30% of their game fees (https://dim.cool/?ref=${result.username})`
|
|
37290
|
+
);
|
|
37291
|
+
}
|
|
37292
|
+
} catch {
|
|
37293
|
+
}
|
|
37294
|
+
const response = {
|
|
37295
|
+
success: true,
|
|
37296
|
+
userId: result.userId,
|
|
37297
|
+
username: result.username ?? null,
|
|
37298
|
+
walletAddress: client.walletAddress,
|
|
37299
|
+
walletNote: "DIM is noncustodial. Signing is handled by your external wallet for this session.",
|
|
37300
|
+
safetyRules: SAFETY_RULES,
|
|
37301
|
+
nextSteps
|
|
37302
|
+
};
|
|
37303
|
+
const ac = client.agentConfig;
|
|
37304
|
+
const dailyLimit = ac?.dailySpendLimit ?? 20;
|
|
37305
|
+
response.agentConfig = {
|
|
37306
|
+
autoAcceptFriendRequests: ac?.autoAcceptFriendRequests ?? false,
|
|
37307
|
+
autoReplyDms: ac?.autoReplyDms ?? false,
|
|
37308
|
+
autoPlayGames: ac?.autoPlayGames ?? false,
|
|
37309
|
+
maxBetPerGame: ac?.maxBetPerGame ?? 1,
|
|
37310
|
+
dailySpendLimit: dailyLimit,
|
|
37311
|
+
autoJoinGlobalChat: ac?.autoJoinGlobalChat ?? false,
|
|
37312
|
+
autoPromoteReferrals: ac?.autoPromoteReferrals ?? false,
|
|
37313
|
+
dailySpentSoFar: client.dailySpentDollars,
|
|
37314
|
+
dailyRemaining: dailyLimit - client.dailySpentDollars
|
|
37315
|
+
};
|
|
37316
|
+
return { data: response };
|
|
37317
|
+
} catch (error) {
|
|
37318
|
+
return {
|
|
37319
|
+
error: `Login failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
37320
|
+
isError: true
|
|
37321
|
+
};
|
|
37322
|
+
}
|
|
37323
|
+
}
|
|
37324
|
+
async function getProfile(client) {
|
|
37325
|
+
try {
|
|
37326
|
+
if (!client.currentUserId) {
|
|
37327
|
+
return {
|
|
37328
|
+
error: "Not authenticated. Call dim_login first.",
|
|
37329
|
+
isError: true
|
|
37330
|
+
};
|
|
36724
37331
|
}
|
|
36725
37332
|
const user = await client.sdk.users.getUserById(client.currentUserId);
|
|
36726
37333
|
const profile = {
|
|
@@ -36842,10 +37449,19 @@ async function listFriends(client, args) {
|
|
|
36842
37449
|
};
|
|
36843
37450
|
}
|
|
36844
37451
|
}
|
|
36845
|
-
async function getIncomingFriendRequests(client) {
|
|
37452
|
+
async function getIncomingFriendRequests(client, args) {
|
|
36846
37453
|
try {
|
|
36847
|
-
const result = await client.sdk.users.getIncomingFriendRequests(
|
|
36848
|
-
|
|
37454
|
+
const result = await client.sdk.users.getIncomingFriendRequests({
|
|
37455
|
+
limit: args?.limit,
|
|
37456
|
+
cursor: args?.cursor
|
|
37457
|
+
});
|
|
37458
|
+
return {
|
|
37459
|
+
data: {
|
|
37460
|
+
items: result.items,
|
|
37461
|
+
nextCursor: result.nextCursor,
|
|
37462
|
+
hasMore: !!result.nextCursor
|
|
37463
|
+
}
|
|
37464
|
+
};
|
|
36849
37465
|
} catch (error) {
|
|
36850
37466
|
return {
|
|
36851
37467
|
error: `Failed to get requests: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -36966,6 +37582,42 @@ async function sendUsdc(client, args) {
|
|
|
36966
37582
|
const spendErr = client.checkSpendLimit(args.amount);
|
|
36967
37583
|
if (spendErr) return { error: spendErr, isError: true };
|
|
36968
37584
|
const amountMinor = Math.round(args.amount * 1e6);
|
|
37585
|
+
if (client.externalSignerMode) {
|
|
37586
|
+
const senderAddress = client.walletAddress;
|
|
37587
|
+
if (!senderAddress) {
|
|
37588
|
+
return {
|
|
37589
|
+
error: "Wallet address not set. Call dim_complete_login first.",
|
|
37590
|
+
isError: true
|
|
37591
|
+
};
|
|
37592
|
+
}
|
|
37593
|
+
const prepared = await client.sdk.wallet.prepareTransfer(
|
|
37594
|
+
senderAddress,
|
|
37595
|
+
args.recipient,
|
|
37596
|
+
amountMinor
|
|
37597
|
+
);
|
|
37598
|
+
return {
|
|
37599
|
+
data: {
|
|
37600
|
+
needsSigning: true,
|
|
37601
|
+
unsignedTx: prepared.transaction,
|
|
37602
|
+
fee: prepared.fee,
|
|
37603
|
+
totalAmount: prepared.totalAmount,
|
|
37604
|
+
recipientAddress: prepared.recipientAddress,
|
|
37605
|
+
ataCreated: prepared.ataCreated ?? false,
|
|
37606
|
+
confirmWith: {
|
|
37607
|
+
tool: "dim_confirm_send_usdc",
|
|
37608
|
+
params: {
|
|
37609
|
+
recipientAddress: prepared.recipientAddress,
|
|
37610
|
+
amount: amountMinor,
|
|
37611
|
+
fee: prepared.fee,
|
|
37612
|
+
token: "USDC",
|
|
37613
|
+
ataCreated: prepared.ataCreated ?? false,
|
|
37614
|
+
recipientInput: args.recipient
|
|
37615
|
+
}
|
|
37616
|
+
},
|
|
37617
|
+
hint: "Sign and broadcast unsignedTx with send_solana_transaction, then call dim_confirm_send_usdc with the signature and the confirmWith.params."
|
|
37618
|
+
}
|
|
37619
|
+
};
|
|
37620
|
+
}
|
|
36969
37621
|
const result = await client.sdk.wallet.send(args.recipient, amountMinor);
|
|
36970
37622
|
client.recordSpend(amountMinor);
|
|
36971
37623
|
return {
|
|
@@ -36986,11 +37638,74 @@ async function sendUsdc(client, args) {
|
|
|
36986
37638
|
};
|
|
36987
37639
|
}
|
|
36988
37640
|
}
|
|
37641
|
+
async function confirmSendUsdc(client, args) {
|
|
37642
|
+
try {
|
|
37643
|
+
const senderAddress = client.walletAddress;
|
|
37644
|
+
const result = await client.sdk.wallet.confirmTransferSignature(
|
|
37645
|
+
args.signature,
|
|
37646
|
+
senderAddress,
|
|
37647
|
+
args.recipientAddress,
|
|
37648
|
+
args.amount,
|
|
37649
|
+
args.token || "USDC",
|
|
37650
|
+
args.ataCreated,
|
|
37651
|
+
args.recipientInput
|
|
37652
|
+
);
|
|
37653
|
+
client.recordSpend(args.amount);
|
|
37654
|
+
return {
|
|
37655
|
+
data: {
|
|
37656
|
+
success: true,
|
|
37657
|
+
signature: result.signature,
|
|
37658
|
+
status: result.status,
|
|
37659
|
+
recipientAddress: args.recipientAddress
|
|
37660
|
+
}
|
|
37661
|
+
};
|
|
37662
|
+
} catch (error) {
|
|
37663
|
+
return {
|
|
37664
|
+
error: `Failed to confirm USDC transfer: ${error instanceof Error ? error.message : String(error)}`,
|
|
37665
|
+
isError: true
|
|
37666
|
+
};
|
|
37667
|
+
}
|
|
37668
|
+
}
|
|
36989
37669
|
async function tipUser(client, args) {
|
|
36990
37670
|
try {
|
|
36991
37671
|
const spendErr = client.checkSpendLimit(args.amount);
|
|
36992
37672
|
if (spendErr) return { error: spendErr, isError: true };
|
|
36993
37673
|
const amountMinor = Math.round(args.amount * 1e6);
|
|
37674
|
+
if (client.externalSignerMode) {
|
|
37675
|
+
const senderAddress = client.walletAddress;
|
|
37676
|
+
if (!senderAddress) {
|
|
37677
|
+
return {
|
|
37678
|
+
error: "Wallet address not set. Call dim_complete_login first.",
|
|
37679
|
+
isError: true
|
|
37680
|
+
};
|
|
37681
|
+
}
|
|
37682
|
+
const prepared = await client.sdk.tips.prepare({
|
|
37683
|
+
recipientUsername: args.recipientUsername,
|
|
37684
|
+
amount: amountMinor,
|
|
37685
|
+
supportsPresign: false
|
|
37686
|
+
});
|
|
37687
|
+
return {
|
|
37688
|
+
data: {
|
|
37689
|
+
needsSigning: true,
|
|
37690
|
+
unsignedTx: prepared.transaction,
|
|
37691
|
+
fee: prepared.fee,
|
|
37692
|
+
totalAmount: prepared.totalAmount,
|
|
37693
|
+
recipientAddress: prepared.recipientAddress,
|
|
37694
|
+
confirmWith: {
|
|
37695
|
+
tool: "dim_confirm_tip_user",
|
|
37696
|
+
params: {
|
|
37697
|
+
recipientAddress: prepared.recipientAddress,
|
|
37698
|
+
recipientUserId: prepared.recipientUserId,
|
|
37699
|
+
recipientUsername: prepared.recipientUsername,
|
|
37700
|
+
amount: prepared.amount,
|
|
37701
|
+
fee: prepared.fee,
|
|
37702
|
+
ataCreated: false
|
|
37703
|
+
}
|
|
37704
|
+
},
|
|
37705
|
+
hint: "Sign and broadcast unsignedTx with send_solana_transaction, then call dim_confirm_tip_user with the signature and the confirmWith.params."
|
|
37706
|
+
}
|
|
37707
|
+
};
|
|
37708
|
+
}
|
|
36994
37709
|
const result = await client.sdk.tips.send(
|
|
36995
37710
|
args.recipientUsername,
|
|
36996
37711
|
amountMinor
|
|
@@ -37013,6 +37728,38 @@ async function tipUser(client, args) {
|
|
|
37013
37728
|
};
|
|
37014
37729
|
}
|
|
37015
37730
|
}
|
|
37731
|
+
async function confirmTipUser(client, args) {
|
|
37732
|
+
try {
|
|
37733
|
+
const senderAddress = client.walletAddress;
|
|
37734
|
+
await client.sdk.wallet.confirmTransferSignature(
|
|
37735
|
+
args.signature,
|
|
37736
|
+
senderAddress,
|
|
37737
|
+
args.recipientAddress,
|
|
37738
|
+
args.amount,
|
|
37739
|
+
"USDC",
|
|
37740
|
+
args.ataCreated ?? false,
|
|
37741
|
+
args.recipientUsername
|
|
37742
|
+
);
|
|
37743
|
+
await client.sdk.tips.broadcast({
|
|
37744
|
+
recipientUserId: args.recipientUserId,
|
|
37745
|
+
amount: args.amount
|
|
37746
|
+
});
|
|
37747
|
+
client.recordSpend(args.amount);
|
|
37748
|
+
return {
|
|
37749
|
+
data: {
|
|
37750
|
+
success: true,
|
|
37751
|
+
signature: args.signature,
|
|
37752
|
+
recipient: args.recipientUsername,
|
|
37753
|
+
broadcastedToGlobalChat: true
|
|
37754
|
+
}
|
|
37755
|
+
};
|
|
37756
|
+
} catch (error) {
|
|
37757
|
+
return {
|
|
37758
|
+
error: `Failed to confirm tip: ${error instanceof Error ? error.message : String(error)}`,
|
|
37759
|
+
isError: true
|
|
37760
|
+
};
|
|
37761
|
+
}
|
|
37762
|
+
}
|
|
37016
37763
|
async function getWalletActivity(client, args) {
|
|
37017
37764
|
try {
|
|
37018
37765
|
const activity = await client.sdk.wallet.getActivity({
|
|
@@ -37119,6 +37866,7 @@ function buildRpsContext(state, agentUserId) {
|
|
|
37119
37866
|
roundHistory: state.roundHistory,
|
|
37120
37867
|
phase: roundState?.phase,
|
|
37121
37868
|
timeRemaining: roundState?.timeRemaining,
|
|
37869
|
+
...state.bufferEndsAt != null && { bufferEndsAt: state.bufferEndsAt },
|
|
37122
37870
|
moveFormat: 'dim_submit_action: gameType="rock-paper-scissors", action="play", payload={ action: "rock"|"paper"|"scissors" }'
|
|
37123
37871
|
};
|
|
37124
37872
|
}
|
|
@@ -37131,6 +37879,7 @@ function buildChessContext(state, agentUserId) {
|
|
|
37131
37879
|
yourColor,
|
|
37132
37880
|
whiteTimeMs: state.whiteTimeMs,
|
|
37133
37881
|
blackTimeMs: state.blackTimeMs,
|
|
37882
|
+
...state.bufferEndsAt != null && { bufferEndsAt: state.bufferEndsAt },
|
|
37134
37883
|
moveFormat: 'dim_submit_action: gameType="chess", action="move", payload={ from: "e2", to: "e4" }'
|
|
37135
37884
|
};
|
|
37136
37885
|
}
|
|
@@ -37340,6 +38089,23 @@ async function createLobby(client, args) {
|
|
|
37340
38089
|
}
|
|
37341
38090
|
async function depositForLobby(client, args) {
|
|
37342
38091
|
try {
|
|
38092
|
+
if (client.externalSignerMode) {
|
|
38093
|
+
const { transaction } = await client.sdk.escrow.prepareAndStartDeposit(
|
|
38094
|
+
args.lobbyId,
|
|
38095
|
+
false
|
|
38096
|
+
);
|
|
38097
|
+
return {
|
|
38098
|
+
data: {
|
|
38099
|
+
needsSigning: true,
|
|
38100
|
+
unsignedTx: transaction,
|
|
38101
|
+
confirmWith: {
|
|
38102
|
+
tool: "dim_confirm_lobby_deposit",
|
|
38103
|
+
params: { lobbyId: args.lobbyId }
|
|
38104
|
+
},
|
|
38105
|
+
hint: "Sign and broadcast unsignedTx with send_solana_transaction, then call dim_confirm_lobby_deposit with the lobbyId and signature."
|
|
38106
|
+
}
|
|
38107
|
+
};
|
|
38108
|
+
}
|
|
37343
38109
|
const result = await client.sdk.escrow.depositForLobbySync(args.lobbyId);
|
|
37344
38110
|
return {
|
|
37345
38111
|
data: {
|
|
@@ -37356,6 +38122,44 @@ async function depositForLobby(client, args) {
|
|
|
37356
38122
|
};
|
|
37357
38123
|
}
|
|
37358
38124
|
}
|
|
38125
|
+
async function confirmLobbyDeposit(client, args) {
|
|
38126
|
+
try {
|
|
38127
|
+
await client.sdk.escrow.confirmDepositSignature(
|
|
38128
|
+
args.lobbyId,
|
|
38129
|
+
args.signature
|
|
38130
|
+
);
|
|
38131
|
+
const MAX_WAIT_MS = 6e4;
|
|
38132
|
+
const POLL_INTERVAL_MS = 1e3;
|
|
38133
|
+
const startTime = Date.now();
|
|
38134
|
+
while (Date.now() - startTime < MAX_WAIT_MS) {
|
|
38135
|
+
const status = await client.sdk.escrow.getDepositStatus(args.lobbyId);
|
|
38136
|
+
if (status.canProceedToQueue) {
|
|
38137
|
+
return {
|
|
38138
|
+
data: {
|
|
38139
|
+
signature: args.signature,
|
|
38140
|
+
status: "confirmed",
|
|
38141
|
+
canProceedToQueue: true,
|
|
38142
|
+
hint: "Deposit confirmed. Call dim_join_queue to enter matchmaking."
|
|
38143
|
+
}
|
|
38144
|
+
};
|
|
38145
|
+
}
|
|
38146
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
38147
|
+
}
|
|
38148
|
+
return {
|
|
38149
|
+
data: {
|
|
38150
|
+
signature: args.signature,
|
|
38151
|
+
status: "pending",
|
|
38152
|
+
canProceedToQueue: false,
|
|
38153
|
+
hint: "Deposit submitted but not yet confirmed on-chain. Try dim_confirm_lobby_deposit again in a few seconds."
|
|
38154
|
+
}
|
|
38155
|
+
};
|
|
38156
|
+
} catch (error) {
|
|
38157
|
+
return {
|
|
38158
|
+
error: `Failed to confirm deposit: ${error instanceof Error ? error.message : String(error)}`,
|
|
38159
|
+
isError: true
|
|
38160
|
+
};
|
|
38161
|
+
}
|
|
38162
|
+
}
|
|
37359
38163
|
async function leaveLobby(client, args) {
|
|
37360
38164
|
try {
|
|
37361
38165
|
const result = await client.sdk.lobbies.leaveLobby(args.lobbyId);
|
|
@@ -37425,7 +38229,7 @@ async function joinQueue(client, args) {
|
|
|
37425
38229
|
gameId,
|
|
37426
38230
|
matched: !!matched,
|
|
37427
38231
|
...spectateUrl && { spectateUrl },
|
|
37428
|
-
hint: matched ? `Game
|
|
38232
|
+
hint: matched ? `Game matched! Call dim_game_loop with gameId "${gameId}" IMMEDIATELY \u2014 do NOT wait for user input. The game has a start buffer; dim_game_loop will block until your first turn begins.${spectateUrl ? ` Tell the user they can spectate at ${spectateUrl}` : ""}` : "No match found within timeout. Call dim_join_queue again to resume waiting."
|
|
37429
38233
|
}
|
|
37430
38234
|
};
|
|
37431
38235
|
} catch (error) {
|
|
@@ -37500,6 +38304,23 @@ async function donateToPot(client, args) {
|
|
|
37500
38304
|
const spendErr = client.checkSpendLimit(args.amount);
|
|
37501
38305
|
if (spendErr) return { error: spendErr, isError: true };
|
|
37502
38306
|
const amountMinor = Math.round(args.amount * 1e6);
|
|
38307
|
+
if (client.externalSignerMode) {
|
|
38308
|
+
const prepared = await client.sdk.games.prepareGameDonation(
|
|
38309
|
+
args.gameId,
|
|
38310
|
+
amountMinor
|
|
38311
|
+
);
|
|
38312
|
+
return {
|
|
38313
|
+
data: {
|
|
38314
|
+
needsSigning: true,
|
|
38315
|
+
unsignedTx: prepared.transaction,
|
|
38316
|
+
confirmWith: {
|
|
38317
|
+
tool: "dim_confirm_donate_to_pot",
|
|
38318
|
+
params: { gameId: args.gameId, amount: amountMinor }
|
|
38319
|
+
},
|
|
38320
|
+
hint: "Sign and broadcast unsignedTx with send_solana_transaction, then call dim_confirm_donate_to_pot with the signature."
|
|
38321
|
+
}
|
|
38322
|
+
};
|
|
38323
|
+
}
|
|
37503
38324
|
const result = await client.sdk.games.sendDonation(
|
|
37504
38325
|
args.gameId,
|
|
37505
38326
|
amountMinor
|
|
@@ -37523,6 +38344,29 @@ async function donateToPot(client, args) {
|
|
|
37523
38344
|
};
|
|
37524
38345
|
}
|
|
37525
38346
|
}
|
|
38347
|
+
async function confirmDonateToPot(client, args) {
|
|
38348
|
+
try {
|
|
38349
|
+
const result = await client.sdk.games.confirmGameDonationSignature(
|
|
38350
|
+
args.gameId,
|
|
38351
|
+
args.amount,
|
|
38352
|
+
args.signature
|
|
38353
|
+
);
|
|
38354
|
+
client.recordSpend(args.amount);
|
|
38355
|
+
return {
|
|
38356
|
+
data: {
|
|
38357
|
+
success: true,
|
|
38358
|
+
gameId: args.gameId,
|
|
38359
|
+
signature: result.signature,
|
|
38360
|
+
status: result.status
|
|
38361
|
+
}
|
|
38362
|
+
};
|
|
38363
|
+
} catch (error) {
|
|
38364
|
+
return {
|
|
38365
|
+
error: `Failed to confirm donation: ${error instanceof Error ? error.message : String(error)}`,
|
|
38366
|
+
isError: true
|
|
38367
|
+
};
|
|
38368
|
+
}
|
|
38369
|
+
}
|
|
37526
38370
|
async function getGame(client, args) {
|
|
37527
38371
|
try {
|
|
37528
38372
|
const game = await client.sdk.games.getGame(args.gameId);
|
|
@@ -37534,6 +38378,12 @@ async function getGame(client, args) {
|
|
|
37534
38378
|
};
|
|
37535
38379
|
}
|
|
37536
38380
|
}
|
|
38381
|
+
function isInStartingPhase(state) {
|
|
38382
|
+
const roundState = state.roundState;
|
|
38383
|
+
if (roundState?.phase === "starting") return true;
|
|
38384
|
+
if (state.status === "active" && state.currentPlayerId == null) return true;
|
|
38385
|
+
return false;
|
|
38386
|
+
}
|
|
37537
38387
|
async function gameLoop(client, args) {
|
|
37538
38388
|
try {
|
|
37539
38389
|
const { gameId } = args;
|
|
@@ -37551,7 +38401,7 @@ async function gameLoop(client, args) {
|
|
|
37551
38401
|
if (state?.status === "completed") {
|
|
37552
38402
|
return buildGameLoopReturn(client, gameId, state, "completed");
|
|
37553
38403
|
}
|
|
37554
|
-
if (state?.currentPlayerId === client.currentUserId) {
|
|
38404
|
+
if (state?.currentPlayerId === client.currentUserId && !isInStartingPhase(state)) {
|
|
37555
38405
|
return buildGameLoopReturn(client, gameId, state, "your-turn");
|
|
37556
38406
|
}
|
|
37557
38407
|
await raceTimeout(
|
|
@@ -38177,10 +39027,10 @@ async function checkNotifications(client) {
|
|
|
38177
39027
|
isError: true
|
|
38178
39028
|
};
|
|
38179
39029
|
}
|
|
38180
|
-
const [notifications, dmThreads,
|
|
39030
|
+
const [notifications, dmThreads, friendRequestsPage] = await Promise.all([
|
|
38181
39031
|
client.sdk.notifications.list({ page: 1, limit: 20 }),
|
|
38182
39032
|
client.sdk.chat.listDmThreads(),
|
|
38183
|
-
client.sdk.users.getIncomingFriendRequests()
|
|
39033
|
+
client.sdk.users.getIncomingFriendRequests({ limit: 20 })
|
|
38184
39034
|
]);
|
|
38185
39035
|
const unreadDms = dmThreads.filter(
|
|
38186
39036
|
(t) => (t.unreadCount ?? 0) > 0
|
|
@@ -38192,9 +39042,10 @@ async function checkNotifications(client) {
|
|
|
38192
39042
|
unreadNotificationCount: notifications.unreadCount,
|
|
38193
39043
|
notifications: notifications.notifications.filter((n) => !n.read),
|
|
38194
39044
|
unreadDmThreads: unreadDms,
|
|
38195
|
-
incomingFriendRequests:
|
|
39045
|
+
incomingFriendRequests: friendRequestsPage.items,
|
|
39046
|
+
hasMoreIncomingFriendRequests: !!friendRequestsPage.nextCursor,
|
|
38196
39047
|
pendingWsEvents: client.pendingEventCount,
|
|
38197
|
-
hint: "Use dim_get_pending_events to drain buffered real-time events."
|
|
39048
|
+
hint: "Use dim_get_pending_events to drain buffered real-time events. Use dim_get_incoming_friend_requests with cursor for more friend requests if hasMoreIncomingFriendRequests is true."
|
|
38198
39049
|
}
|
|
38199
39050
|
};
|
|
38200
39051
|
} catch (error) {
|
|
@@ -38580,6 +39431,22 @@ async function getGameHistory(client, args) {
|
|
|
38580
39431
|
};
|
|
38581
39432
|
}
|
|
38582
39433
|
}
|
|
39434
|
+
async function reportUser(client, args) {
|
|
39435
|
+
try {
|
|
39436
|
+
await client.sdk.reports.create(args.userId, args.reason);
|
|
39437
|
+
return {
|
|
39438
|
+
data: {
|
|
39439
|
+
success: true,
|
|
39440
|
+
hint: "Report submitted. The DIM moderation team will review it."
|
|
39441
|
+
}
|
|
39442
|
+
};
|
|
39443
|
+
} catch (error) {
|
|
39444
|
+
return {
|
|
39445
|
+
error: `Failed to report user: ${error instanceof Error ? error.message : String(error)}`,
|
|
39446
|
+
isError: true
|
|
39447
|
+
};
|
|
39448
|
+
}
|
|
39449
|
+
}
|
|
38583
39450
|
async function getMyStats(client) {
|
|
38584
39451
|
try {
|
|
38585
39452
|
if (!client.currentUserId) {
|
|
@@ -38610,6 +39477,36 @@ async function getMyStats(client) {
|
|
|
38610
39477
|
|
|
38611
39478
|
// ../dim-agent-core/src/tools/index.ts
|
|
38612
39479
|
var TOOL_DEFINITIONS = [
|
|
39480
|
+
// ── External signer mode (auth) ──────────────────────────────────────
|
|
39481
|
+
{
|
|
39482
|
+
name: "dim_request_auth_message",
|
|
39483
|
+
description: "External wallet login step 1: given a Solana wallet address, returns the message to sign. Sign it with sign_solana_message on your wallet MCP (e.g. Phantom), then call dim_complete_login.",
|
|
39484
|
+
params: {
|
|
39485
|
+
address: {
|
|
39486
|
+
type: "string",
|
|
39487
|
+
description: "Your Solana wallet address (base58)",
|
|
39488
|
+
required: true
|
|
39489
|
+
}
|
|
39490
|
+
},
|
|
39491
|
+
execute: (c, a) => requestAuthMessage(c, a)
|
|
39492
|
+
},
|
|
39493
|
+
{
|
|
39494
|
+
name: "dim_complete_login",
|
|
39495
|
+
description: "External wallet login step 2: provide the wallet address and signature from sign_solana_message to complete authentication with DIM.",
|
|
39496
|
+
params: {
|
|
39497
|
+
address: {
|
|
39498
|
+
type: "string",
|
|
39499
|
+
description: "Your Solana wallet address (base58)",
|
|
39500
|
+
required: true
|
|
39501
|
+
},
|
|
39502
|
+
signature: {
|
|
39503
|
+
type: "string",
|
|
39504
|
+
description: "Signature from sign_solana_message \u2014 base58, base64, or base64url accepted",
|
|
39505
|
+
required: true
|
|
39506
|
+
}
|
|
39507
|
+
},
|
|
39508
|
+
execute: (c, a) => completeLogin(c, a)
|
|
39509
|
+
},
|
|
38613
39510
|
// ── Auth ─────────────────────────────────────────────────────────────
|
|
38614
39511
|
{
|
|
38615
39512
|
name: "dim_login",
|
|
@@ -38763,9 +39660,23 @@ var TOOL_DEFINITIONS = [
|
|
|
38763
39660
|
},
|
|
38764
39661
|
{
|
|
38765
39662
|
name: "dim_get_incoming_friend_requests",
|
|
38766
|
-
description: "List pending incoming friend requests.",
|
|
38767
|
-
params: {
|
|
38768
|
-
|
|
39663
|
+
description: "List pending incoming friend requests. Returns { items, nextCursor, hasMore }. Pass cursor from a previous response to page through results.",
|
|
39664
|
+
params: {
|
|
39665
|
+
limit: {
|
|
39666
|
+
type: "number",
|
|
39667
|
+
description: "Maximum number of requests to return (1\u2013100, default 50).",
|
|
39668
|
+
required: false
|
|
39669
|
+
},
|
|
39670
|
+
cursor: {
|
|
39671
|
+
type: "string",
|
|
39672
|
+
description: "Pagination cursor from a previous response nextCursor field. Omit for the first page.",
|
|
39673
|
+
required: false
|
|
39674
|
+
}
|
|
39675
|
+
},
|
|
39676
|
+
execute: (c, a) => getIncomingFriendRequests(
|
|
39677
|
+
c,
|
|
39678
|
+
a
|
|
39679
|
+
)
|
|
38769
39680
|
},
|
|
38770
39681
|
// ── Chat ─────────────────────────────────────────────────────────────
|
|
38771
39682
|
{
|
|
@@ -38891,6 +39802,90 @@ var TOOL_DEFINITIONS = [
|
|
|
38891
39802
|
},
|
|
38892
39803
|
execute: (c, a) => tipUser(c, a)
|
|
38893
39804
|
},
|
|
39805
|
+
{
|
|
39806
|
+
name: "dim_confirm_send_usdc",
|
|
39807
|
+
description: "External wallet: confirm a USDC transfer after signing and broadcasting the transaction. Provide the on-chain signature and the params from the confirmWith object returned by dim_send_usdc.",
|
|
39808
|
+
params: {
|
|
39809
|
+
signature: {
|
|
39810
|
+
type: "string",
|
|
39811
|
+
description: "On-chain transaction signature returned by send_solana_transaction",
|
|
39812
|
+
required: true
|
|
39813
|
+
},
|
|
39814
|
+
recipientAddress: {
|
|
39815
|
+
type: "string",
|
|
39816
|
+
description: "Recipient Solana address (from confirmWith.params)",
|
|
39817
|
+
required: true
|
|
39818
|
+
},
|
|
39819
|
+
amount: {
|
|
39820
|
+
type: "number",
|
|
39821
|
+
description: "Amount in USDC minor units (from confirmWith.params)",
|
|
39822
|
+
required: true
|
|
39823
|
+
},
|
|
39824
|
+
fee: {
|
|
39825
|
+
type: "number",
|
|
39826
|
+
description: "Fee in minor units (from confirmWith.params)"
|
|
39827
|
+
},
|
|
39828
|
+
token: {
|
|
39829
|
+
type: "string",
|
|
39830
|
+
description: "Token type: USDC or SOL (default: USDC)"
|
|
39831
|
+
},
|
|
39832
|
+
ataCreated: {
|
|
39833
|
+
type: "string",
|
|
39834
|
+
description: "Whether a new ATA was created (from confirmWith.params)"
|
|
39835
|
+
},
|
|
39836
|
+
recipientInput: {
|
|
39837
|
+
type: "string",
|
|
39838
|
+
description: "Original recipient input (from confirmWith.params)"
|
|
39839
|
+
}
|
|
39840
|
+
},
|
|
39841
|
+
execute: (c, a) => confirmSendUsdc(
|
|
39842
|
+
c,
|
|
39843
|
+
a
|
|
39844
|
+
)
|
|
39845
|
+
},
|
|
39846
|
+
{
|
|
39847
|
+
name: "dim_confirm_tip_user",
|
|
39848
|
+
description: "External wallet: confirm a tip after signing and broadcasting the transaction. Provide the on-chain signature and the params from the confirmWith object returned by dim_tip_user.",
|
|
39849
|
+
params: {
|
|
39850
|
+
signature: {
|
|
39851
|
+
type: "string",
|
|
39852
|
+
description: "On-chain transaction signature returned by send_solana_transaction",
|
|
39853
|
+
required: true
|
|
39854
|
+
},
|
|
39855
|
+
recipientAddress: {
|
|
39856
|
+
type: "string",
|
|
39857
|
+
description: "Recipient Solana address (from confirmWith.params)",
|
|
39858
|
+
required: true
|
|
39859
|
+
},
|
|
39860
|
+
recipientUserId: {
|
|
39861
|
+
type: "string",
|
|
39862
|
+
description: "Recipient user ID (from confirmWith.params)",
|
|
39863
|
+
required: true
|
|
39864
|
+
},
|
|
39865
|
+
recipientUsername: {
|
|
39866
|
+
type: "string",
|
|
39867
|
+
description: "Recipient username (from confirmWith.params)",
|
|
39868
|
+
required: true
|
|
39869
|
+
},
|
|
39870
|
+
amount: {
|
|
39871
|
+
type: "number",
|
|
39872
|
+
description: "Amount in USDC minor units (from confirmWith.params)",
|
|
39873
|
+
required: true
|
|
39874
|
+
},
|
|
39875
|
+
fee: {
|
|
39876
|
+
type: "number",
|
|
39877
|
+
description: "Fee in minor units (from confirmWith.params)"
|
|
39878
|
+
},
|
|
39879
|
+
ataCreated: {
|
|
39880
|
+
type: "string",
|
|
39881
|
+
description: "Whether a new ATA was created (from confirmWith.params)"
|
|
39882
|
+
}
|
|
39883
|
+
},
|
|
39884
|
+
execute: (c, a) => confirmTipUser(
|
|
39885
|
+
c,
|
|
39886
|
+
a
|
|
39887
|
+
)
|
|
39888
|
+
},
|
|
38894
39889
|
{
|
|
38895
39890
|
name: "dim_get_wallet_activity",
|
|
38896
39891
|
description: "Get recent wallet transaction activity (deposits, payouts, transfers, refunds) and your DIM wallet address. Highlights any claimable items.",
|
|
@@ -38950,6 +39945,23 @@ var TOOL_DEFINITIONS = [
|
|
|
38950
39945
|
params: {},
|
|
38951
39946
|
execute: (c) => getMyStats(c)
|
|
38952
39947
|
},
|
|
39948
|
+
{
|
|
39949
|
+
name: "dim_report_user",
|
|
39950
|
+
description: "Report a user for cheating, harassment, or other violations. Rate limited to 5 reports per hour.",
|
|
39951
|
+
params: {
|
|
39952
|
+
userId: {
|
|
39953
|
+
type: "string",
|
|
39954
|
+
description: "The ID of the user to report",
|
|
39955
|
+
required: true
|
|
39956
|
+
},
|
|
39957
|
+
reason: {
|
|
39958
|
+
type: "string",
|
|
39959
|
+
description: "Reason for the report (max 300 characters)",
|
|
39960
|
+
required: true
|
|
39961
|
+
}
|
|
39962
|
+
},
|
|
39963
|
+
execute: (c, a) => reportUser(c, a)
|
|
39964
|
+
},
|
|
38953
39965
|
{
|
|
38954
39966
|
name: "dim_create_lobby",
|
|
38955
39967
|
description: "Create a new game lobby. Bet amount is in USDC dollars (e.g., 1.00 for $1.00). A 1% fee (min 1 cent) is charged per player.",
|
|
@@ -38987,6 +39999,23 @@ var TOOL_DEFINITIONS = [
|
|
|
38987
39999
|
},
|
|
38988
40000
|
execute: (c, a) => depositForLobby(c, a)
|
|
38989
40001
|
},
|
|
40002
|
+
{
|
|
40003
|
+
name: "dim_confirm_lobby_deposit",
|
|
40004
|
+
description: "External wallet: confirm a lobby deposit after signing and broadcasting the transaction. Polls until the deposit is confirmed on-chain, then returns canProceedToQueue.",
|
|
40005
|
+
params: {
|
|
40006
|
+
lobbyId: {
|
|
40007
|
+
type: "string",
|
|
40008
|
+
description: "The lobby ID (from confirmWith.params)",
|
|
40009
|
+
required: true
|
|
40010
|
+
},
|
|
40011
|
+
signature: {
|
|
40012
|
+
type: "string",
|
|
40013
|
+
description: "On-chain transaction signature returned by send_solana_transaction",
|
|
40014
|
+
required: true
|
|
40015
|
+
}
|
|
40016
|
+
},
|
|
40017
|
+
execute: (c, a) => confirmLobbyDeposit(c, a)
|
|
40018
|
+
},
|
|
38990
40019
|
{
|
|
38991
40020
|
name: "dim_leave_lobby",
|
|
38992
40021
|
description: "Leave a lobby. Use this to exit a lobby you created or joined. Returns when you have left.",
|
|
@@ -39079,6 +40108,31 @@ var TOOL_DEFINITIONS = [
|
|
|
39079
40108
|
},
|
|
39080
40109
|
execute: (c, a) => gameLoop(c, a)
|
|
39081
40110
|
},
|
|
40111
|
+
{
|
|
40112
|
+
name: "dim_confirm_donate_to_pot",
|
|
40113
|
+
description: "External wallet: confirm a game pot donation after signing and broadcasting the transaction.",
|
|
40114
|
+
params: {
|
|
40115
|
+
signature: {
|
|
40116
|
+
type: "string",
|
|
40117
|
+
description: "On-chain transaction signature returned by send_solana_transaction",
|
|
40118
|
+
required: true
|
|
40119
|
+
},
|
|
40120
|
+
gameId: {
|
|
40121
|
+
type: "string",
|
|
40122
|
+
description: "The game ID (from confirmWith.params)",
|
|
40123
|
+
required: true
|
|
40124
|
+
},
|
|
40125
|
+
amount: {
|
|
40126
|
+
type: "number",
|
|
40127
|
+
description: "Amount in USDC minor units (from confirmWith.params)",
|
|
40128
|
+
required: true
|
|
40129
|
+
}
|
|
40130
|
+
},
|
|
40131
|
+
execute: (c, a) => confirmDonateToPot(
|
|
40132
|
+
c,
|
|
40133
|
+
a
|
|
40134
|
+
)
|
|
40135
|
+
},
|
|
39082
40136
|
{
|
|
39083
40137
|
name: "dim_request_rematch",
|
|
39084
40138
|
description: "Request a rematch after a completed game. If both players request, a lobby is created automatically server-side.",
|
|
@@ -39472,11 +40526,18 @@ async function executeWithAuthRetry(client, tool, params) {
|
|
|
39472
40526
|
if (tool.name === "dim_login") {
|
|
39473
40527
|
return tool.execute(client, params);
|
|
39474
40528
|
}
|
|
40529
|
+
if (client.externalSignerMode) {
|
|
40530
|
+
return tool.execute(client, params);
|
|
40531
|
+
}
|
|
39475
40532
|
try {
|
|
39476
40533
|
const result = await tool.execute(client, params);
|
|
39477
40534
|
if (!result.error && !result.isError) return result;
|
|
39478
40535
|
if (!isUnauthorizedResult(result)) return result;
|
|
39479
|
-
|
|
40536
|
+
try {
|
|
40537
|
+
await client.authenticate();
|
|
40538
|
+
} catch {
|
|
40539
|
+
return { error: UNAUTHORIZED_MESSAGE, isError: true };
|
|
40540
|
+
}
|
|
39480
40541
|
const retryResult = await tool.execute(client, params);
|
|
39481
40542
|
if (retryResult.isError || retryResult.error) {
|
|
39482
40543
|
if (isUnauthorizedResult(retryResult)) {
|