@dimcool/mcp 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/README.md +173 -117
- package/dist/index.js +1245 -173
- package/dist/server.d.ts +31 -0
- package/dist/server.js +19465 -0
- package/package.json +5 -1
package/dist/index.js
CHANGED
|
@@ -7328,8 +7328,6 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
7328
7328
|
import { Transaction } from "@solana/web3.js";
|
|
7329
7329
|
import { Transaction as Transaction2 } from "@solana/web3.js";
|
|
7330
7330
|
import { Transaction as Transaction3 } from "@solana/web3.js";
|
|
7331
|
-
import { Transaction as Transaction4 } from "@solana/web3.js";
|
|
7332
|
-
import { Transaction as Transaction5 } from "@solana/web3.js";
|
|
7333
7331
|
|
|
7334
7332
|
// ../../node_modules/engine.io-client/build/esm-debug/transports/polling-xhr.node.js
|
|
7335
7333
|
var XMLHttpRequestModule = __toESM(require_XMLHttpRequest(), 1);
|
|
@@ -11587,6 +11585,16 @@ var Auth = class {
|
|
|
11587
11585
|
this.logger.debug("Token stored in storage", { tokenKey: TOKEN_KEY });
|
|
11588
11586
|
return response;
|
|
11589
11587
|
}
|
|
11588
|
+
async loginWithExternalSignature(address, signedMessage, options) {
|
|
11589
|
+
const response = await this.http.post("/auth/login-wallet", {
|
|
11590
|
+
signedMessage,
|
|
11591
|
+
address,
|
|
11592
|
+
referralCode: options?.referralCode,
|
|
11593
|
+
walletMeta: options?.walletMeta
|
|
11594
|
+
});
|
|
11595
|
+
this.storage.set(TOKEN_KEY, response.access_token);
|
|
11596
|
+
return response;
|
|
11597
|
+
}
|
|
11590
11598
|
logout() {
|
|
11591
11599
|
this.logger.debug("Auth.logout called");
|
|
11592
11600
|
const hadToken = this.storage.get(TOKEN_KEY) !== null;
|
|
@@ -11600,6 +11608,10 @@ var Auth = class {
|
|
|
11600
11608
|
});
|
|
11601
11609
|
return isAuth;
|
|
11602
11610
|
}
|
|
11611
|
+
async getSessionStats() {
|
|
11612
|
+
this.logger.debug("Auth.getSessionStats called");
|
|
11613
|
+
return this.http.get("/auth/sessions/stats");
|
|
11614
|
+
}
|
|
11603
11615
|
async getLatestSessions(limit) {
|
|
11604
11616
|
this.logger.debug("Auth.getLatestSessions called", { limit });
|
|
11605
11617
|
const params = new URLSearchParams();
|
|
@@ -11618,6 +11630,9 @@ var Admin = class {
|
|
|
11618
11630
|
this.http = http;
|
|
11619
11631
|
this.logger = logger2;
|
|
11620
11632
|
}
|
|
11633
|
+
async getInternalBots() {
|
|
11634
|
+
return this.http.get("/admin/internal-bots");
|
|
11635
|
+
}
|
|
11621
11636
|
async getUserById(id) {
|
|
11622
11637
|
return this.http.get(`/admin/users/${id}`);
|
|
11623
11638
|
}
|
|
@@ -11823,11 +11838,25 @@ var Users = class {
|
|
|
11823
11838
|
async removeFriend(userId) {
|
|
11824
11839
|
return this.http.delete(`/friends/${userId}`);
|
|
11825
11840
|
}
|
|
11826
|
-
async getIncomingFriendRequests() {
|
|
11827
|
-
|
|
11841
|
+
async getIncomingFriendRequests(opts) {
|
|
11842
|
+
const params = new URLSearchParams();
|
|
11843
|
+
if (opts?.limit !== void 0)
|
|
11844
|
+
params.append("limit", opts.limit.toString());
|
|
11845
|
+
if (opts?.cursor !== void 0) params.append("cursor", opts.cursor);
|
|
11846
|
+
const qs = params.toString();
|
|
11847
|
+
return this.http.get(
|
|
11848
|
+
qs ? `/friends/requests/incoming?${qs}` : "/friends/requests/incoming"
|
|
11849
|
+
);
|
|
11828
11850
|
}
|
|
11829
|
-
async getOutgoingFriendRequests() {
|
|
11830
|
-
|
|
11851
|
+
async getOutgoingFriendRequests(opts) {
|
|
11852
|
+
const params = new URLSearchParams();
|
|
11853
|
+
if (opts?.limit !== void 0)
|
|
11854
|
+
params.append("limit", opts.limit.toString());
|
|
11855
|
+
if (opts?.cursor !== void 0) params.append("cursor", opts.cursor);
|
|
11856
|
+
const qs = params.toString();
|
|
11857
|
+
return this.http.get(
|
|
11858
|
+
qs ? `/friends/requests/outgoing?${qs}` : "/friends/requests/outgoing"
|
|
11859
|
+
);
|
|
11831
11860
|
}
|
|
11832
11861
|
async acceptFriendRequest(userId) {
|
|
11833
11862
|
return this.http.post(
|
|
@@ -11914,6 +11943,9 @@ var FeatureFlags = class {
|
|
|
11914
11943
|
this.loaded = true;
|
|
11915
11944
|
return this.flags;
|
|
11916
11945
|
}
|
|
11946
|
+
async getAdminFeatureFlags() {
|
|
11947
|
+
return this.http.get("/feature-flags/admin");
|
|
11948
|
+
}
|
|
11917
11949
|
isEnabledFlag(name) {
|
|
11918
11950
|
const flag = this.flags.find((f) => f.name === name);
|
|
11919
11951
|
return flag?.enabled ?? false;
|
|
@@ -11933,10 +11965,11 @@ var FeatureFlags = class {
|
|
|
11933
11965
|
}
|
|
11934
11966
|
return flag;
|
|
11935
11967
|
}
|
|
11936
|
-
async createFeatureFlag(name, enabled) {
|
|
11968
|
+
async createFeatureFlag(name, enabled, description) {
|
|
11937
11969
|
const flag = await this.http.post("/feature-flags", {
|
|
11938
11970
|
name,
|
|
11939
|
-
enabled
|
|
11971
|
+
enabled,
|
|
11972
|
+
...description !== void 0 && { description }
|
|
11940
11973
|
});
|
|
11941
11974
|
this.flags.push(flag);
|
|
11942
11975
|
return flag;
|
|
@@ -11947,11 +11980,17 @@ var Lobbies = class {
|
|
|
11947
11980
|
this.http = http;
|
|
11948
11981
|
this.logger = logger2;
|
|
11949
11982
|
}
|
|
11983
|
+
/** Called by SDK after the store is created so mutations can update state directly. */
|
|
11984
|
+
setLobbyStore(store) {
|
|
11985
|
+
this.lobbyStore = store;
|
|
11986
|
+
}
|
|
11950
11987
|
async createLobby(gameType, betAmount) {
|
|
11951
11988
|
return this.http.post("/lobbies", { gameType, betAmount });
|
|
11952
11989
|
}
|
|
11953
11990
|
async getLobby(lobbyId) {
|
|
11954
|
-
|
|
11991
|
+
const lobby = await this.http.get(`/lobbies/${lobbyId}`);
|
|
11992
|
+
this.lobbyStore?.setBaseState([lobby]);
|
|
11993
|
+
return lobby;
|
|
11955
11994
|
}
|
|
11956
11995
|
async inviteFriend(lobbyId, friendId) {
|
|
11957
11996
|
return this.http.post(`/lobbies/${lobbyId}/invite`, {
|
|
@@ -11962,7 +12001,9 @@ var Lobbies = class {
|
|
|
11962
12001
|
return this.http.post(`/lobbies/${lobbyId}/accept-invite`, {});
|
|
11963
12002
|
}
|
|
11964
12003
|
async joinLobby(lobbyId) {
|
|
11965
|
-
|
|
12004
|
+
const lobby = await this.http.post(`/lobbies/${lobbyId}/join`, {});
|
|
12005
|
+
this.lobbyStore?.setBaseState([lobby]);
|
|
12006
|
+
return lobby;
|
|
11966
12007
|
}
|
|
11967
12008
|
async removePlayer(lobbyId, userId) {
|
|
11968
12009
|
return this.http.delete(
|
|
@@ -11979,12 +12020,17 @@ var Lobbies = class {
|
|
|
11979
12020
|
return this.http.post(`/lobbies/${lobbyId}/join-queue`, {});
|
|
11980
12021
|
}
|
|
11981
12022
|
async cancelQueue(lobbyId) {
|
|
11982
|
-
|
|
12023
|
+
const lobby = await this.http.delete(`/lobbies/${lobbyId}/queue`);
|
|
12024
|
+
this.lobbyStore?.setBaseState([lobby]);
|
|
12025
|
+
return lobby;
|
|
11983
12026
|
}
|
|
11984
12027
|
async updateBetAmount(lobbyId, betAmount) {
|
|
11985
|
-
|
|
11986
|
-
|
|
11987
|
-
|
|
12028
|
+
const lobby = await this.http.patch(
|
|
12029
|
+
`/lobbies/${lobbyId}/bet-amount`,
|
|
12030
|
+
{ betAmount }
|
|
12031
|
+
);
|
|
12032
|
+
this.lobbyStore?.setBaseState([lobby]);
|
|
12033
|
+
return lobby;
|
|
11988
12034
|
}
|
|
11989
12035
|
async playSound(lobbyId, sound) {
|
|
11990
12036
|
return this.http.post(`/lobbies/${lobbyId}/sound`, {
|
|
@@ -12023,6 +12069,12 @@ var Games = class {
|
|
|
12023
12069
|
this.wallet = wallet;
|
|
12024
12070
|
this.logger = logger2;
|
|
12025
12071
|
}
|
|
12072
|
+
setGameStore(store) {
|
|
12073
|
+
this.gameStore = store;
|
|
12074
|
+
}
|
|
12075
|
+
setGameActionsStore(store) {
|
|
12076
|
+
this.gameActionsStore = store;
|
|
12077
|
+
}
|
|
12026
12078
|
async getAvailableGames() {
|
|
12027
12079
|
return this.http.get("/games/available");
|
|
12028
12080
|
}
|
|
@@ -12035,7 +12087,11 @@ var Games = class {
|
|
|
12035
12087
|
return this.http.get("/games/metrics");
|
|
12036
12088
|
}
|
|
12037
12089
|
async getGame(gameId) {
|
|
12038
|
-
|
|
12090
|
+
const existing = this.gameStore?.store.getState().gamesById[gameId];
|
|
12091
|
+
if (existing?.status === "completed") return existing;
|
|
12092
|
+
const game = await this.http.get(`/games/${gameId}`);
|
|
12093
|
+
this.gameStore?.setBaseState([game]);
|
|
12094
|
+
return game;
|
|
12039
12095
|
}
|
|
12040
12096
|
/**
|
|
12041
12097
|
* Get list of currently active (live) games. Public endpoint for spectating.
|
|
@@ -12062,7 +12118,13 @@ var Games = class {
|
|
|
12062
12118
|
* Get current game state with timer information
|
|
12063
12119
|
*/
|
|
12064
12120
|
async getGameState(gameId) {
|
|
12065
|
-
|
|
12121
|
+
const existing = this.gameActionsStore?.store.getState().statesByGameId[gameId];
|
|
12122
|
+
if (existing?.status === "completed") return existing;
|
|
12123
|
+
const state = await this.http.get(
|
|
12124
|
+
`/games/${gameId}/state`
|
|
12125
|
+
);
|
|
12126
|
+
this.gameActionsStore?.setBaseState(gameId, state);
|
|
12127
|
+
return state;
|
|
12066
12128
|
}
|
|
12067
12129
|
/**
|
|
12068
12130
|
* Request a rematch for a completed game
|
|
@@ -12098,8 +12160,18 @@ var Games = class {
|
|
|
12098
12160
|
{ amountMinor, signedTransaction }
|
|
12099
12161
|
);
|
|
12100
12162
|
}
|
|
12163
|
+
/**
|
|
12164
|
+
* Confirm a donation that was already sent by the client (signAndSendTransaction path).
|
|
12165
|
+
*/
|
|
12166
|
+
async confirmGameDonationSignature(gameId, amountMinor, signature) {
|
|
12167
|
+
return this.http.post(
|
|
12168
|
+
`/games/${gameId}/donate/confirm-signature`,
|
|
12169
|
+
{ amountMinor, signature }
|
|
12170
|
+
);
|
|
12171
|
+
}
|
|
12101
12172
|
/**
|
|
12102
12173
|
* One-call donation flow: prepare -> sign -> submit.
|
|
12174
|
+
* Automatically uses the right signing path (embedded vs injected wallet).
|
|
12103
12175
|
*/
|
|
12104
12176
|
async sendDonation(gameId, amountMinor) {
|
|
12105
12177
|
if (!this.wallet.hasSigner()) {
|
|
@@ -12108,16 +12180,10 @@ var Games = class {
|
|
|
12108
12180
|
);
|
|
12109
12181
|
}
|
|
12110
12182
|
const prepared = await this.prepareGameDonation(gameId, amountMinor);
|
|
12111
|
-
const
|
|
12112
|
-
|
|
12113
|
-
|
|
12114
|
-
|
|
12115
|
-
);
|
|
12116
|
-
const result = await this.donateToGame(
|
|
12117
|
-
gameId,
|
|
12118
|
-
amountMinor,
|
|
12119
|
-
signedTransaction
|
|
12120
|
-
);
|
|
12183
|
+
const result = await this.wallet.signAndDispatch(prepared.transaction, {
|
|
12184
|
+
onSigned: (signedTxBase64) => this.donateToGame(gameId, amountMinor, signedTxBase64),
|
|
12185
|
+
onSignedAndSent: (sig) => this.confirmGameDonationSignature(gameId, amountMinor, sig)
|
|
12186
|
+
});
|
|
12121
12187
|
return {
|
|
12122
12188
|
...result,
|
|
12123
12189
|
escrowAddress: prepared.escrowAddress,
|
|
@@ -12210,6 +12276,9 @@ var Chat = class {
|
|
|
12210
12276
|
this.logger = logger2;
|
|
12211
12277
|
this.retryOptions = { ...DEFAULT_RETRY_OPTIONS, ...retryOptions };
|
|
12212
12278
|
}
|
|
12279
|
+
setDmThreadsStore(store) {
|
|
12280
|
+
this.dmThreadsStore = store;
|
|
12281
|
+
}
|
|
12213
12282
|
encodeContextId(id) {
|
|
12214
12283
|
return encodeURIComponent(id);
|
|
12215
12284
|
}
|
|
@@ -12291,7 +12360,9 @@ var Chat = class {
|
|
|
12291
12360
|
return response;
|
|
12292
12361
|
}
|
|
12293
12362
|
async listDmThreads() {
|
|
12294
|
-
|
|
12363
|
+
const threads = await this.http.get("/chat/dm/threads");
|
|
12364
|
+
this.dmThreadsStore?.setBaseState(threads);
|
|
12365
|
+
return threads;
|
|
12295
12366
|
}
|
|
12296
12367
|
async getDmThread(dmKey) {
|
|
12297
12368
|
return this.http.get(
|
|
@@ -12379,21 +12450,32 @@ var Tips = class {
|
|
|
12379
12450
|
);
|
|
12380
12451
|
}
|
|
12381
12452
|
const { publicKey: senderAddress } = await this.wallet.loadWallet();
|
|
12382
|
-
const
|
|
12383
|
-
const
|
|
12384
|
-
|
|
12385
|
-
|
|
12386
|
-
|
|
12387
|
-
);
|
|
12388
|
-
const transfer = await this.wallet.
|
|
12389
|
-
signedTxBase64
|
|
12390
|
-
|
|
12391
|
-
|
|
12392
|
-
|
|
12393
|
-
|
|
12394
|
-
|
|
12395
|
-
|
|
12396
|
-
|
|
12453
|
+
const supportsPresign = !this.wallet.isSignAndSendMode();
|
|
12454
|
+
const prepared = await this.prepare({
|
|
12455
|
+
recipientUsername,
|
|
12456
|
+
amount,
|
|
12457
|
+
supportsPresign
|
|
12458
|
+
});
|
|
12459
|
+
const transfer = await this.wallet.signAndDispatch(prepared.transaction, {
|
|
12460
|
+
onSigned: (signedTxBase64) => this.wallet.submitTransfer(
|
|
12461
|
+
signedTxBase64,
|
|
12462
|
+
senderAddress,
|
|
12463
|
+
prepared.recipientAddress,
|
|
12464
|
+
prepared.amount,
|
|
12465
|
+
"USDC",
|
|
12466
|
+
false,
|
|
12467
|
+
prepared.recipientUsername
|
|
12468
|
+
),
|
|
12469
|
+
onSignedAndSent: (sig) => this.wallet.confirmTransferSignature(
|
|
12470
|
+
sig,
|
|
12471
|
+
senderAddress,
|
|
12472
|
+
prepared.recipientAddress,
|
|
12473
|
+
prepared.amount,
|
|
12474
|
+
"USDC",
|
|
12475
|
+
false,
|
|
12476
|
+
prepared.recipientUsername
|
|
12477
|
+
)
|
|
12478
|
+
});
|
|
12397
12479
|
const message = await this.chat.broadcastGlobalTip(
|
|
12398
12480
|
prepared.recipientUserId,
|
|
12399
12481
|
prepared.amount
|
|
@@ -12711,13 +12793,15 @@ var Wallet = class {
|
|
|
12711
12793
|
);
|
|
12712
12794
|
}
|
|
12713
12795
|
try {
|
|
12796
|
+
const supportsPresign = !this.isSignAndSendMode();
|
|
12714
12797
|
const response = await this.http.post(
|
|
12715
12798
|
"/wallets/transfer/prepare",
|
|
12716
12799
|
{
|
|
12717
12800
|
senderAddress,
|
|
12718
12801
|
recipient,
|
|
12719
12802
|
amount: amountMinor,
|
|
12720
|
-
token
|
|
12803
|
+
token,
|
|
12804
|
+
supportsPresign
|
|
12721
12805
|
}
|
|
12722
12806
|
);
|
|
12723
12807
|
this.logger.debug("Transfer prepared", {
|
|
@@ -12801,6 +12885,29 @@ var Wallet = class {
|
|
|
12801
12885
|
throw error;
|
|
12802
12886
|
}
|
|
12803
12887
|
}
|
|
12888
|
+
/**
|
|
12889
|
+
* Sign a prepared transaction and invoke the appropriate backend callback
|
|
12890
|
+
* based on the configured signer — without exposing the mode decision to callers.
|
|
12891
|
+
* - Embedded wallet (signAndSend): signs+sends, calls onSignedAndSent(signature)
|
|
12892
|
+
* - Injected wallet (signTransaction): signs locally, calls onSigned(signedTxBase64)
|
|
12893
|
+
*/
|
|
12894
|
+
async signAndDispatch(unsignedTxBase64, handlers) {
|
|
12895
|
+
if (!this.signer) {
|
|
12896
|
+
throw new Error(
|
|
12897
|
+
"No signer configured. Call setSigner() with a WalletSigner implementation first."
|
|
12898
|
+
);
|
|
12899
|
+
}
|
|
12900
|
+
const unsignedTx = Transaction2.from(base64ToBytes(unsignedTxBase64));
|
|
12901
|
+
if (this.isSignAndSendMode()) {
|
|
12902
|
+
const sig = await this.signAndSendTransaction(unsignedTx);
|
|
12903
|
+
return handlers.onSignedAndSent(sig);
|
|
12904
|
+
}
|
|
12905
|
+
const signedTx = await this.signTransaction(unsignedTx);
|
|
12906
|
+
const signedBase64 = bytesToBase64(
|
|
12907
|
+
signedTx.serialize({ requireAllSignatures: false })
|
|
12908
|
+
);
|
|
12909
|
+
return handlers.onSigned(signedBase64);
|
|
12910
|
+
}
|
|
12804
12911
|
/**
|
|
12805
12912
|
* Full transfer flow in one call: prepare -> sign -> submit
|
|
12806
12913
|
* Recipient can be username, .sol domain, or Solana address (resolved by backend).
|
|
@@ -12817,7 +12924,7 @@ var Wallet = class {
|
|
|
12817
12924
|
amount,
|
|
12818
12925
|
token
|
|
12819
12926
|
);
|
|
12820
|
-
const unsignedTx =
|
|
12927
|
+
const unsignedTx = Transaction2.from(base64ToBytes(prepared.transaction));
|
|
12821
12928
|
let submitted;
|
|
12822
12929
|
if (this.isSignAndSendMode()) {
|
|
12823
12930
|
const signature = await this.signAndSendTransaction(unsignedTx);
|
|
@@ -12882,12 +12989,32 @@ var Escrow = class {
|
|
|
12882
12989
|
* initializes depositStatus, and prepares the unsigned transaction in one call.
|
|
12883
12990
|
* Eliminates one HTTP round-trip vs startDeposits() + prepareDepositTransaction().
|
|
12884
12991
|
*/
|
|
12885
|
-
async prepareAndStartDeposit(lobbyId) {
|
|
12992
|
+
async prepareAndStartDeposit(lobbyId, supportsPresign) {
|
|
12993
|
+
const presign = supportsPresign ?? !this.wallet.isSignAndSendMode();
|
|
12886
12994
|
return this.http.post(
|
|
12887
12995
|
`/escrow/lobby/${lobbyId}/deposit/prepare-and-start`,
|
|
12888
|
-
{}
|
|
12996
|
+
{ supportsPresign: presign }
|
|
12889
12997
|
);
|
|
12890
12998
|
}
|
|
12999
|
+
/**
|
|
13000
|
+
* Sign and submit a prepared (unsigned) deposit transaction using the
|
|
13001
|
+
* configured signer. Automatically picks the right path:
|
|
13002
|
+
* - Embedded wallet: signAndSendTransaction → confirmDepositSignature
|
|
13003
|
+
* - Injected wallet: signTransaction → submitDeposit
|
|
13004
|
+
* Returns the on-chain signature.
|
|
13005
|
+
*/
|
|
13006
|
+
async signAndSubmitPreparedDeposit(lobbyId, unsignedTxBase64) {
|
|
13007
|
+
return this.wallet.signAndDispatch(unsignedTxBase64, {
|
|
13008
|
+
onSigned: async (signedTxBase64) => {
|
|
13009
|
+
const result = await this.submitDeposit(lobbyId, signedTxBase64);
|
|
13010
|
+
return result.signature;
|
|
13011
|
+
},
|
|
13012
|
+
onSignedAndSent: async (signature) => {
|
|
13013
|
+
await this.confirmDepositSignature(lobbyId, signature);
|
|
13014
|
+
return signature;
|
|
13015
|
+
}
|
|
13016
|
+
});
|
|
13017
|
+
}
|
|
12891
13018
|
/**
|
|
12892
13019
|
* Submit a signed deposit transaction
|
|
12893
13020
|
* The transaction will be submitted to the Solana network and confirmed
|
|
@@ -12930,8 +13057,12 @@ var Escrow = class {
|
|
|
12930
13057
|
"No signer configured. Use sdk.wallet.setSigner(...) first."
|
|
12931
13058
|
);
|
|
12932
13059
|
}
|
|
12933
|
-
const
|
|
12934
|
-
const
|
|
13060
|
+
const supportsPresign = !this.wallet.isSignAndSendMode();
|
|
13061
|
+
const { transaction } = await this.prepareAndStartDeposit(
|
|
13062
|
+
lobbyId,
|
|
13063
|
+
supportsPresign
|
|
13064
|
+
);
|
|
13065
|
+
const unsignedTx = Transaction3.from(base64ToBytes(transaction));
|
|
12935
13066
|
let signature;
|
|
12936
13067
|
if (this.wallet.isSignAndSendMode()) {
|
|
12937
13068
|
signature = await this.wallet.signAndSendTransaction(unsignedTx);
|
|
@@ -12965,8 +13096,13 @@ var Escrow = class {
|
|
|
12965
13096
|
};
|
|
12966
13097
|
}
|
|
12967
13098
|
/**
|
|
12968
|
-
*
|
|
12969
|
-
*
|
|
13099
|
+
* Deposit for a lobby and wait until the calling user's own deposit is confirmed.
|
|
13100
|
+
* Ideal for agents: returns as soon as your deposit is on-chain without waiting
|
|
13101
|
+
* for the other player. The server auto-joins the queue when all players deposit.
|
|
13102
|
+
*
|
|
13103
|
+
* Confirmation is handled asynchronously by the transaction queue processor.
|
|
13104
|
+
* This method polls the deposit status endpoint until the signature appears as
|
|
13105
|
+
* confirmed (up to 60 seconds).
|
|
12970
13106
|
*
|
|
12971
13107
|
* Automatically uses signAndSendTransaction or signTransaction based on signer capability.
|
|
12972
13108
|
*/
|
|
@@ -12976,26 +13112,45 @@ var Escrow = class {
|
|
|
12976
13112
|
"No signer configured. Use sdk.wallet.setSigner(...) first."
|
|
12977
13113
|
);
|
|
12978
13114
|
}
|
|
12979
|
-
const
|
|
12980
|
-
const
|
|
13115
|
+
const supportsPresign2 = !this.wallet.isSignAndSendMode();
|
|
13116
|
+
const { transaction } = await this.prepareAndStartDeposit(
|
|
13117
|
+
lobbyId,
|
|
13118
|
+
supportsPresign2
|
|
13119
|
+
);
|
|
13120
|
+
const unsignedTx = Transaction3.from(base64ToBytes(transaction));
|
|
12981
13121
|
let signature;
|
|
12982
13122
|
if (this.wallet.isSignAndSendMode()) {
|
|
12983
13123
|
signature = await this.wallet.signAndSendTransaction(unsignedTx);
|
|
12984
|
-
await this.
|
|
12985
|
-
`/escrow/lobby/${lobbyId}/deposit/confirm-signature?awaitConfirmation=true`,
|
|
12986
|
-
{ signature }
|
|
12987
|
-
);
|
|
13124
|
+
await this.confirmDepositSignature(lobbyId, signature);
|
|
12988
13125
|
} else {
|
|
12989
13126
|
const signedTx = await this.wallet.signTransaction(unsignedTx);
|
|
12990
13127
|
const signedBase64 = bytesToBase64(
|
|
12991
13128
|
signedTx.serialize({ requireAllSignatures: false })
|
|
12992
13129
|
);
|
|
12993
|
-
const
|
|
12994
|
-
|
|
12995
|
-
|
|
12996
|
-
|
|
13130
|
+
const submitResult = await this.submitDeposit(lobbyId, signedBase64);
|
|
13131
|
+
signature = submitResult.signature;
|
|
13132
|
+
}
|
|
13133
|
+
const MAX_WAIT_MS = 6e4;
|
|
13134
|
+
const POLL_INTERVAL_MS = 1e3;
|
|
13135
|
+
const startTime = Date.now();
|
|
13136
|
+
while (Date.now() - startTime < MAX_WAIT_MS) {
|
|
13137
|
+
const status = await this.getDepositStatus(lobbyId);
|
|
13138
|
+
const myDeposit = status.deposits.find(
|
|
13139
|
+
(d) => d.transactionHash === signature
|
|
13140
|
+
);
|
|
13141
|
+
if (myDeposit?.status === "confirmed") {
|
|
13142
|
+
return {
|
|
13143
|
+
signature,
|
|
13144
|
+
status: "confirmed",
|
|
13145
|
+
canProceedToQueue: status.canProceedToQueue
|
|
13146
|
+
};
|
|
13147
|
+
}
|
|
13148
|
+
if (status.allConfirmed && status.canProceedToQueue) {
|
|
13149
|
+
return { signature, status: "confirmed", canProceedToQueue: true };
|
|
13150
|
+
}
|
|
13151
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
12997
13152
|
}
|
|
12998
|
-
return { signature, status: "
|
|
13153
|
+
return { signature, status: "pending", canProceedToQueue: false };
|
|
12999
13154
|
}
|
|
13000
13155
|
async claimLobbyDepositRefund(lobbyId, depositSignature) {
|
|
13001
13156
|
return this.http.post(
|
|
@@ -13337,9 +13492,10 @@ var Support = class {
|
|
|
13337
13492
|
}
|
|
13338
13493
|
};
|
|
13339
13494
|
var Markets = class {
|
|
13340
|
-
constructor(http, logger2) {
|
|
13495
|
+
constructor(http, logger2, wallet) {
|
|
13341
13496
|
this.http = http;
|
|
13342
13497
|
this.logger = logger2;
|
|
13498
|
+
this.wallet = wallet;
|
|
13343
13499
|
}
|
|
13344
13500
|
/**
|
|
13345
13501
|
* Get the prediction market state for a game.
|
|
@@ -13381,6 +13537,35 @@ var Markets = class {
|
|
|
13381
13537
|
{ signedTransaction, outcomeId, amountMinor }
|
|
13382
13538
|
);
|
|
13383
13539
|
}
|
|
13540
|
+
/**
|
|
13541
|
+
* Confirm a buy order that was already broadcast by the client (signAndSendTransaction path).
|
|
13542
|
+
*/
|
|
13543
|
+
async confirmBuyOrderSignature(gameId, depositSignature, outcomeId, amountMinor) {
|
|
13544
|
+
return this.http.post(
|
|
13545
|
+
`/games/${gameId}/market/orders/buy/confirm-signature`,
|
|
13546
|
+
{ depositSignature, outcomeId, amountMinor }
|
|
13547
|
+
);
|
|
13548
|
+
}
|
|
13549
|
+
/**
|
|
13550
|
+
* One-call buy order: prepare → sign → submit, automatically choosing
|
|
13551
|
+
* the right signing path (embedded vs injected wallet).
|
|
13552
|
+
*/
|
|
13553
|
+
async buy(gameId, outcomeId, amountMinor) {
|
|
13554
|
+
if (!this.wallet?.hasSigner()) {
|
|
13555
|
+
throw new Error(
|
|
13556
|
+
"No signer configured. Use sdk.wallet.setSigner(...) first."
|
|
13557
|
+
);
|
|
13558
|
+
}
|
|
13559
|
+
const { transaction } = await this.prepareBuyOrder(
|
|
13560
|
+
gameId,
|
|
13561
|
+
outcomeId,
|
|
13562
|
+
amountMinor
|
|
13563
|
+
);
|
|
13564
|
+
return this.wallet.signAndDispatch(transaction, {
|
|
13565
|
+
onSigned: (signedTxBase64) => this.submitBuyOrder(gameId, signedTxBase64, outcomeId, amountMinor),
|
|
13566
|
+
onSignedAndSent: (sig) => this.confirmBuyOrderSignature(gameId, sig, outcomeId, amountMinor)
|
|
13567
|
+
});
|
|
13568
|
+
}
|
|
13384
13569
|
/**
|
|
13385
13570
|
* Sell shares back to the AMM pool.
|
|
13386
13571
|
* @param gameId - The game ID
|
|
@@ -13429,6 +13614,20 @@ var Markets = class {
|
|
|
13429
13614
|
return this.http.get(`/games/admin/markets${qs ? `?${qs}` : ""}`);
|
|
13430
13615
|
}
|
|
13431
13616
|
};
|
|
13617
|
+
var NoopAnalyticsClient = class {
|
|
13618
|
+
userLoggedIn(_user, _meta) {
|
|
13619
|
+
}
|
|
13620
|
+
userLoggedOut() {
|
|
13621
|
+
}
|
|
13622
|
+
sessionRestored(_user) {
|
|
13623
|
+
}
|
|
13624
|
+
track(_event, _properties) {
|
|
13625
|
+
}
|
|
13626
|
+
setUserProperties(_properties) {
|
|
13627
|
+
}
|
|
13628
|
+
group(_groupType, _groupKey, _properties) {
|
|
13629
|
+
}
|
|
13630
|
+
};
|
|
13432
13631
|
var BaseWsTransport = class {
|
|
13433
13632
|
constructor() {
|
|
13434
13633
|
this.roomRefs = /* @__PURE__ */ new Map();
|
|
@@ -13516,21 +13715,35 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
|
|
|
13516
13715
|
this.accessToken = null;
|
|
13517
13716
|
this.reconnectAttempts = 0;
|
|
13518
13717
|
this.reconnectTimer = null;
|
|
13519
|
-
this.
|
|
13520
|
-
this.
|
|
13521
|
-
this.reconnectDelay = 1e3;
|
|
13522
|
-
this.reconnectDelayMax = 3e4;
|
|
13718
|
+
this.needsReconnect = false;
|
|
13719
|
+
this.visibilityHandler = null;
|
|
13523
13720
|
this.wildcardHandlers = /* @__PURE__ */ new Set();
|
|
13524
13721
|
this.eventHandlers = /* @__PURE__ */ new Map();
|
|
13525
13722
|
this.registeredEvents = /* @__PURE__ */ new Set();
|
|
13526
13723
|
this.wildcardRegistered = false;
|
|
13527
13724
|
this.url = url2;
|
|
13725
|
+
if (typeof document !== "undefined") {
|
|
13726
|
+
this.visibilityHandler = () => {
|
|
13727
|
+
if (document.visibilityState === "visible" && !this.connectionState.connected) {
|
|
13728
|
+
this.reconnectImmediately();
|
|
13729
|
+
}
|
|
13730
|
+
};
|
|
13731
|
+
document.addEventListener("visibilitychange", this.visibilityHandler);
|
|
13732
|
+
}
|
|
13528
13733
|
}
|
|
13529
13734
|
connect() {
|
|
13735
|
+
if (this.reconnectTimer !== null) {
|
|
13736
|
+
clearTimeout(this.reconnectTimer);
|
|
13737
|
+
this.reconnectTimer = null;
|
|
13738
|
+
}
|
|
13739
|
+
this.reconnectAttempts = 0;
|
|
13530
13740
|
this.ensureSocket();
|
|
13531
13741
|
}
|
|
13532
13742
|
disconnect() {
|
|
13533
|
-
this.
|
|
13743
|
+
if (this.visibilityHandler && typeof document !== "undefined") {
|
|
13744
|
+
document.removeEventListener("visibilitychange", this.visibilityHandler);
|
|
13745
|
+
this.visibilityHandler = null;
|
|
13746
|
+
}
|
|
13534
13747
|
if (!this.socket) return;
|
|
13535
13748
|
this.socket.disconnect();
|
|
13536
13749
|
this.socket = null;
|
|
@@ -13629,13 +13842,13 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
|
|
|
13629
13842
|
reconnection: false
|
|
13630
13843
|
});
|
|
13631
13844
|
socket.on("connect", () => {
|
|
13632
|
-
this.clearPeriodicReconnect();
|
|
13633
13845
|
this.updateConnectionState({
|
|
13634
13846
|
connected: true,
|
|
13635
13847
|
connecting: false,
|
|
13636
13848
|
error: null
|
|
13637
13849
|
});
|
|
13638
|
-
const wasReconnect = this.
|
|
13850
|
+
const wasReconnect = this.needsReconnect;
|
|
13851
|
+
this.needsReconnect = false;
|
|
13639
13852
|
this.reconnectAttempts = 0;
|
|
13640
13853
|
this.onReconnect();
|
|
13641
13854
|
if (wasReconnect) {
|
|
@@ -13646,6 +13859,7 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
|
|
|
13646
13859
|
});
|
|
13647
13860
|
socket.on("disconnect", (reason) => {
|
|
13648
13861
|
this.updateConnectionState({ connected: false, connecting: false });
|
|
13862
|
+
this.needsReconnect = true;
|
|
13649
13863
|
this.clearSocketForReconnect();
|
|
13650
13864
|
if (reason === "io server disconnect") {
|
|
13651
13865
|
this.handleAuthFailure("io server disconnect");
|
|
@@ -13655,6 +13869,7 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
|
|
|
13655
13869
|
});
|
|
13656
13870
|
socket.on("connect_error", (error) => {
|
|
13657
13871
|
const message = error?.message || "connect_error";
|
|
13872
|
+
this.needsReconnect = true;
|
|
13658
13873
|
this.clearSocketForReconnect();
|
|
13659
13874
|
if (message.includes("unauthorized") || message.includes("401")) {
|
|
13660
13875
|
this.handleAuthFailure(message);
|
|
@@ -13682,31 +13897,31 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
|
|
|
13682
13897
|
this.socket.removeAllListeners();
|
|
13683
13898
|
this.socket = null;
|
|
13684
13899
|
}
|
|
13685
|
-
|
|
13686
|
-
if (this.
|
|
13687
|
-
|
|
13688
|
-
this.
|
|
13900
|
+
reconnectImmediately() {
|
|
13901
|
+
if (this.reconnectTimer !== null) {
|
|
13902
|
+
clearTimeout(this.reconnectTimer);
|
|
13903
|
+
this.reconnectTimer = null;
|
|
13689
13904
|
}
|
|
13690
|
-
|
|
13691
|
-
|
|
13692
|
-
if (this.periodicReconnectInterval !== null) return;
|
|
13693
|
-
this.periodicReconnectInterval = setInterval(() => {
|
|
13694
|
-
if (this.connectionState.connected) return;
|
|
13695
|
-
this.reconnectAttempts = 0;
|
|
13696
|
-
this.ensureSocket();
|
|
13697
|
-
}, _StandaloneWsTransport2.PERIODIC_RECONNECT_MS);
|
|
13905
|
+
this.reconnectAttempts = 0;
|
|
13906
|
+
this.ensureSocket();
|
|
13698
13907
|
}
|
|
13699
13908
|
scheduleReconnect() {
|
|
13700
13909
|
if (this.reconnectTimer !== null) return;
|
|
13701
|
-
|
|
13702
|
-
this.startPeriodicReconnect();
|
|
13703
|
-
return;
|
|
13704
|
-
}
|
|
13705
|
-
const delay = Math.min(
|
|
13706
|
-
this.reconnectDelay * Math.pow(2, this.reconnectAttempts),
|
|
13707
|
-
this.reconnectDelayMax
|
|
13708
|
-
);
|
|
13910
|
+
const attempt = this.reconnectAttempts;
|
|
13709
13911
|
this.reconnectAttempts += 1;
|
|
13912
|
+
let delay;
|
|
13913
|
+
if (attempt < _StandaloneWsTransport2.FAST_RETRY_LIMIT) {
|
|
13914
|
+
delay = _StandaloneWsTransport2.FAST_RETRY_DELAY_MS;
|
|
13915
|
+
} else {
|
|
13916
|
+
const backoffAttempt = attempt - _StandaloneWsTransport2.FAST_RETRY_LIMIT;
|
|
13917
|
+
delay = Math.min(
|
|
13918
|
+
_StandaloneWsTransport2.FAST_RETRY_DELAY_MS * Math.pow(2, backoffAttempt),
|
|
13919
|
+
_StandaloneWsTransport2.MAX_BACKOFF_MS
|
|
13920
|
+
);
|
|
13921
|
+
if (attempt === _StandaloneWsTransport2.FAST_RETRY_LIMIT) {
|
|
13922
|
+
this.dispatchEvent("connection:slow-retry", { timestamp: Date.now() });
|
|
13923
|
+
}
|
|
13924
|
+
}
|
|
13710
13925
|
this.reconnectTimer = setTimeout(() => {
|
|
13711
13926
|
this.reconnectTimer = null;
|
|
13712
13927
|
this.ensureSocket();
|
|
@@ -13767,7 +13982,9 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
|
|
|
13767
13982
|
this.wildcardRegistered = true;
|
|
13768
13983
|
}
|
|
13769
13984
|
};
|
|
13770
|
-
_StandaloneWsTransport.
|
|
13985
|
+
_StandaloneWsTransport.FAST_RETRY_LIMIT = 60;
|
|
13986
|
+
_StandaloneWsTransport.FAST_RETRY_DELAY_MS = 1e3;
|
|
13987
|
+
_StandaloneWsTransport.MAX_BACKOFF_MS = 3e4;
|
|
13771
13988
|
var StandaloneWsTransport = _StandaloneWsTransport;
|
|
13772
13989
|
function createSdkStore(initial) {
|
|
13773
13990
|
const store = createStore()(subscribeWithSelector(() => initial));
|
|
@@ -13788,14 +14005,16 @@ function createLobbyStore(transport2) {
|
|
|
13788
14005
|
depositStatusByLobbyId: {}
|
|
13789
14006
|
});
|
|
13790
14007
|
const setBaseState = (lobbies) => {
|
|
13791
|
-
|
|
13792
|
-
|
|
13793
|
-
|
|
13794
|
-
|
|
13795
|
-
|
|
13796
|
-
|
|
13797
|
-
|
|
13798
|
-
|
|
14008
|
+
store.updateState((state) => {
|
|
14009
|
+
const lobbiesById = { ...state.lobbiesById };
|
|
14010
|
+
for (const lobby of lobbies) {
|
|
14011
|
+
const existing = lobbiesById[lobby.id];
|
|
14012
|
+
if (!existing || lobby.updatedAt >= existing.updatedAt) {
|
|
14013
|
+
lobbiesById[lobby.id] = lobby;
|
|
14014
|
+
}
|
|
14015
|
+
}
|
|
14016
|
+
return { ...state, lobbiesById };
|
|
14017
|
+
});
|
|
13799
14018
|
};
|
|
13800
14019
|
const applyWsEvent = (event) => {
|
|
13801
14020
|
switch (event.event) {
|
|
@@ -13925,12 +14144,23 @@ function createGameStore(transport2) {
|
|
|
13925
14144
|
gamesById: {},
|
|
13926
14145
|
spectatorCounts: {}
|
|
13927
14146
|
});
|
|
14147
|
+
const STATUS_RANK = {
|
|
14148
|
+
active: 0,
|
|
14149
|
+
completed: 1,
|
|
14150
|
+
abandoned: 1
|
|
14151
|
+
};
|
|
13928
14152
|
const setBaseState = (games) => {
|
|
13929
|
-
|
|
13930
|
-
|
|
13931
|
-
|
|
13932
|
-
|
|
13933
|
-
|
|
14153
|
+
store.updateState((state) => {
|
|
14154
|
+
const gamesById = { ...state.gamesById };
|
|
14155
|
+
for (const game of games) {
|
|
14156
|
+
const existing = gamesById[game.gameId];
|
|
14157
|
+
if (existing && STATUS_RANK[existing.status] > STATUS_RANK[game.status]) {
|
|
14158
|
+
continue;
|
|
14159
|
+
}
|
|
14160
|
+
gamesById[game.gameId] = game;
|
|
14161
|
+
}
|
|
14162
|
+
return { ...state, gamesById };
|
|
14163
|
+
});
|
|
13934
14164
|
};
|
|
13935
14165
|
const applyWsEvent = (event) => {
|
|
13936
14166
|
switch (event.event) {
|
|
@@ -14026,8 +14256,21 @@ function createGameActionsStore(transport2) {
|
|
|
14026
14256
|
});
|
|
14027
14257
|
};
|
|
14028
14258
|
const isNonRpsState = (state) => Boolean(state && !isRpsState(state));
|
|
14259
|
+
const pendingEvents = /* @__PURE__ */ new Map();
|
|
14260
|
+
function enqueue(gameId, event) {
|
|
14261
|
+
const q = pendingEvents.get(gameId) ?? [];
|
|
14262
|
+
q.push(event);
|
|
14263
|
+
pendingEvents.set(gameId, q);
|
|
14264
|
+
}
|
|
14265
|
+
function drainQueue(gameId) {
|
|
14266
|
+
const q = pendingEvents.get(gameId);
|
|
14267
|
+
if (!q?.length) return;
|
|
14268
|
+
pendingEvents.delete(gameId);
|
|
14269
|
+
for (const ev of q) applyWsEvent(ev);
|
|
14270
|
+
}
|
|
14029
14271
|
const setBaseState = (gameId, state) => {
|
|
14030
14272
|
updateState(gameId, state);
|
|
14273
|
+
drainQueue(gameId);
|
|
14031
14274
|
};
|
|
14032
14275
|
const clearState = (gameId) => {
|
|
14033
14276
|
store.updateState((state) => {
|
|
@@ -14037,6 +14280,7 @@ function createGameActionsStore(transport2) {
|
|
|
14037
14280
|
const { [gameId]: _, ...rest } = state.statesByGameId;
|
|
14038
14281
|
return { ...state, statesByGameId: rest };
|
|
14039
14282
|
});
|
|
14283
|
+
pendingEvents.delete(gameId);
|
|
14040
14284
|
};
|
|
14041
14285
|
const applyWsEvent = (event) => {
|
|
14042
14286
|
switch (event.event) {
|
|
@@ -14055,7 +14299,10 @@ function createGameActionsStore(transport2) {
|
|
|
14055
14299
|
}
|
|
14056
14300
|
case "game:rps:starting": {
|
|
14057
14301
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
14058
|
-
if (!current || !isRpsState(current))
|
|
14302
|
+
if (!current || !isRpsState(current)) {
|
|
14303
|
+
enqueue(event.payload.gameId, event);
|
|
14304
|
+
return;
|
|
14305
|
+
}
|
|
14059
14306
|
const betAmount = typeof event.payload.betAmount === "number" ? event.payload.betAmount : void 0;
|
|
14060
14307
|
const startedAt = typeof event.payload.startedAt === "string" ? event.payload.startedAt : current.roundState.startedAt;
|
|
14061
14308
|
const bufferEndsAt = typeof event.payload.bufferEndsAt === "string" ? event.payload.bufferEndsAt : current.roundState.selectionEndsAt;
|
|
@@ -14076,7 +14323,10 @@ function createGameActionsStore(transport2) {
|
|
|
14076
14323
|
}
|
|
14077
14324
|
case "game:rps:round:started": {
|
|
14078
14325
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
14079
|
-
if (!current || !isRpsState(current))
|
|
14326
|
+
if (!current || !isRpsState(current)) {
|
|
14327
|
+
enqueue(event.payload.gameId, event);
|
|
14328
|
+
return;
|
|
14329
|
+
}
|
|
14080
14330
|
const actions = {};
|
|
14081
14331
|
const baseUsers = /* @__PURE__ */ new Set();
|
|
14082
14332
|
Object.keys(current.roundState.actions).forEach(
|
|
@@ -14110,7 +14360,10 @@ function createGameActionsStore(transport2) {
|
|
|
14110
14360
|
}
|
|
14111
14361
|
case "game:rps:action:received": {
|
|
14112
14362
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
14113
|
-
if (!current || !isRpsState(current))
|
|
14363
|
+
if (!current || !isRpsState(current)) {
|
|
14364
|
+
enqueue(event.payload.gameId, event);
|
|
14365
|
+
return;
|
|
14366
|
+
}
|
|
14114
14367
|
const updated = {
|
|
14115
14368
|
...current,
|
|
14116
14369
|
roundState: {
|
|
@@ -14129,7 +14382,10 @@ function createGameActionsStore(transport2) {
|
|
|
14129
14382
|
}
|
|
14130
14383
|
case "game:rps:timer:cutoff": {
|
|
14131
14384
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
14132
|
-
if (!current || !isRpsState(current))
|
|
14385
|
+
if (!current || !isRpsState(current)) {
|
|
14386
|
+
enqueue(event.payload.gameId, event);
|
|
14387
|
+
return;
|
|
14388
|
+
}
|
|
14133
14389
|
const updated = {
|
|
14134
14390
|
...current,
|
|
14135
14391
|
roundState: {
|
|
@@ -14143,7 +14399,10 @@ function createGameActionsStore(transport2) {
|
|
|
14143
14399
|
}
|
|
14144
14400
|
case "game:rps:round:reveal": {
|
|
14145
14401
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
14146
|
-
if (!current || !isRpsState(current))
|
|
14402
|
+
if (!current || !isRpsState(current)) {
|
|
14403
|
+
enqueue(event.payload.gameId, event);
|
|
14404
|
+
return;
|
|
14405
|
+
}
|
|
14147
14406
|
const actions = {};
|
|
14148
14407
|
const payloadActions = event.payload.actions;
|
|
14149
14408
|
Object.keys(payloadActions || {}).forEach((userId) => {
|
|
@@ -14167,7 +14426,10 @@ function createGameActionsStore(transport2) {
|
|
|
14167
14426
|
}
|
|
14168
14427
|
case "game:rps:round:completed": {
|
|
14169
14428
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
14170
|
-
if (!current || !isRpsState(current))
|
|
14429
|
+
if (!current || !isRpsState(current)) {
|
|
14430
|
+
enqueue(event.payload.gameId, event);
|
|
14431
|
+
return;
|
|
14432
|
+
}
|
|
14171
14433
|
const roundHistory = [
|
|
14172
14434
|
...current.roundHistory || [],
|
|
14173
14435
|
{
|
|
@@ -14192,7 +14454,10 @@ function createGameActionsStore(transport2) {
|
|
|
14192
14454
|
}
|
|
14193
14455
|
case "game:rps:timeout": {
|
|
14194
14456
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
14195
|
-
if (!current || !isRpsState(current))
|
|
14457
|
+
if (!current || !isRpsState(current)) {
|
|
14458
|
+
enqueue(event.payload.gameId, event);
|
|
14459
|
+
return;
|
|
14460
|
+
}
|
|
14196
14461
|
const timedOutUser = event.payload.playerId;
|
|
14197
14462
|
const action = event.payload.action;
|
|
14198
14463
|
const updated = {
|
|
@@ -14217,7 +14482,10 @@ function createGameActionsStore(transport2) {
|
|
|
14217
14482
|
const payload = event.payload;
|
|
14218
14483
|
const { gameId } = payload;
|
|
14219
14484
|
const current = store.getState().statesByGameId[gameId];
|
|
14220
|
-
if (!current)
|
|
14485
|
+
if (!current) {
|
|
14486
|
+
enqueue(gameId, event);
|
|
14487
|
+
return;
|
|
14488
|
+
}
|
|
14221
14489
|
const updated = isRpsCompletionPayload(payload) && isRpsState(current) ? {
|
|
14222
14490
|
...current,
|
|
14223
14491
|
status: "completed",
|
|
@@ -14266,11 +14534,15 @@ function createGameActionsStore(transport2) {
|
|
|
14266
14534
|
const current = store.getState().statesByGameId[gameId];
|
|
14267
14535
|
const updated = current ? { ...current, ...incoming } : incoming;
|
|
14268
14536
|
updateState(gameId, updated);
|
|
14537
|
+
drainQueue(gameId);
|
|
14269
14538
|
break;
|
|
14270
14539
|
}
|
|
14271
14540
|
case "game:rematch:requested": {
|
|
14272
14541
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
14273
|
-
if (!current)
|
|
14542
|
+
if (!current) {
|
|
14543
|
+
enqueue(event.payload.gameId, event);
|
|
14544
|
+
return;
|
|
14545
|
+
}
|
|
14274
14546
|
const requestedBy = event.payload.requestedBy;
|
|
14275
14547
|
const userId = event.payload.userId;
|
|
14276
14548
|
const requested = new Set(
|
|
@@ -14286,7 +14558,10 @@ function createGameActionsStore(transport2) {
|
|
|
14286
14558
|
}
|
|
14287
14559
|
case "game:rematch:cancelled": {
|
|
14288
14560
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
14289
|
-
if (!current)
|
|
14561
|
+
if (!current) {
|
|
14562
|
+
enqueue(event.payload.gameId, event);
|
|
14563
|
+
return;
|
|
14564
|
+
}
|
|
14290
14565
|
const requestedBy = event.payload.requestedBy ?? [];
|
|
14291
14566
|
const updated = {
|
|
14292
14567
|
...current,
|
|
@@ -14297,7 +14572,10 @@ function createGameActionsStore(transport2) {
|
|
|
14297
14572
|
}
|
|
14298
14573
|
case "game:rematch:started": {
|
|
14299
14574
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
14300
|
-
if (!current)
|
|
14575
|
+
if (!current) {
|
|
14576
|
+
enqueue(event.payload.gameId, event);
|
|
14577
|
+
return;
|
|
14578
|
+
}
|
|
14301
14579
|
const updated = {
|
|
14302
14580
|
...current,
|
|
14303
14581
|
rematchRequestedBy: event.payload.playerIds ?? []
|
|
@@ -14308,7 +14586,10 @@ function createGameActionsStore(transport2) {
|
|
|
14308
14586
|
case "game:pot:updated": {
|
|
14309
14587
|
const { gameId, totalPotMinor } = event.payload;
|
|
14310
14588
|
const current = store.getState().statesByGameId[gameId];
|
|
14311
|
-
if (!current)
|
|
14589
|
+
if (!current) {
|
|
14590
|
+
enqueue(gameId, event);
|
|
14591
|
+
return;
|
|
14592
|
+
}
|
|
14312
14593
|
const updated = {
|
|
14313
14594
|
...current,
|
|
14314
14595
|
totalPotMinor
|
|
@@ -14321,12 +14602,123 @@ function createGameActionsStore(transport2) {
|
|
|
14321
14602
|
}
|
|
14322
14603
|
};
|
|
14323
14604
|
const joinGame = (gameId) => transport2.joinRoom(`game:${gameId}`);
|
|
14605
|
+
const getCountdownDigit = (gameId, nowMs) => {
|
|
14606
|
+
const state = store.getState().statesByGameId[gameId];
|
|
14607
|
+
if (!state) return null;
|
|
14608
|
+
if (isRpsState(state)) {
|
|
14609
|
+
if (state.roundState.phase !== "starting") return null;
|
|
14610
|
+
const remaining = new Date(state.roundState.selectionEndsAt).getTime() - nowMs;
|
|
14611
|
+
if (remaining <= 0) return null;
|
|
14612
|
+
return Math.ceil(remaining / 1e3);
|
|
14613
|
+
}
|
|
14614
|
+
const bufferEndsAt = state.bufferEndsAt;
|
|
14615
|
+
if (bufferEndsAt) {
|
|
14616
|
+
const remaining = new Date(bufferEndsAt).getTime() - nowMs;
|
|
14617
|
+
if (remaining <= 0) return null;
|
|
14618
|
+
return Math.ceil(remaining / 1e3);
|
|
14619
|
+
}
|
|
14620
|
+
return null;
|
|
14621
|
+
};
|
|
14622
|
+
const getChessClockTimes = (gameId, nowMs) => {
|
|
14623
|
+
const state = store.getState().statesByGameId[gameId];
|
|
14624
|
+
if (!state || state.gameType !== "chess") return null;
|
|
14625
|
+
const s = state;
|
|
14626
|
+
let whiteMs = s.whiteTimeMs ?? 0;
|
|
14627
|
+
let blackMs = s.blackTimeMs ?? 0;
|
|
14628
|
+
if (s.status === "active" && s.currentPlayerId) {
|
|
14629
|
+
const startedAt = Date.parse(s.turnStartedAt);
|
|
14630
|
+
if (!Number.isNaN(startedAt)) {
|
|
14631
|
+
const elapsed = Math.max(0, nowMs - startedAt);
|
|
14632
|
+
if (s.currentPlayerId === s.whitePlayerId) {
|
|
14633
|
+
whiteMs = Math.max(0, whiteMs - elapsed);
|
|
14634
|
+
} else if (s.currentPlayerId === s.blackPlayerId) {
|
|
14635
|
+
blackMs = Math.max(0, blackMs - elapsed);
|
|
14636
|
+
}
|
|
14637
|
+
}
|
|
14638
|
+
}
|
|
14639
|
+
return { whiteTimeMs: whiteMs, blackTimeMs: blackMs };
|
|
14640
|
+
};
|
|
14641
|
+
const getChessCapturedPieces = (gameId) => {
|
|
14642
|
+
const state = store.getState().statesByGameId[gameId];
|
|
14643
|
+
if (!state || state.gameType !== "chess") return null;
|
|
14644
|
+
const fen = state.fen;
|
|
14645
|
+
if (!fen) return { capturedByWhite: [], capturedByBlack: [] };
|
|
14646
|
+
const placement = fen.split(" ")[0];
|
|
14647
|
+
const white = {};
|
|
14648
|
+
const black = {};
|
|
14649
|
+
for (const char of placement) {
|
|
14650
|
+
if (char === "/" || char >= "1" && char <= "8") continue;
|
|
14651
|
+
const lower = char.toLowerCase();
|
|
14652
|
+
if (char === lower) {
|
|
14653
|
+
black[lower] = (black[lower] ?? 0) + 1;
|
|
14654
|
+
} else {
|
|
14655
|
+
white[lower] = (white[lower] ?? 0) + 1;
|
|
14656
|
+
}
|
|
14657
|
+
}
|
|
14658
|
+
const INITIAL = {
|
|
14659
|
+
p: 8,
|
|
14660
|
+
r: 2,
|
|
14661
|
+
n: 2,
|
|
14662
|
+
b: 2,
|
|
14663
|
+
q: 1,
|
|
14664
|
+
k: 1
|
|
14665
|
+
};
|
|
14666
|
+
const capturedByWhite = [];
|
|
14667
|
+
const capturedByBlack = [];
|
|
14668
|
+
for (const [type, initial] of Object.entries(INITIAL)) {
|
|
14669
|
+
const missingBlack = initial - (black[type] ?? 0);
|
|
14670
|
+
for (let i = 0; i < missingBlack; i++) capturedByWhite.push(`b${type}`);
|
|
14671
|
+
const missingWhite = initial - (white[type] ?? 0);
|
|
14672
|
+
for (let i = 0; i < missingWhite; i++) capturedByBlack.push(`w${type}`);
|
|
14673
|
+
}
|
|
14674
|
+
return { capturedByWhite, capturedByBlack };
|
|
14675
|
+
};
|
|
14676
|
+
const getTicTacToeClockTimes = (gameId, nowMs) => {
|
|
14677
|
+
const state = store.getState().statesByGameId[gameId];
|
|
14678
|
+
if (!state || state.gameType !== "tic-tac-toe") return null;
|
|
14679
|
+
const s = state;
|
|
14680
|
+
let xMs = s.xTimeMs ?? 0;
|
|
14681
|
+
let oMs = s.oTimeMs ?? 0;
|
|
14682
|
+
if (s.status === "active" && s.currentPlayerId) {
|
|
14683
|
+
const currentMark = s.playerMarks[s.currentPlayerId];
|
|
14684
|
+
const startedAt = Date.parse(s.turnStartedAt);
|
|
14685
|
+
if (!Number.isNaN(startedAt)) {
|
|
14686
|
+
const elapsed = Math.max(0, nowMs - startedAt);
|
|
14687
|
+
if (currentMark === "X") xMs = Math.max(0, xMs - elapsed);
|
|
14688
|
+
else if (currentMark === "O") oMs = Math.max(0, oMs - elapsed);
|
|
14689
|
+
}
|
|
14690
|
+
}
|
|
14691
|
+
return { xTimeMs: xMs, oTimeMs: oMs };
|
|
14692
|
+
};
|
|
14693
|
+
const getConnect4ClockTimes = (gameId, nowMs) => {
|
|
14694
|
+
const state = store.getState().statesByGameId[gameId];
|
|
14695
|
+
if (!state || state.gameType !== "connect-four") return null;
|
|
14696
|
+
const s = state;
|
|
14697
|
+
let redMs = s.redTimeMs ?? 0;
|
|
14698
|
+
let yellowMs = s.yellowTimeMs ?? 0;
|
|
14699
|
+
if (s.status === "active" && s.currentPlayerId) {
|
|
14700
|
+
const currentColor = s.playerColors[s.currentPlayerId];
|
|
14701
|
+
const startedAt = Date.parse(s.turnStartedAt);
|
|
14702
|
+
if (!Number.isNaN(startedAt)) {
|
|
14703
|
+
const elapsed = Math.max(0, nowMs - startedAt);
|
|
14704
|
+
if (currentColor === "RED") redMs = Math.max(0, redMs - elapsed);
|
|
14705
|
+
else if (currentColor === "YELLOW")
|
|
14706
|
+
yellowMs = Math.max(0, yellowMs - elapsed);
|
|
14707
|
+
}
|
|
14708
|
+
}
|
|
14709
|
+
return { redTimeMs: redMs, yellowTimeMs: yellowMs };
|
|
14710
|
+
};
|
|
14324
14711
|
return {
|
|
14325
14712
|
store,
|
|
14326
14713
|
setBaseState,
|
|
14327
14714
|
clearState,
|
|
14328
14715
|
applyWsEvent,
|
|
14329
|
-
joinGame
|
|
14716
|
+
joinGame,
|
|
14717
|
+
getCountdownDigit,
|
|
14718
|
+
getChessClockTimes,
|
|
14719
|
+
getChessCapturedPieces,
|
|
14720
|
+
getTicTacToeClockTimes,
|
|
14721
|
+
getConnect4ClockTimes
|
|
14330
14722
|
};
|
|
14331
14723
|
}
|
|
14332
14724
|
function isRpsState(state) {
|
|
@@ -14668,7 +15060,38 @@ function createNotificationsStore() {
|
|
|
14668
15060
|
appNotifications: []
|
|
14669
15061
|
}));
|
|
14670
15062
|
};
|
|
14671
|
-
|
|
15063
|
+
const markAsRead = (id) => {
|
|
15064
|
+
store.updateState((state) => ({
|
|
15065
|
+
...state,
|
|
15066
|
+
appNotifications: state.appNotifications.map(
|
|
15067
|
+
(n) => n.id === id ? { ...n, read: true } : n
|
|
15068
|
+
)
|
|
15069
|
+
}));
|
|
15070
|
+
};
|
|
15071
|
+
const markAllAsRead = () => {
|
|
15072
|
+
store.updateState((state) => ({
|
|
15073
|
+
...state,
|
|
15074
|
+
appNotifications: state.appNotifications.map((n) => ({
|
|
15075
|
+
...n,
|
|
15076
|
+
read: true
|
|
15077
|
+
}))
|
|
15078
|
+
}));
|
|
15079
|
+
};
|
|
15080
|
+
const dismiss = (id) => {
|
|
15081
|
+
store.updateState((state) => ({
|
|
15082
|
+
...state,
|
|
15083
|
+
appNotifications: state.appNotifications.filter((n) => n.id !== id)
|
|
15084
|
+
}));
|
|
15085
|
+
};
|
|
15086
|
+
return {
|
|
15087
|
+
store,
|
|
15088
|
+
applyWsEvent,
|
|
15089
|
+
setListFromApi,
|
|
15090
|
+
clear,
|
|
15091
|
+
markAsRead,
|
|
15092
|
+
markAllAsRead,
|
|
15093
|
+
dismiss
|
|
15094
|
+
};
|
|
14672
15095
|
}
|
|
14673
15096
|
function createFriendsStore() {
|
|
14674
15097
|
const store = createSdkStore({
|
|
@@ -14953,12 +15376,14 @@ var WsRouter = class {
|
|
|
14953
15376
|
}
|
|
14954
15377
|
const decoded = decodeWsEvent(eventName, payload);
|
|
14955
15378
|
if (!decoded) return;
|
|
14956
|
-
|
|
14957
|
-
|
|
14958
|
-
this.deps.
|
|
14959
|
-
this.deps.
|
|
14960
|
-
this.deps.
|
|
14961
|
-
this.deps.
|
|
15379
|
+
const serverTs = payload !== null && typeof payload === "object" ? payload._serverTs : void 0;
|
|
15380
|
+
const event = serverTs !== void 0 ? { ...decoded, _serverTs: serverTs } : decoded;
|
|
15381
|
+
this.deps.lobbyStore.applyWsEvent(event);
|
|
15382
|
+
this.deps.gameStore.applyWsEvent(event);
|
|
15383
|
+
this.deps.gameActionsStore.applyWsEvent(event);
|
|
15384
|
+
this.deps.chatStore.applyWsEvent(event);
|
|
15385
|
+
this.deps.dmThreadsStore.applyWsEvent(event);
|
|
15386
|
+
this.deps.notificationsStore.applyWsEvent(event);
|
|
14962
15387
|
});
|
|
14963
15388
|
}
|
|
14964
15389
|
stop() {
|
|
@@ -14970,12 +15395,14 @@ var WsRouter = class {
|
|
|
14970
15395
|
this.transport.subscribeEvent(eventName, (payload) => {
|
|
14971
15396
|
const decoded = decodeWsEvent(eventName, payload);
|
|
14972
15397
|
if (!decoded) return;
|
|
14973
|
-
|
|
14974
|
-
|
|
14975
|
-
this.deps.
|
|
14976
|
-
this.deps.
|
|
14977
|
-
this.deps.
|
|
14978
|
-
this.deps.
|
|
15398
|
+
const serverTs = payload !== null && typeof payload === "object" ? payload._serverTs : void 0;
|
|
15399
|
+
const event = serverTs !== void 0 ? { ...decoded, _serverTs: serverTs } : decoded;
|
|
15400
|
+
this.deps.lobbyStore.applyWsEvent(event);
|
|
15401
|
+
this.deps.gameStore.applyWsEvent(event);
|
|
15402
|
+
this.deps.gameActionsStore.applyWsEvent(event);
|
|
15403
|
+
this.deps.chatStore.applyWsEvent(event);
|
|
15404
|
+
this.deps.dmThreadsStore.applyWsEvent(event);
|
|
15405
|
+
this.deps.notificationsStore.applyWsEvent(event);
|
|
14979
15406
|
});
|
|
14980
15407
|
}
|
|
14981
15408
|
}
|
|
@@ -15015,6 +15442,7 @@ var SDK = class {
|
|
|
15015
15442
|
constructor(config) {
|
|
15016
15443
|
const baseUrl = config.baseUrl || "http://localhost:3000";
|
|
15017
15444
|
const logger2 = config.logger || logger;
|
|
15445
|
+
this.analytics = config.analytics ?? new NoopAnalyticsClient();
|
|
15018
15446
|
this.http = config.httpClient || new HttpClient(
|
|
15019
15447
|
baseUrl,
|
|
15020
15448
|
config.storage,
|
|
@@ -15047,14 +15475,18 @@ var SDK = class {
|
|
|
15047
15475
|
this.referrals = new Referrals(this.http, logger2);
|
|
15048
15476
|
this.reports = new Reports(this.http, logger2);
|
|
15049
15477
|
this.support = new Support(this.http, logger2);
|
|
15050
|
-
this.markets = new Markets(this.http, logger2);
|
|
15478
|
+
this.markets = new Markets(this.http, logger2, this.wallet);
|
|
15051
15479
|
this.wsTransport = config.wsTransport || new StandaloneWsTransport(baseUrl);
|
|
15052
15480
|
this.wsTransport.setAppId(config.appId);
|
|
15053
15481
|
this.lobbyStore = createLobbyStore(this.wsTransport);
|
|
15482
|
+
this.lobbies.setLobbyStore(this.lobbyStore);
|
|
15054
15483
|
this.gameStore = createGameStore(this.wsTransport);
|
|
15484
|
+
this.games.setGameStore(this.gameStore);
|
|
15055
15485
|
this.gameActionsStore = createGameActionsStore(this.wsTransport);
|
|
15486
|
+
this.games.setGameActionsStore(this.gameActionsStore);
|
|
15056
15487
|
this.chatStore = createChatStore(this.wsTransport);
|
|
15057
15488
|
this.dmThreadsStore = createDmThreadsStore();
|
|
15489
|
+
this.chat.setDmThreadsStore(this.dmThreadsStore);
|
|
15058
15490
|
this.notificationsStore = createNotificationsStore();
|
|
15059
15491
|
this.friendsStore = createFriendsStore();
|
|
15060
15492
|
this.notifications = new Notifications(
|
|
@@ -15084,6 +15516,29 @@ var SDK = class {
|
|
|
15084
15516
|
this.wsTransport.connect();
|
|
15085
15517
|
await this.wsTransport.waitUntilConnected(timeoutMs);
|
|
15086
15518
|
}
|
|
15519
|
+
/**
|
|
15520
|
+
* Handle the full deposit-to-queue lifecycle for a lobby.
|
|
15521
|
+
*
|
|
15522
|
+
* After the deposit is confirmed, the backend may not have processed the
|
|
15523
|
+
* `lobby.deposit.allConfirmed` event yet, so the lobby can still be in
|
|
15524
|
+
* 'preparing' status. This method re-fetches the lobby and explicitly
|
|
15525
|
+
* joins the queue if needed, then pushes the latest state to the store
|
|
15526
|
+
* so the UI transitions immediately.
|
|
15527
|
+
*/
|
|
15528
|
+
async depositAndJoinQueue(lobbyId) {
|
|
15529
|
+
const result = await this.escrow.depositForLobby(lobbyId);
|
|
15530
|
+
if (!result.canProceedToQueue) {
|
|
15531
|
+
const lobby2 = await this.lobbies.getLobby(lobbyId);
|
|
15532
|
+
this.lobbyStore.setBaseState([lobby2]);
|
|
15533
|
+
return { ...result, lobby: lobby2 };
|
|
15534
|
+
}
|
|
15535
|
+
let lobby = await this.lobbies.getLobby(lobbyId);
|
|
15536
|
+
if (lobby.status === "preparing") {
|
|
15537
|
+
lobby = await this.lobbies.joinQueue(lobbyId);
|
|
15538
|
+
}
|
|
15539
|
+
this.lobbyStore.setBaseState([lobby]);
|
|
15540
|
+
return { ...result, lobby };
|
|
15541
|
+
}
|
|
15087
15542
|
};
|
|
15088
15543
|
var import_utils = __toESM2(require_dist(), 1);
|
|
15089
15544
|
var export_MICRO_UNITS = import_utils.MICRO_UNITS;
|
|
@@ -15094,10 +15549,34 @@ var export_toMajor = import_utils.toMajor;
|
|
|
15094
15549
|
var import_tweetnacl = __toESM(require_nacl_fast(), 1);
|
|
15095
15550
|
import { Keypair } from "@solana/web3.js";
|
|
15096
15551
|
import bs58 from "bs58";
|
|
15552
|
+
function decodeJwtSub(token) {
|
|
15553
|
+
try {
|
|
15554
|
+
const parts2 = token.split(".");
|
|
15555
|
+
if (parts2.length < 2) return null;
|
|
15556
|
+
const base64 = parts2[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
15557
|
+
const json = Buffer.from(base64, "base64").toString("utf-8");
|
|
15558
|
+
const parsed = JSON.parse(json);
|
|
15559
|
+
return typeof parsed.sub === "string" ? parsed.sub : null;
|
|
15560
|
+
} catch {
|
|
15561
|
+
return null;
|
|
15562
|
+
}
|
|
15563
|
+
}
|
|
15564
|
+
function signatureToBytes(sig) {
|
|
15565
|
+
if (sig.includes("-") || sig.includes("_")) {
|
|
15566
|
+
const padded = sig.replace(/-/g, "+").replace(/_/g, "/") + "==".slice(sig.length % 4 === 0 ? 4 : sig.length % 4);
|
|
15567
|
+
return new Uint8Array(Buffer.from(padded, "base64"));
|
|
15568
|
+
}
|
|
15569
|
+
if (/[+/=]/.test(sig)) {
|
|
15570
|
+
return new Uint8Array(Buffer.from(sig, "base64"));
|
|
15571
|
+
}
|
|
15572
|
+
return bs58.decode(sig);
|
|
15573
|
+
}
|
|
15097
15574
|
var DimAgentClient = class {
|
|
15098
15575
|
sdk;
|
|
15099
15576
|
agentConfig;
|
|
15577
|
+
externalSignerMode;
|
|
15100
15578
|
keypair;
|
|
15579
|
+
_externalAddress = null;
|
|
15101
15580
|
config;
|
|
15102
15581
|
authenticated = false;
|
|
15103
15582
|
userId = null;
|
|
@@ -15111,21 +15590,35 @@ var DimAgentClient = class {
|
|
|
15111
15590
|
constructor(config) {
|
|
15112
15591
|
this.config = config;
|
|
15113
15592
|
this.agentConfig = config.agentConfig;
|
|
15114
|
-
|
|
15115
|
-
|
|
15593
|
+
this.externalSignerMode = !config.walletPrivateKey;
|
|
15594
|
+
if (config.walletPrivateKey) {
|
|
15595
|
+
const secretKeyBytes = bs58.decode(config.walletPrivateKey);
|
|
15596
|
+
this.keypair = Keypair.fromSecretKey(secretKeyBytes);
|
|
15597
|
+
} else {
|
|
15598
|
+
this.keypair = null;
|
|
15599
|
+
}
|
|
15600
|
+
const storage = new NodeStorage();
|
|
15601
|
+
if (config.accessToken) {
|
|
15602
|
+
storage.set(TOKEN_KEY, config.accessToken);
|
|
15603
|
+
const sub = decodeJwtSub(config.accessToken);
|
|
15604
|
+
if (sub) {
|
|
15605
|
+
this.authenticated = true;
|
|
15606
|
+
this.userId = sub;
|
|
15607
|
+
}
|
|
15608
|
+
}
|
|
15116
15609
|
this.sdk = new SDK({
|
|
15117
15610
|
appId: "dim-agents",
|
|
15118
15611
|
baseUrl: config.apiUrl || "https://api.dim.cool",
|
|
15119
|
-
storage
|
|
15612
|
+
storage,
|
|
15120
15613
|
autoPay: {
|
|
15121
|
-
enabled:
|
|
15614
|
+
enabled: !this.externalSignerMode,
|
|
15122
15615
|
maxAmountMinor: 25e3,
|
|
15123
15616
|
maxRetries: 1
|
|
15124
15617
|
}
|
|
15125
15618
|
});
|
|
15126
15619
|
}
|
|
15127
15620
|
get walletAddress() {
|
|
15128
|
-
return this.keypair
|
|
15621
|
+
return this.keypair?.publicKey.toBase58() ?? this._externalAddress ?? "";
|
|
15129
15622
|
}
|
|
15130
15623
|
get isAuthenticated() {
|
|
15131
15624
|
return this.authenticated;
|
|
@@ -15134,17 +15627,23 @@ var DimAgentClient = class {
|
|
|
15134
15627
|
return this.userId;
|
|
15135
15628
|
}
|
|
15136
15629
|
async authenticate() {
|
|
15630
|
+
if (!this.keypair) {
|
|
15631
|
+
throw new Error(
|
|
15632
|
+
"No keypair configured. Use dim_request_auth_message + dim_complete_login for external signer mode."
|
|
15633
|
+
);
|
|
15634
|
+
}
|
|
15635
|
+
const keypair = this.keypair;
|
|
15137
15636
|
this.sdk.wallet.setSigner({
|
|
15138
15637
|
address: this.walletAddress,
|
|
15139
15638
|
signMessage: async (message) => {
|
|
15140
15639
|
const signature = import_tweetnacl.default.sign.detached(
|
|
15141
15640
|
new TextEncoder().encode(message),
|
|
15142
|
-
|
|
15641
|
+
keypair.secretKey
|
|
15143
15642
|
);
|
|
15144
15643
|
return Buffer.from(signature).toString("base64");
|
|
15145
15644
|
},
|
|
15146
15645
|
signTransaction: async (transaction) => {
|
|
15147
|
-
transaction.partialSign(
|
|
15646
|
+
transaction.partialSign(keypair);
|
|
15148
15647
|
return transaction;
|
|
15149
15648
|
}
|
|
15150
15649
|
});
|
|
@@ -15198,26 +15697,61 @@ var DimAgentClient = class {
|
|
|
15198
15697
|
get pendingEventCount() {
|
|
15199
15698
|
return this.eventQueue.length;
|
|
15200
15699
|
}
|
|
15201
|
-
/** Get the Keypair for transaction signing. */
|
|
15700
|
+
/** Get the Keypair for transaction signing (keypair mode only). */
|
|
15202
15701
|
getKeypair() {
|
|
15702
|
+
if (!this.keypair) {
|
|
15703
|
+
throw new Error(
|
|
15704
|
+
"No keypair configured \u2014 running in external signer mode."
|
|
15705
|
+
);
|
|
15706
|
+
}
|
|
15203
15707
|
return this.keypair;
|
|
15204
15708
|
}
|
|
15205
|
-
|
|
15206
|
-
|
|
15207
|
-
|
|
15208
|
-
|
|
15209
|
-
|
|
15210
|
-
|
|
15211
|
-
|
|
15212
|
-
|
|
15213
|
-
|
|
15214
|
-
|
|
15215
|
-
|
|
15216
|
-
const
|
|
15217
|
-
|
|
15218
|
-
|
|
15219
|
-
|
|
15220
|
-
|
|
15709
|
+
/** Request the auth handshake message for a given address (external signer mode). */
|
|
15710
|
+
async requestAuthMessage(address) {
|
|
15711
|
+
return this.sdk.auth.generateHandshake(address);
|
|
15712
|
+
}
|
|
15713
|
+
/**
|
|
15714
|
+
* Complete authentication using an externally provided signature (external signer mode).
|
|
15715
|
+
* @param address - Solana wallet address (base58)
|
|
15716
|
+
* @param signature - Detached ed25519 signature in base58, base64, or base64url (from sign_solana_message)
|
|
15717
|
+
*/
|
|
15718
|
+
async completeAuth(address, signature) {
|
|
15719
|
+
const signatureBytes = signatureToBytes(signature);
|
|
15720
|
+
const signedMessage = Buffer.from(signatureBytes).toString("base64");
|
|
15721
|
+
const response = await this.sdk.auth.loginWithExternalSignature(
|
|
15722
|
+
address,
|
|
15723
|
+
signedMessage,
|
|
15724
|
+
{
|
|
15725
|
+
referralCode: this.config.referralCode,
|
|
15726
|
+
walletMeta: { type: "phantom-embedded", provider: "injected" }
|
|
15727
|
+
}
|
|
15728
|
+
);
|
|
15729
|
+
this.sdk.wsTransport.setAccessToken(response.access_token);
|
|
15730
|
+
this._externalAddress = address;
|
|
15731
|
+
this.authenticated = true;
|
|
15732
|
+
this.userId = response.user.id;
|
|
15733
|
+
return {
|
|
15734
|
+
userId: response.user.id,
|
|
15735
|
+
username: response.user.username,
|
|
15736
|
+
accessToken: response.access_token
|
|
15737
|
+
};
|
|
15738
|
+
}
|
|
15739
|
+
// ── Game chat cursors ────────────────────────────────────────────────
|
|
15740
|
+
/** Get the last-seen chat timestamp for a game. */
|
|
15741
|
+
getGameChatCursor(gameId) {
|
|
15742
|
+
return this.gameChatCursors.get(gameId);
|
|
15743
|
+
}
|
|
15744
|
+
/** Update the last-seen chat timestamp for a game. */
|
|
15745
|
+
setGameChatCursor(gameId, timestamp) {
|
|
15746
|
+
this.gameChatCursors.set(gameId, timestamp);
|
|
15747
|
+
}
|
|
15748
|
+
// ── Spend tracking ──────────────────────────────────────────────────
|
|
15749
|
+
resetDailySpendIfNeeded() {
|
|
15750
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
15751
|
+
if (this.spendResetDate !== today) {
|
|
15752
|
+
this.dailySpendMinor = 0;
|
|
15753
|
+
this.spendResetDate = today;
|
|
15754
|
+
}
|
|
15221
15755
|
}
|
|
15222
15756
|
recordSpend(amountMinor) {
|
|
15223
15757
|
this.resetDailySpendIfNeeded();
|
|
@@ -15329,6 +15863,77 @@ async function login(client) {
|
|
|
15329
15863
|
};
|
|
15330
15864
|
}
|
|
15331
15865
|
}
|
|
15866
|
+
async function requestAuthMessage(client, args) {
|
|
15867
|
+
try {
|
|
15868
|
+
const { message } = await client.requestAuthMessage(args.address);
|
|
15869
|
+
return {
|
|
15870
|
+
data: {
|
|
15871
|
+
message,
|
|
15872
|
+
address: args.address,
|
|
15873
|
+
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).'
|
|
15874
|
+
}
|
|
15875
|
+
};
|
|
15876
|
+
} catch (error) {
|
|
15877
|
+
return {
|
|
15878
|
+
error: `Failed to get auth message: ${error instanceof Error ? error.message : String(error)}`,
|
|
15879
|
+
isError: true
|
|
15880
|
+
};
|
|
15881
|
+
}
|
|
15882
|
+
}
|
|
15883
|
+
async function completeLogin(client, args) {
|
|
15884
|
+
try {
|
|
15885
|
+
const result = await client.completeAuth(args.address, args.signature);
|
|
15886
|
+
client.startEventListeners();
|
|
15887
|
+
const nextSteps = [];
|
|
15888
|
+
if (result.username == null || result.username === "") {
|
|
15889
|
+
nextSteps.push("No username set \u2014 call dim_set_username to claim one");
|
|
15890
|
+
}
|
|
15891
|
+
nextSteps.push("Check your balance with dim_get_balance");
|
|
15892
|
+
nextSteps.push("Explore available games with dim_list_games");
|
|
15893
|
+
try {
|
|
15894
|
+
const summary = await client.sdk.referrals.getSummary();
|
|
15895
|
+
if (!summary.hasReferrer) {
|
|
15896
|
+
nextSteps.push(
|
|
15897
|
+
"No referrer yet \u2014 call dim_apply_referral_code for 10% fee discount"
|
|
15898
|
+
);
|
|
15899
|
+
}
|
|
15900
|
+
if (result.username) {
|
|
15901
|
+
nextSteps.push(
|
|
15902
|
+
`Share your referral code "${result.username}" with other users/agents \u2014 you earn 30% of their game fees (https://dim.cool/?ref=${result.username})`
|
|
15903
|
+
);
|
|
15904
|
+
}
|
|
15905
|
+
} catch {
|
|
15906
|
+
}
|
|
15907
|
+
const response = {
|
|
15908
|
+
success: true,
|
|
15909
|
+
userId: result.userId,
|
|
15910
|
+
username: result.username ?? null,
|
|
15911
|
+
walletAddress: client.walletAddress,
|
|
15912
|
+
walletNote: "DIM is noncustodial. Signing is handled by your external wallet for this session.",
|
|
15913
|
+
safetyRules: SAFETY_RULES,
|
|
15914
|
+
nextSteps
|
|
15915
|
+
};
|
|
15916
|
+
const ac = client.agentConfig;
|
|
15917
|
+
const dailyLimit = ac?.dailySpendLimit ?? 20;
|
|
15918
|
+
response.agentConfig = {
|
|
15919
|
+
autoAcceptFriendRequests: ac?.autoAcceptFriendRequests ?? false,
|
|
15920
|
+
autoReplyDms: ac?.autoReplyDms ?? false,
|
|
15921
|
+
autoPlayGames: ac?.autoPlayGames ?? false,
|
|
15922
|
+
maxBetPerGame: ac?.maxBetPerGame ?? 1,
|
|
15923
|
+
dailySpendLimit: dailyLimit,
|
|
15924
|
+
autoJoinGlobalChat: ac?.autoJoinGlobalChat ?? false,
|
|
15925
|
+
autoPromoteReferrals: ac?.autoPromoteReferrals ?? false,
|
|
15926
|
+
dailySpentSoFar: client.dailySpentDollars,
|
|
15927
|
+
dailyRemaining: dailyLimit - client.dailySpentDollars
|
|
15928
|
+
};
|
|
15929
|
+
return { data: response };
|
|
15930
|
+
} catch (error) {
|
|
15931
|
+
return {
|
|
15932
|
+
error: `Login failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
15933
|
+
isError: true
|
|
15934
|
+
};
|
|
15935
|
+
}
|
|
15936
|
+
}
|
|
15332
15937
|
async function getProfile(client) {
|
|
15333
15938
|
try {
|
|
15334
15939
|
if (!client.currentUserId) {
|
|
@@ -15457,10 +16062,19 @@ async function listFriends(client, args) {
|
|
|
15457
16062
|
};
|
|
15458
16063
|
}
|
|
15459
16064
|
}
|
|
15460
|
-
async function getIncomingFriendRequests(client) {
|
|
16065
|
+
async function getIncomingFriendRequests(client, args) {
|
|
15461
16066
|
try {
|
|
15462
|
-
const result = await client.sdk.users.getIncomingFriendRequests(
|
|
15463
|
-
|
|
16067
|
+
const result = await client.sdk.users.getIncomingFriendRequests({
|
|
16068
|
+
limit: args?.limit,
|
|
16069
|
+
cursor: args?.cursor
|
|
16070
|
+
});
|
|
16071
|
+
return {
|
|
16072
|
+
data: {
|
|
16073
|
+
items: result.items,
|
|
16074
|
+
nextCursor: result.nextCursor,
|
|
16075
|
+
hasMore: !!result.nextCursor
|
|
16076
|
+
}
|
|
16077
|
+
};
|
|
15464
16078
|
} catch (error) {
|
|
15465
16079
|
return {
|
|
15466
16080
|
error: `Failed to get requests: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -15581,6 +16195,42 @@ async function sendUsdc(client, args) {
|
|
|
15581
16195
|
const spendErr = client.checkSpendLimit(args.amount);
|
|
15582
16196
|
if (spendErr) return { error: spendErr, isError: true };
|
|
15583
16197
|
const amountMinor = Math.round(args.amount * 1e6);
|
|
16198
|
+
if (client.externalSignerMode) {
|
|
16199
|
+
const senderAddress = client.walletAddress;
|
|
16200
|
+
if (!senderAddress) {
|
|
16201
|
+
return {
|
|
16202
|
+
error: "Wallet address not set. Call dim_complete_login first.",
|
|
16203
|
+
isError: true
|
|
16204
|
+
};
|
|
16205
|
+
}
|
|
16206
|
+
const prepared = await client.sdk.wallet.prepareTransfer(
|
|
16207
|
+
senderAddress,
|
|
16208
|
+
args.recipient,
|
|
16209
|
+
amountMinor
|
|
16210
|
+
);
|
|
16211
|
+
return {
|
|
16212
|
+
data: {
|
|
16213
|
+
needsSigning: true,
|
|
16214
|
+
unsignedTx: prepared.transaction,
|
|
16215
|
+
fee: prepared.fee,
|
|
16216
|
+
totalAmount: prepared.totalAmount,
|
|
16217
|
+
recipientAddress: prepared.recipientAddress,
|
|
16218
|
+
ataCreated: prepared.ataCreated ?? false,
|
|
16219
|
+
confirmWith: {
|
|
16220
|
+
tool: "dim_confirm_send_usdc",
|
|
16221
|
+
params: {
|
|
16222
|
+
recipientAddress: prepared.recipientAddress,
|
|
16223
|
+
amount: amountMinor,
|
|
16224
|
+
fee: prepared.fee,
|
|
16225
|
+
token: "USDC",
|
|
16226
|
+
ataCreated: prepared.ataCreated ?? false,
|
|
16227
|
+
recipientInput: args.recipient
|
|
16228
|
+
}
|
|
16229
|
+
},
|
|
16230
|
+
hint: "Sign and broadcast unsignedTx with send_solana_transaction, then call dim_confirm_send_usdc with the signature and the confirmWith.params."
|
|
16231
|
+
}
|
|
16232
|
+
};
|
|
16233
|
+
}
|
|
15584
16234
|
const result = await client.sdk.wallet.send(args.recipient, amountMinor);
|
|
15585
16235
|
client.recordSpend(amountMinor);
|
|
15586
16236
|
return {
|
|
@@ -15601,11 +16251,74 @@ async function sendUsdc(client, args) {
|
|
|
15601
16251
|
};
|
|
15602
16252
|
}
|
|
15603
16253
|
}
|
|
16254
|
+
async function confirmSendUsdc(client, args) {
|
|
16255
|
+
try {
|
|
16256
|
+
const senderAddress = client.walletAddress;
|
|
16257
|
+
const result = await client.sdk.wallet.confirmTransferSignature(
|
|
16258
|
+
args.signature,
|
|
16259
|
+
senderAddress,
|
|
16260
|
+
args.recipientAddress,
|
|
16261
|
+
args.amount,
|
|
16262
|
+
args.token || "USDC",
|
|
16263
|
+
args.ataCreated,
|
|
16264
|
+
args.recipientInput
|
|
16265
|
+
);
|
|
16266
|
+
client.recordSpend(args.amount);
|
|
16267
|
+
return {
|
|
16268
|
+
data: {
|
|
16269
|
+
success: true,
|
|
16270
|
+
signature: result.signature,
|
|
16271
|
+
status: result.status,
|
|
16272
|
+
recipientAddress: args.recipientAddress
|
|
16273
|
+
}
|
|
16274
|
+
};
|
|
16275
|
+
} catch (error) {
|
|
16276
|
+
return {
|
|
16277
|
+
error: `Failed to confirm USDC transfer: ${error instanceof Error ? error.message : String(error)}`,
|
|
16278
|
+
isError: true
|
|
16279
|
+
};
|
|
16280
|
+
}
|
|
16281
|
+
}
|
|
15604
16282
|
async function tipUser(client, args) {
|
|
15605
16283
|
try {
|
|
15606
16284
|
const spendErr = client.checkSpendLimit(args.amount);
|
|
15607
16285
|
if (spendErr) return { error: spendErr, isError: true };
|
|
15608
16286
|
const amountMinor = Math.round(args.amount * 1e6);
|
|
16287
|
+
if (client.externalSignerMode) {
|
|
16288
|
+
const senderAddress = client.walletAddress;
|
|
16289
|
+
if (!senderAddress) {
|
|
16290
|
+
return {
|
|
16291
|
+
error: "Wallet address not set. Call dim_complete_login first.",
|
|
16292
|
+
isError: true
|
|
16293
|
+
};
|
|
16294
|
+
}
|
|
16295
|
+
const prepared = await client.sdk.tips.prepare({
|
|
16296
|
+
recipientUsername: args.recipientUsername,
|
|
16297
|
+
amount: amountMinor,
|
|
16298
|
+
supportsPresign: false
|
|
16299
|
+
});
|
|
16300
|
+
return {
|
|
16301
|
+
data: {
|
|
16302
|
+
needsSigning: true,
|
|
16303
|
+
unsignedTx: prepared.transaction,
|
|
16304
|
+
fee: prepared.fee,
|
|
16305
|
+
totalAmount: prepared.totalAmount,
|
|
16306
|
+
recipientAddress: prepared.recipientAddress,
|
|
16307
|
+
confirmWith: {
|
|
16308
|
+
tool: "dim_confirm_tip_user",
|
|
16309
|
+
params: {
|
|
16310
|
+
recipientAddress: prepared.recipientAddress,
|
|
16311
|
+
recipientUserId: prepared.recipientUserId,
|
|
16312
|
+
recipientUsername: prepared.recipientUsername,
|
|
16313
|
+
amount: prepared.amount,
|
|
16314
|
+
fee: prepared.fee,
|
|
16315
|
+
ataCreated: false
|
|
16316
|
+
}
|
|
16317
|
+
},
|
|
16318
|
+
hint: "Sign and broadcast unsignedTx with send_solana_transaction, then call dim_confirm_tip_user with the signature and the confirmWith.params."
|
|
16319
|
+
}
|
|
16320
|
+
};
|
|
16321
|
+
}
|
|
15609
16322
|
const result = await client.sdk.tips.send(
|
|
15610
16323
|
args.recipientUsername,
|
|
15611
16324
|
amountMinor
|
|
@@ -15628,6 +16341,38 @@ async function tipUser(client, args) {
|
|
|
15628
16341
|
};
|
|
15629
16342
|
}
|
|
15630
16343
|
}
|
|
16344
|
+
async function confirmTipUser(client, args) {
|
|
16345
|
+
try {
|
|
16346
|
+
const senderAddress = client.walletAddress;
|
|
16347
|
+
await client.sdk.wallet.confirmTransferSignature(
|
|
16348
|
+
args.signature,
|
|
16349
|
+
senderAddress,
|
|
16350
|
+
args.recipientAddress,
|
|
16351
|
+
args.amount,
|
|
16352
|
+
"USDC",
|
|
16353
|
+
args.ataCreated ?? false,
|
|
16354
|
+
args.recipientUsername
|
|
16355
|
+
);
|
|
16356
|
+
await client.sdk.tips.broadcast({
|
|
16357
|
+
recipientUserId: args.recipientUserId,
|
|
16358
|
+
amount: args.amount
|
|
16359
|
+
});
|
|
16360
|
+
client.recordSpend(args.amount);
|
|
16361
|
+
return {
|
|
16362
|
+
data: {
|
|
16363
|
+
success: true,
|
|
16364
|
+
signature: args.signature,
|
|
16365
|
+
recipient: args.recipientUsername,
|
|
16366
|
+
broadcastedToGlobalChat: true
|
|
16367
|
+
}
|
|
16368
|
+
};
|
|
16369
|
+
} catch (error) {
|
|
16370
|
+
return {
|
|
16371
|
+
error: `Failed to confirm tip: ${error instanceof Error ? error.message : String(error)}`,
|
|
16372
|
+
isError: true
|
|
16373
|
+
};
|
|
16374
|
+
}
|
|
16375
|
+
}
|
|
15631
16376
|
async function getWalletActivity(client, args) {
|
|
15632
16377
|
try {
|
|
15633
16378
|
const activity = await client.sdk.wallet.getActivity({
|
|
@@ -15734,6 +16479,7 @@ function buildRpsContext(state, agentUserId) {
|
|
|
15734
16479
|
roundHistory: state.roundHistory,
|
|
15735
16480
|
phase: roundState?.phase,
|
|
15736
16481
|
timeRemaining: roundState?.timeRemaining,
|
|
16482
|
+
...state.bufferEndsAt != null && { bufferEndsAt: state.bufferEndsAt },
|
|
15737
16483
|
moveFormat: 'dim_submit_action: gameType="rock-paper-scissors", action="play", payload={ action: "rock"|"paper"|"scissors" }'
|
|
15738
16484
|
};
|
|
15739
16485
|
}
|
|
@@ -15746,6 +16492,7 @@ function buildChessContext(state, agentUserId) {
|
|
|
15746
16492
|
yourColor,
|
|
15747
16493
|
whiteTimeMs: state.whiteTimeMs,
|
|
15748
16494
|
blackTimeMs: state.blackTimeMs,
|
|
16495
|
+
...state.bufferEndsAt != null && { bufferEndsAt: state.bufferEndsAt },
|
|
15749
16496
|
moveFormat: 'dim_submit_action: gameType="chess", action="move", payload={ from: "e2", to: "e4" }'
|
|
15750
16497
|
};
|
|
15751
16498
|
}
|
|
@@ -15955,6 +16702,23 @@ async function createLobby(client, args) {
|
|
|
15955
16702
|
}
|
|
15956
16703
|
async function depositForLobby(client, args) {
|
|
15957
16704
|
try {
|
|
16705
|
+
if (client.externalSignerMode) {
|
|
16706
|
+
const { transaction } = await client.sdk.escrow.prepareAndStartDeposit(
|
|
16707
|
+
args.lobbyId,
|
|
16708
|
+
false
|
|
16709
|
+
);
|
|
16710
|
+
return {
|
|
16711
|
+
data: {
|
|
16712
|
+
needsSigning: true,
|
|
16713
|
+
unsignedTx: transaction,
|
|
16714
|
+
confirmWith: {
|
|
16715
|
+
tool: "dim_confirm_lobby_deposit",
|
|
16716
|
+
params: { lobbyId: args.lobbyId }
|
|
16717
|
+
},
|
|
16718
|
+
hint: "Sign and broadcast unsignedTx with send_solana_transaction, then call dim_confirm_lobby_deposit with the lobbyId and signature."
|
|
16719
|
+
}
|
|
16720
|
+
};
|
|
16721
|
+
}
|
|
15958
16722
|
const result = await client.sdk.escrow.depositForLobbySync(args.lobbyId);
|
|
15959
16723
|
return {
|
|
15960
16724
|
data: {
|
|
@@ -15971,6 +16735,44 @@ async function depositForLobby(client, args) {
|
|
|
15971
16735
|
};
|
|
15972
16736
|
}
|
|
15973
16737
|
}
|
|
16738
|
+
async function confirmLobbyDeposit(client, args) {
|
|
16739
|
+
try {
|
|
16740
|
+
await client.sdk.escrow.confirmDepositSignature(
|
|
16741
|
+
args.lobbyId,
|
|
16742
|
+
args.signature
|
|
16743
|
+
);
|
|
16744
|
+
const MAX_WAIT_MS = 6e4;
|
|
16745
|
+
const POLL_INTERVAL_MS = 1e3;
|
|
16746
|
+
const startTime = Date.now();
|
|
16747
|
+
while (Date.now() - startTime < MAX_WAIT_MS) {
|
|
16748
|
+
const status = await client.sdk.escrow.getDepositStatus(args.lobbyId);
|
|
16749
|
+
if (status.canProceedToQueue) {
|
|
16750
|
+
return {
|
|
16751
|
+
data: {
|
|
16752
|
+
signature: args.signature,
|
|
16753
|
+
status: "confirmed",
|
|
16754
|
+
canProceedToQueue: true,
|
|
16755
|
+
hint: "Deposit confirmed. Call dim_join_queue to enter matchmaking."
|
|
16756
|
+
}
|
|
16757
|
+
};
|
|
16758
|
+
}
|
|
16759
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
16760
|
+
}
|
|
16761
|
+
return {
|
|
16762
|
+
data: {
|
|
16763
|
+
signature: args.signature,
|
|
16764
|
+
status: "pending",
|
|
16765
|
+
canProceedToQueue: false,
|
|
16766
|
+
hint: "Deposit submitted but not yet confirmed on-chain. Try dim_confirm_lobby_deposit again in a few seconds."
|
|
16767
|
+
}
|
|
16768
|
+
};
|
|
16769
|
+
} catch (error) {
|
|
16770
|
+
return {
|
|
16771
|
+
error: `Failed to confirm deposit: ${error instanceof Error ? error.message : String(error)}`,
|
|
16772
|
+
isError: true
|
|
16773
|
+
};
|
|
16774
|
+
}
|
|
16775
|
+
}
|
|
15974
16776
|
async function leaveLobby(client, args) {
|
|
15975
16777
|
try {
|
|
15976
16778
|
const result = await client.sdk.lobbies.leaveLobby(args.lobbyId);
|
|
@@ -16040,7 +16842,7 @@ async function joinQueue(client, args) {
|
|
|
16040
16842
|
gameId,
|
|
16041
16843
|
matched: !!matched,
|
|
16042
16844
|
...spectateUrl && { spectateUrl },
|
|
16043
|
-
hint: matched ? `Game
|
|
16845
|
+
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."
|
|
16044
16846
|
}
|
|
16045
16847
|
};
|
|
16046
16848
|
} catch (error) {
|
|
@@ -16115,6 +16917,23 @@ async function donateToPot(client, args) {
|
|
|
16115
16917
|
const spendErr = client.checkSpendLimit(args.amount);
|
|
16116
16918
|
if (spendErr) return { error: spendErr, isError: true };
|
|
16117
16919
|
const amountMinor = Math.round(args.amount * 1e6);
|
|
16920
|
+
if (client.externalSignerMode) {
|
|
16921
|
+
const prepared = await client.sdk.games.prepareGameDonation(
|
|
16922
|
+
args.gameId,
|
|
16923
|
+
amountMinor
|
|
16924
|
+
);
|
|
16925
|
+
return {
|
|
16926
|
+
data: {
|
|
16927
|
+
needsSigning: true,
|
|
16928
|
+
unsignedTx: prepared.transaction,
|
|
16929
|
+
confirmWith: {
|
|
16930
|
+
tool: "dim_confirm_donate_to_pot",
|
|
16931
|
+
params: { gameId: args.gameId, amount: amountMinor }
|
|
16932
|
+
},
|
|
16933
|
+
hint: "Sign and broadcast unsignedTx with send_solana_transaction, then call dim_confirm_donate_to_pot with the signature."
|
|
16934
|
+
}
|
|
16935
|
+
};
|
|
16936
|
+
}
|
|
16118
16937
|
const result = await client.sdk.games.sendDonation(
|
|
16119
16938
|
args.gameId,
|
|
16120
16939
|
amountMinor
|
|
@@ -16138,6 +16957,29 @@ async function donateToPot(client, args) {
|
|
|
16138
16957
|
};
|
|
16139
16958
|
}
|
|
16140
16959
|
}
|
|
16960
|
+
async function confirmDonateToPot(client, args) {
|
|
16961
|
+
try {
|
|
16962
|
+
const result = await client.sdk.games.confirmGameDonationSignature(
|
|
16963
|
+
args.gameId,
|
|
16964
|
+
args.amount,
|
|
16965
|
+
args.signature
|
|
16966
|
+
);
|
|
16967
|
+
client.recordSpend(args.amount);
|
|
16968
|
+
return {
|
|
16969
|
+
data: {
|
|
16970
|
+
success: true,
|
|
16971
|
+
gameId: args.gameId,
|
|
16972
|
+
signature: result.signature,
|
|
16973
|
+
status: result.status
|
|
16974
|
+
}
|
|
16975
|
+
};
|
|
16976
|
+
} catch (error) {
|
|
16977
|
+
return {
|
|
16978
|
+
error: `Failed to confirm donation: ${error instanceof Error ? error.message : String(error)}`,
|
|
16979
|
+
isError: true
|
|
16980
|
+
};
|
|
16981
|
+
}
|
|
16982
|
+
}
|
|
16141
16983
|
async function getGame(client, args) {
|
|
16142
16984
|
try {
|
|
16143
16985
|
const game = await client.sdk.games.getGame(args.gameId);
|
|
@@ -16149,6 +16991,12 @@ async function getGame(client, args) {
|
|
|
16149
16991
|
};
|
|
16150
16992
|
}
|
|
16151
16993
|
}
|
|
16994
|
+
function isInStartingPhase(state) {
|
|
16995
|
+
const roundState = state.roundState;
|
|
16996
|
+
if (roundState?.phase === "starting") return true;
|
|
16997
|
+
if (state.status === "active" && state.currentPlayerId == null) return true;
|
|
16998
|
+
return false;
|
|
16999
|
+
}
|
|
16152
17000
|
async function gameLoop(client, args) {
|
|
16153
17001
|
try {
|
|
16154
17002
|
const { gameId } = args;
|
|
@@ -16166,7 +17014,7 @@ async function gameLoop(client, args) {
|
|
|
16166
17014
|
if (state?.status === "completed") {
|
|
16167
17015
|
return buildGameLoopReturn(client, gameId, state, "completed");
|
|
16168
17016
|
}
|
|
16169
|
-
if (state?.currentPlayerId === client.currentUserId) {
|
|
17017
|
+
if (state?.currentPlayerId === client.currentUserId && !isInStartingPhase(state)) {
|
|
16170
17018
|
return buildGameLoopReturn(client, gameId, state, "your-turn");
|
|
16171
17019
|
}
|
|
16172
17020
|
await raceTimeout(
|
|
@@ -16792,10 +17640,10 @@ async function checkNotifications(client) {
|
|
|
16792
17640
|
isError: true
|
|
16793
17641
|
};
|
|
16794
17642
|
}
|
|
16795
|
-
const [notifications, dmThreads,
|
|
17643
|
+
const [notifications, dmThreads, friendRequestsPage] = await Promise.all([
|
|
16796
17644
|
client.sdk.notifications.list({ page: 1, limit: 20 }),
|
|
16797
17645
|
client.sdk.chat.listDmThreads(),
|
|
16798
|
-
client.sdk.users.getIncomingFriendRequests()
|
|
17646
|
+
client.sdk.users.getIncomingFriendRequests({ limit: 20 })
|
|
16799
17647
|
]);
|
|
16800
17648
|
const unreadDms = dmThreads.filter(
|
|
16801
17649
|
(t) => (t.unreadCount ?? 0) > 0
|
|
@@ -16807,9 +17655,10 @@ async function checkNotifications(client) {
|
|
|
16807
17655
|
unreadNotificationCount: notifications.unreadCount,
|
|
16808
17656
|
notifications: notifications.notifications.filter((n) => !n.read),
|
|
16809
17657
|
unreadDmThreads: unreadDms,
|
|
16810
|
-
incomingFriendRequests:
|
|
17658
|
+
incomingFriendRequests: friendRequestsPage.items,
|
|
17659
|
+
hasMoreIncomingFriendRequests: !!friendRequestsPage.nextCursor,
|
|
16811
17660
|
pendingWsEvents: client.pendingEventCount,
|
|
16812
|
-
hint: "Use dim_get_pending_events to drain buffered real-time events."
|
|
17661
|
+
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."
|
|
16813
17662
|
}
|
|
16814
17663
|
};
|
|
16815
17664
|
} catch (error) {
|
|
@@ -17195,6 +18044,22 @@ async function getGameHistory(client, args) {
|
|
|
17195
18044
|
};
|
|
17196
18045
|
}
|
|
17197
18046
|
}
|
|
18047
|
+
async function reportUser(client, args) {
|
|
18048
|
+
try {
|
|
18049
|
+
await client.sdk.reports.create(args.userId, args.reason);
|
|
18050
|
+
return {
|
|
18051
|
+
data: {
|
|
18052
|
+
success: true,
|
|
18053
|
+
hint: "Report submitted. The DIM moderation team will review it."
|
|
18054
|
+
}
|
|
18055
|
+
};
|
|
18056
|
+
} catch (error) {
|
|
18057
|
+
return {
|
|
18058
|
+
error: `Failed to report user: ${error instanceof Error ? error.message : String(error)}`,
|
|
18059
|
+
isError: true
|
|
18060
|
+
};
|
|
18061
|
+
}
|
|
18062
|
+
}
|
|
17198
18063
|
async function getMyStats(client) {
|
|
17199
18064
|
try {
|
|
17200
18065
|
if (!client.currentUserId) {
|
|
@@ -17225,6 +18090,36 @@ async function getMyStats(client) {
|
|
|
17225
18090
|
|
|
17226
18091
|
// ../dim-agent-core/src/tools/index.ts
|
|
17227
18092
|
var TOOL_DEFINITIONS = [
|
|
18093
|
+
// ── External signer mode (auth) ──────────────────────────────────────
|
|
18094
|
+
{
|
|
18095
|
+
name: "dim_request_auth_message",
|
|
18096
|
+
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.",
|
|
18097
|
+
params: {
|
|
18098
|
+
address: {
|
|
18099
|
+
type: "string",
|
|
18100
|
+
description: "Your Solana wallet address (base58)",
|
|
18101
|
+
required: true
|
|
18102
|
+
}
|
|
18103
|
+
},
|
|
18104
|
+
execute: (c, a) => requestAuthMessage(c, a)
|
|
18105
|
+
},
|
|
18106
|
+
{
|
|
18107
|
+
name: "dim_complete_login",
|
|
18108
|
+
description: "External wallet login step 2: provide the wallet address and signature from sign_solana_message to complete authentication with DIM.",
|
|
18109
|
+
params: {
|
|
18110
|
+
address: {
|
|
18111
|
+
type: "string",
|
|
18112
|
+
description: "Your Solana wallet address (base58)",
|
|
18113
|
+
required: true
|
|
18114
|
+
},
|
|
18115
|
+
signature: {
|
|
18116
|
+
type: "string",
|
|
18117
|
+
description: "Signature from sign_solana_message \u2014 base58, base64, or base64url accepted",
|
|
18118
|
+
required: true
|
|
18119
|
+
}
|
|
18120
|
+
},
|
|
18121
|
+
execute: (c, a) => completeLogin(c, a)
|
|
18122
|
+
},
|
|
17228
18123
|
// ── Auth ─────────────────────────────────────────────────────────────
|
|
17229
18124
|
{
|
|
17230
18125
|
name: "dim_login",
|
|
@@ -17378,9 +18273,23 @@ var TOOL_DEFINITIONS = [
|
|
|
17378
18273
|
},
|
|
17379
18274
|
{
|
|
17380
18275
|
name: "dim_get_incoming_friend_requests",
|
|
17381
|
-
description: "List pending incoming friend requests.",
|
|
17382
|
-
params: {
|
|
17383
|
-
|
|
18276
|
+
description: "List pending incoming friend requests. Returns { items, nextCursor, hasMore }. Pass cursor from a previous response to page through results.",
|
|
18277
|
+
params: {
|
|
18278
|
+
limit: {
|
|
18279
|
+
type: "number",
|
|
18280
|
+
description: "Maximum number of requests to return (1\u2013100, default 50).",
|
|
18281
|
+
required: false
|
|
18282
|
+
},
|
|
18283
|
+
cursor: {
|
|
18284
|
+
type: "string",
|
|
18285
|
+
description: "Pagination cursor from a previous response nextCursor field. Omit for the first page.",
|
|
18286
|
+
required: false
|
|
18287
|
+
}
|
|
18288
|
+
},
|
|
18289
|
+
execute: (c, a) => getIncomingFriendRequests(
|
|
18290
|
+
c,
|
|
18291
|
+
a
|
|
18292
|
+
)
|
|
17384
18293
|
},
|
|
17385
18294
|
// ── Chat ─────────────────────────────────────────────────────────────
|
|
17386
18295
|
{
|
|
@@ -17506,6 +18415,90 @@ var TOOL_DEFINITIONS = [
|
|
|
17506
18415
|
},
|
|
17507
18416
|
execute: (c, a) => tipUser(c, a)
|
|
17508
18417
|
},
|
|
18418
|
+
{
|
|
18419
|
+
name: "dim_confirm_send_usdc",
|
|
18420
|
+
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.",
|
|
18421
|
+
params: {
|
|
18422
|
+
signature: {
|
|
18423
|
+
type: "string",
|
|
18424
|
+
description: "On-chain transaction signature returned by send_solana_transaction",
|
|
18425
|
+
required: true
|
|
18426
|
+
},
|
|
18427
|
+
recipientAddress: {
|
|
18428
|
+
type: "string",
|
|
18429
|
+
description: "Recipient Solana address (from confirmWith.params)",
|
|
18430
|
+
required: true
|
|
18431
|
+
},
|
|
18432
|
+
amount: {
|
|
18433
|
+
type: "number",
|
|
18434
|
+
description: "Amount in USDC minor units (from confirmWith.params)",
|
|
18435
|
+
required: true
|
|
18436
|
+
},
|
|
18437
|
+
fee: {
|
|
18438
|
+
type: "number",
|
|
18439
|
+
description: "Fee in minor units (from confirmWith.params)"
|
|
18440
|
+
},
|
|
18441
|
+
token: {
|
|
18442
|
+
type: "string",
|
|
18443
|
+
description: "Token type: USDC or SOL (default: USDC)"
|
|
18444
|
+
},
|
|
18445
|
+
ataCreated: {
|
|
18446
|
+
type: "string",
|
|
18447
|
+
description: "Whether a new ATA was created (from confirmWith.params)"
|
|
18448
|
+
},
|
|
18449
|
+
recipientInput: {
|
|
18450
|
+
type: "string",
|
|
18451
|
+
description: "Original recipient input (from confirmWith.params)"
|
|
18452
|
+
}
|
|
18453
|
+
},
|
|
18454
|
+
execute: (c, a) => confirmSendUsdc(
|
|
18455
|
+
c,
|
|
18456
|
+
a
|
|
18457
|
+
)
|
|
18458
|
+
},
|
|
18459
|
+
{
|
|
18460
|
+
name: "dim_confirm_tip_user",
|
|
18461
|
+
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.",
|
|
18462
|
+
params: {
|
|
18463
|
+
signature: {
|
|
18464
|
+
type: "string",
|
|
18465
|
+
description: "On-chain transaction signature returned by send_solana_transaction",
|
|
18466
|
+
required: true
|
|
18467
|
+
},
|
|
18468
|
+
recipientAddress: {
|
|
18469
|
+
type: "string",
|
|
18470
|
+
description: "Recipient Solana address (from confirmWith.params)",
|
|
18471
|
+
required: true
|
|
18472
|
+
},
|
|
18473
|
+
recipientUserId: {
|
|
18474
|
+
type: "string",
|
|
18475
|
+
description: "Recipient user ID (from confirmWith.params)",
|
|
18476
|
+
required: true
|
|
18477
|
+
},
|
|
18478
|
+
recipientUsername: {
|
|
18479
|
+
type: "string",
|
|
18480
|
+
description: "Recipient username (from confirmWith.params)",
|
|
18481
|
+
required: true
|
|
18482
|
+
},
|
|
18483
|
+
amount: {
|
|
18484
|
+
type: "number",
|
|
18485
|
+
description: "Amount in USDC minor units (from confirmWith.params)",
|
|
18486
|
+
required: true
|
|
18487
|
+
},
|
|
18488
|
+
fee: {
|
|
18489
|
+
type: "number",
|
|
18490
|
+
description: "Fee in minor units (from confirmWith.params)"
|
|
18491
|
+
},
|
|
18492
|
+
ataCreated: {
|
|
18493
|
+
type: "string",
|
|
18494
|
+
description: "Whether a new ATA was created (from confirmWith.params)"
|
|
18495
|
+
}
|
|
18496
|
+
},
|
|
18497
|
+
execute: (c, a) => confirmTipUser(
|
|
18498
|
+
c,
|
|
18499
|
+
a
|
|
18500
|
+
)
|
|
18501
|
+
},
|
|
17509
18502
|
{
|
|
17510
18503
|
name: "dim_get_wallet_activity",
|
|
17511
18504
|
description: "Get recent wallet transaction activity (deposits, payouts, transfers, refunds) and your DIM wallet address. Highlights any claimable items.",
|
|
@@ -17565,6 +18558,23 @@ var TOOL_DEFINITIONS = [
|
|
|
17565
18558
|
params: {},
|
|
17566
18559
|
execute: (c) => getMyStats(c)
|
|
17567
18560
|
},
|
|
18561
|
+
{
|
|
18562
|
+
name: "dim_report_user",
|
|
18563
|
+
description: "Report a user for cheating, harassment, or other violations. Rate limited to 5 reports per hour.",
|
|
18564
|
+
params: {
|
|
18565
|
+
userId: {
|
|
18566
|
+
type: "string",
|
|
18567
|
+
description: "The ID of the user to report",
|
|
18568
|
+
required: true
|
|
18569
|
+
},
|
|
18570
|
+
reason: {
|
|
18571
|
+
type: "string",
|
|
18572
|
+
description: "Reason for the report (max 300 characters)",
|
|
18573
|
+
required: true
|
|
18574
|
+
}
|
|
18575
|
+
},
|
|
18576
|
+
execute: (c, a) => reportUser(c, a)
|
|
18577
|
+
},
|
|
17568
18578
|
{
|
|
17569
18579
|
name: "dim_create_lobby",
|
|
17570
18580
|
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.",
|
|
@@ -17602,6 +18612,23 @@ var TOOL_DEFINITIONS = [
|
|
|
17602
18612
|
},
|
|
17603
18613
|
execute: (c, a) => depositForLobby(c, a)
|
|
17604
18614
|
},
|
|
18615
|
+
{
|
|
18616
|
+
name: "dim_confirm_lobby_deposit",
|
|
18617
|
+
description: "External wallet: confirm a lobby deposit after signing and broadcasting the transaction. Polls until the deposit is confirmed on-chain, then returns canProceedToQueue.",
|
|
18618
|
+
params: {
|
|
18619
|
+
lobbyId: {
|
|
18620
|
+
type: "string",
|
|
18621
|
+
description: "The lobby ID (from confirmWith.params)",
|
|
18622
|
+
required: true
|
|
18623
|
+
},
|
|
18624
|
+
signature: {
|
|
18625
|
+
type: "string",
|
|
18626
|
+
description: "On-chain transaction signature returned by send_solana_transaction",
|
|
18627
|
+
required: true
|
|
18628
|
+
}
|
|
18629
|
+
},
|
|
18630
|
+
execute: (c, a) => confirmLobbyDeposit(c, a)
|
|
18631
|
+
},
|
|
17605
18632
|
{
|
|
17606
18633
|
name: "dim_leave_lobby",
|
|
17607
18634
|
description: "Leave a lobby. Use this to exit a lobby you created or joined. Returns when you have left.",
|
|
@@ -17694,6 +18721,31 @@ var TOOL_DEFINITIONS = [
|
|
|
17694
18721
|
},
|
|
17695
18722
|
execute: (c, a) => gameLoop(c, a)
|
|
17696
18723
|
},
|
|
18724
|
+
{
|
|
18725
|
+
name: "dim_confirm_donate_to_pot",
|
|
18726
|
+
description: "External wallet: confirm a game pot donation after signing and broadcasting the transaction.",
|
|
18727
|
+
params: {
|
|
18728
|
+
signature: {
|
|
18729
|
+
type: "string",
|
|
18730
|
+
description: "On-chain transaction signature returned by send_solana_transaction",
|
|
18731
|
+
required: true
|
|
18732
|
+
},
|
|
18733
|
+
gameId: {
|
|
18734
|
+
type: "string",
|
|
18735
|
+
description: "The game ID (from confirmWith.params)",
|
|
18736
|
+
required: true
|
|
18737
|
+
},
|
|
18738
|
+
amount: {
|
|
18739
|
+
type: "number",
|
|
18740
|
+
description: "Amount in USDC minor units (from confirmWith.params)",
|
|
18741
|
+
required: true
|
|
18742
|
+
}
|
|
18743
|
+
},
|
|
18744
|
+
execute: (c, a) => confirmDonateToPot(
|
|
18745
|
+
c,
|
|
18746
|
+
a
|
|
18747
|
+
)
|
|
18748
|
+
},
|
|
17697
18749
|
{
|
|
17698
18750
|
name: "dim_request_rematch",
|
|
17699
18751
|
description: "Request a rematch after a completed game. If both players request, a lobby is created automatically server-side.",
|
|
@@ -18087,11 +19139,18 @@ async function executeWithAuthRetry(client, tool, params) {
|
|
|
18087
19139
|
if (tool.name === "dim_login") {
|
|
18088
19140
|
return tool.execute(client, params);
|
|
18089
19141
|
}
|
|
19142
|
+
if (client.externalSignerMode) {
|
|
19143
|
+
return tool.execute(client, params);
|
|
19144
|
+
}
|
|
18090
19145
|
try {
|
|
18091
19146
|
const result = await tool.execute(client, params);
|
|
18092
19147
|
if (!result.error && !result.isError) return result;
|
|
18093
19148
|
if (!isUnauthorizedResult(result)) return result;
|
|
18094
|
-
|
|
19149
|
+
try {
|
|
19150
|
+
await client.authenticate();
|
|
19151
|
+
} catch {
|
|
19152
|
+
return { error: UNAUTHORIZED_MESSAGE, isError: true };
|
|
19153
|
+
}
|
|
18095
19154
|
const retryResult = await tool.execute(client, params);
|
|
18096
19155
|
if (retryResult.isError || retryResult.error) {
|
|
18097
19156
|
if (isUnauthorizedResult(retryResult)) {
|
|
@@ -18294,6 +19353,10 @@ function registerResources(server2, client) {
|
|
|
18294
19353
|
}
|
|
18295
19354
|
|
|
18296
19355
|
// src/server.ts
|
|
19356
|
+
var HOSTED_EXCLUDED_TOOLS = /* @__PURE__ */ new Set([
|
|
19357
|
+
"dim_game_loop",
|
|
19358
|
+
"dim_get_pending_events"
|
|
19359
|
+
]);
|
|
18297
19360
|
function paramToZod(p) {
|
|
18298
19361
|
let schema;
|
|
18299
19362
|
if (p.enum) {
|
|
@@ -18314,14 +19377,14 @@ function paramToZod(p) {
|
|
|
18314
19377
|
if (!p.required) schema = schema.optional();
|
|
18315
19378
|
return schema.describe(p.description);
|
|
18316
19379
|
}
|
|
18317
|
-
function
|
|
19380
|
+
function buildMcpServer(client, mode) {
|
|
18318
19381
|
const server2 = new McpServer({
|
|
18319
19382
|
name: "dim",
|
|
18320
19383
|
version: "1.0.0",
|
|
18321
19384
|
description: "DIM Gaming Platform \u2014 Play games, chat, send USDC, challenge users, and earn referral income. DIM is noncustodial; your Solana address is returned by dim_get_profile and dim_get_balance. Start by calling dim_login. After login: set a username with dim_set_username, check balance with dim_get_balance, list games with dim_list_games, or apply a referral code with dim_apply_referral_code for a 10% fee discount."
|
|
18322
19385
|
});
|
|
18323
|
-
const client = new DimAgentClient(config);
|
|
18324
19386
|
for (const tool of TOOL_DEFINITIONS) {
|
|
19387
|
+
if (mode === "hosted" && HOSTED_EXCLUDED_TOOLS.has(tool.name)) continue;
|
|
18325
19388
|
const zodParams = {};
|
|
18326
19389
|
for (const [key, param] of Object.entries(tool.params)) {
|
|
18327
19390
|
zodParams[key] = paramToZod(param);
|
|
@@ -18351,6 +19414,7 @@ function createDimMcpServer(config) {
|
|
|
18351
19414
|
}
|
|
18352
19415
|
);
|
|
18353
19416
|
}
|
|
19417
|
+
const gameHint = mode === "hosted" ? "Game flow: dim_create_lobby \u2192 dim_deposit_for_lobby (returns unsignedTx \u2014 sign with Phantom MCP) \u2192 dim_join_queue \u2192 poll dim_get_lobby until matched \u2192 dim_get_game_state \u2192 dim_submit_action \u2192 repeat. For real-time game_loop, use the DIM CLI (npx @dimcool/cli)." : "Game flow: For paid lobbies, dim_create_lobby \u2192 dim_deposit_for_lobby \u2192 dim_join_queue. For free lobbies, dim_create_lobby \u2192 dim_join_queue. Then dim_game_loop (blocks until your turn) \u2192 dim_submit_action \u2192 dim_game_loop \u2192 repeat. Use dim_leave_lobby to exit a lobby.";
|
|
18354
19418
|
server2.tool(
|
|
18355
19419
|
"dim_list_instructions",
|
|
18356
19420
|
"List all available DIM tools with short descriptions.",
|
|
@@ -18364,7 +19428,7 @@ function createDimMcpServer(config) {
|
|
|
18364
19428
|
wallet_linkage: WALLET_LINKAGE_NOTE,
|
|
18365
19429
|
wallet_address: "Your DIM Solana address is returned by dim_get_profile, dim_get_balance, dim_get_wallet_activity, and dim_get_wallet_address. Use it to confirm which address is tied to DIM.",
|
|
18366
19430
|
wallet_activity: "For deposits, transfers, payouts, and claimable items use dim_get_wallet_activity.",
|
|
18367
|
-
hint:
|
|
19431
|
+
hint: gameHint,
|
|
18368
19432
|
spectate_on_game_start: "When a game starts (dim_join_queue or dim_accept_challenge returns matched/gameId), tell the user they can spectate at the spectateUrl in the tool response, or at https://dim.cool/spectate/[your DIM username].",
|
|
18369
19433
|
daily_summary: "At least once per day, report to the operator your DIM progress: call dim_get_game_history (and optionally dim_get_my_stats and dim_get_referral_summary), then summarize games played and referral earnings.",
|
|
18370
19434
|
move_formats: {
|
|
@@ -18385,6 +19449,15 @@ function createDimMcpServer(config) {
|
|
|
18385
19449
|
})
|
|
18386
19450
|
);
|
|
18387
19451
|
registerResources(server2, client);
|
|
19452
|
+
return server2;
|
|
19453
|
+
}
|
|
19454
|
+
function createDimMcpServer(clientOrConfig, options) {
|
|
19455
|
+
const mode = options?.mode ?? "local";
|
|
19456
|
+
if (clientOrConfig instanceof DimAgentClient) {
|
|
19457
|
+
return buildMcpServer(clientOrConfig, mode);
|
|
19458
|
+
}
|
|
19459
|
+
const client = new DimAgentClient(clientOrConfig);
|
|
19460
|
+
const server2 = buildMcpServer(client, mode);
|
|
18388
19461
|
return { server: server2, client };
|
|
18389
19462
|
}
|
|
18390
19463
|
|
|
@@ -18517,12 +19590,11 @@ if (cliArgs[0] === "init-wallet") {
|
|
|
18517
19590
|
var walletPrivateKey = await resolveWalletPrivateKey(cliArgs);
|
|
18518
19591
|
if (!walletPrivateKey) {
|
|
18519
19592
|
console.error(
|
|
18520
|
-
"
|
|
19593
|
+
"No DIM wallet key configured \u2014 starting in external signer mode.\nUse dim_request_auth_message \u2192 sign_solana_message \u2192 dim_complete_login to authenticate.\n\nTo use a dedicated wallet instead:\n DIM_WALLET_PRIVATE_KEY=your-base58-key npx @dimcool/mcp\n npx @dimcool/mcp init-wallet\n DIM_WALLET_AUTO_CREATE=true npx @dimcool/mcp"
|
|
18521
19594
|
);
|
|
18522
|
-
process.exit(1);
|
|
18523
19595
|
}
|
|
18524
19596
|
var { server } = createDimMcpServer({
|
|
18525
|
-
walletPrivateKey,
|
|
19597
|
+
walletPrivateKey: walletPrivateKey ?? void 0,
|
|
18526
19598
|
apiUrl: process.env.DIM_API_URL || "https://api.dim.cool",
|
|
18527
19599
|
referralCode: process.env.DIM_REFERRAL_CODE
|
|
18528
19600
|
});
|