@dimcool/mcp 0.1.29 → 0.1.30
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 +22 -12
- package/dist/index.js +945 -127
- package/package.json +1 -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();
|
|
@@ -11823,11 +11835,25 @@ var Users = class {
|
|
|
11823
11835
|
async removeFriend(userId) {
|
|
11824
11836
|
return this.http.delete(`/friends/${userId}`);
|
|
11825
11837
|
}
|
|
11826
|
-
async getIncomingFriendRequests() {
|
|
11827
|
-
|
|
11838
|
+
async getIncomingFriendRequests(opts) {
|
|
11839
|
+
const params = new URLSearchParams();
|
|
11840
|
+
if (opts?.limit !== void 0)
|
|
11841
|
+
params.append("limit", opts.limit.toString());
|
|
11842
|
+
if (opts?.cursor !== void 0) params.append("cursor", opts.cursor);
|
|
11843
|
+
const qs = params.toString();
|
|
11844
|
+
return this.http.get(
|
|
11845
|
+
qs ? `/friends/requests/incoming?${qs}` : "/friends/requests/incoming"
|
|
11846
|
+
);
|
|
11828
11847
|
}
|
|
11829
|
-
async getOutgoingFriendRequests() {
|
|
11830
|
-
|
|
11848
|
+
async getOutgoingFriendRequests(opts) {
|
|
11849
|
+
const params = new URLSearchParams();
|
|
11850
|
+
if (opts?.limit !== void 0)
|
|
11851
|
+
params.append("limit", opts.limit.toString());
|
|
11852
|
+
if (opts?.cursor !== void 0) params.append("cursor", opts.cursor);
|
|
11853
|
+
const qs = params.toString();
|
|
11854
|
+
return this.http.get(
|
|
11855
|
+
qs ? `/friends/requests/outgoing?${qs}` : "/friends/requests/outgoing"
|
|
11856
|
+
);
|
|
11831
11857
|
}
|
|
11832
11858
|
async acceptFriendRequest(userId) {
|
|
11833
11859
|
return this.http.post(
|
|
@@ -11914,6 +11940,9 @@ var FeatureFlags = class {
|
|
|
11914
11940
|
this.loaded = true;
|
|
11915
11941
|
return this.flags;
|
|
11916
11942
|
}
|
|
11943
|
+
async getAdminFeatureFlags() {
|
|
11944
|
+
return this.http.get("/feature-flags/admin");
|
|
11945
|
+
}
|
|
11917
11946
|
isEnabledFlag(name) {
|
|
11918
11947
|
const flag = this.flags.find((f) => f.name === name);
|
|
11919
11948
|
return flag?.enabled ?? false;
|
|
@@ -11933,10 +11962,11 @@ var FeatureFlags = class {
|
|
|
11933
11962
|
}
|
|
11934
11963
|
return flag;
|
|
11935
11964
|
}
|
|
11936
|
-
async createFeatureFlag(name, enabled) {
|
|
11965
|
+
async createFeatureFlag(name, enabled, description) {
|
|
11937
11966
|
const flag = await this.http.post("/feature-flags", {
|
|
11938
11967
|
name,
|
|
11939
|
-
enabled
|
|
11968
|
+
enabled,
|
|
11969
|
+
...description !== void 0 && { description }
|
|
11940
11970
|
});
|
|
11941
11971
|
this.flags.push(flag);
|
|
11942
11972
|
return flag;
|
|
@@ -11947,11 +11977,17 @@ var Lobbies = class {
|
|
|
11947
11977
|
this.http = http;
|
|
11948
11978
|
this.logger = logger2;
|
|
11949
11979
|
}
|
|
11980
|
+
/** Called by SDK after the store is created so mutations can update state directly. */
|
|
11981
|
+
setLobbyStore(store) {
|
|
11982
|
+
this.lobbyStore = store;
|
|
11983
|
+
}
|
|
11950
11984
|
async createLobby(gameType, betAmount) {
|
|
11951
11985
|
return this.http.post("/lobbies", { gameType, betAmount });
|
|
11952
11986
|
}
|
|
11953
11987
|
async getLobby(lobbyId) {
|
|
11954
|
-
|
|
11988
|
+
const lobby = await this.http.get(`/lobbies/${lobbyId}`);
|
|
11989
|
+
this.lobbyStore?.setBaseState([lobby]);
|
|
11990
|
+
return lobby;
|
|
11955
11991
|
}
|
|
11956
11992
|
async inviteFriend(lobbyId, friendId) {
|
|
11957
11993
|
return this.http.post(`/lobbies/${lobbyId}/invite`, {
|
|
@@ -11962,7 +11998,9 @@ var Lobbies = class {
|
|
|
11962
11998
|
return this.http.post(`/lobbies/${lobbyId}/accept-invite`, {});
|
|
11963
11999
|
}
|
|
11964
12000
|
async joinLobby(lobbyId) {
|
|
11965
|
-
|
|
12001
|
+
const lobby = await this.http.post(`/lobbies/${lobbyId}/join`, {});
|
|
12002
|
+
this.lobbyStore?.setBaseState([lobby]);
|
|
12003
|
+
return lobby;
|
|
11966
12004
|
}
|
|
11967
12005
|
async removePlayer(lobbyId, userId) {
|
|
11968
12006
|
return this.http.delete(
|
|
@@ -11979,12 +12017,17 @@ var Lobbies = class {
|
|
|
11979
12017
|
return this.http.post(`/lobbies/${lobbyId}/join-queue`, {});
|
|
11980
12018
|
}
|
|
11981
12019
|
async cancelQueue(lobbyId) {
|
|
11982
|
-
|
|
12020
|
+
const lobby = await this.http.delete(`/lobbies/${lobbyId}/queue`);
|
|
12021
|
+
this.lobbyStore?.setBaseState([lobby]);
|
|
12022
|
+
return lobby;
|
|
11983
12023
|
}
|
|
11984
12024
|
async updateBetAmount(lobbyId, betAmount) {
|
|
11985
|
-
|
|
11986
|
-
|
|
11987
|
-
|
|
12025
|
+
const lobby = await this.http.patch(
|
|
12026
|
+
`/lobbies/${lobbyId}/bet-amount`,
|
|
12027
|
+
{ betAmount }
|
|
12028
|
+
);
|
|
12029
|
+
this.lobbyStore?.setBaseState([lobby]);
|
|
12030
|
+
return lobby;
|
|
11988
12031
|
}
|
|
11989
12032
|
async playSound(lobbyId, sound) {
|
|
11990
12033
|
return this.http.post(`/lobbies/${lobbyId}/sound`, {
|
|
@@ -12023,6 +12066,12 @@ var Games = class {
|
|
|
12023
12066
|
this.wallet = wallet;
|
|
12024
12067
|
this.logger = logger2;
|
|
12025
12068
|
}
|
|
12069
|
+
setGameStore(store) {
|
|
12070
|
+
this.gameStore = store;
|
|
12071
|
+
}
|
|
12072
|
+
setGameActionsStore(store) {
|
|
12073
|
+
this.gameActionsStore = store;
|
|
12074
|
+
}
|
|
12026
12075
|
async getAvailableGames() {
|
|
12027
12076
|
return this.http.get("/games/available");
|
|
12028
12077
|
}
|
|
@@ -12035,7 +12084,11 @@ var Games = class {
|
|
|
12035
12084
|
return this.http.get("/games/metrics");
|
|
12036
12085
|
}
|
|
12037
12086
|
async getGame(gameId) {
|
|
12038
|
-
|
|
12087
|
+
const existing = this.gameStore?.store.getState().gamesById[gameId];
|
|
12088
|
+
if (existing?.status === "completed") return existing;
|
|
12089
|
+
const game = await this.http.get(`/games/${gameId}`);
|
|
12090
|
+
this.gameStore?.setBaseState([game]);
|
|
12091
|
+
return game;
|
|
12039
12092
|
}
|
|
12040
12093
|
/**
|
|
12041
12094
|
* Get list of currently active (live) games. Public endpoint for spectating.
|
|
@@ -12062,7 +12115,13 @@ var Games = class {
|
|
|
12062
12115
|
* Get current game state with timer information
|
|
12063
12116
|
*/
|
|
12064
12117
|
async getGameState(gameId) {
|
|
12065
|
-
|
|
12118
|
+
const existing = this.gameActionsStore?.store.getState().statesByGameId[gameId];
|
|
12119
|
+
if (existing?.status === "completed") return existing;
|
|
12120
|
+
const state = await this.http.get(
|
|
12121
|
+
`/games/${gameId}/state`
|
|
12122
|
+
);
|
|
12123
|
+
this.gameActionsStore?.setBaseState(gameId, state);
|
|
12124
|
+
return state;
|
|
12066
12125
|
}
|
|
12067
12126
|
/**
|
|
12068
12127
|
* Request a rematch for a completed game
|
|
@@ -12098,8 +12157,18 @@ var Games = class {
|
|
|
12098
12157
|
{ amountMinor, signedTransaction }
|
|
12099
12158
|
);
|
|
12100
12159
|
}
|
|
12160
|
+
/**
|
|
12161
|
+
* Confirm a donation that was already sent by the client (signAndSendTransaction path).
|
|
12162
|
+
*/
|
|
12163
|
+
async confirmGameDonationSignature(gameId, amountMinor, signature) {
|
|
12164
|
+
return this.http.post(
|
|
12165
|
+
`/games/${gameId}/donate/confirm-signature`,
|
|
12166
|
+
{ amountMinor, signature }
|
|
12167
|
+
);
|
|
12168
|
+
}
|
|
12101
12169
|
/**
|
|
12102
12170
|
* One-call donation flow: prepare -> sign -> submit.
|
|
12171
|
+
* Automatically uses the right signing path (embedded vs injected wallet).
|
|
12103
12172
|
*/
|
|
12104
12173
|
async sendDonation(gameId, amountMinor) {
|
|
12105
12174
|
if (!this.wallet.hasSigner()) {
|
|
@@ -12108,16 +12177,10 @@ var Games = class {
|
|
|
12108
12177
|
);
|
|
12109
12178
|
}
|
|
12110
12179
|
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
|
-
);
|
|
12180
|
+
const result = await this.wallet.signAndDispatch(prepared.transaction, {
|
|
12181
|
+
onSigned: (signedTxBase64) => this.donateToGame(gameId, amountMinor, signedTxBase64),
|
|
12182
|
+
onSignedAndSent: (sig) => this.confirmGameDonationSignature(gameId, amountMinor, sig)
|
|
12183
|
+
});
|
|
12121
12184
|
return {
|
|
12122
12185
|
...result,
|
|
12123
12186
|
escrowAddress: prepared.escrowAddress,
|
|
@@ -12210,6 +12273,9 @@ var Chat = class {
|
|
|
12210
12273
|
this.logger = logger2;
|
|
12211
12274
|
this.retryOptions = { ...DEFAULT_RETRY_OPTIONS, ...retryOptions };
|
|
12212
12275
|
}
|
|
12276
|
+
setDmThreadsStore(store) {
|
|
12277
|
+
this.dmThreadsStore = store;
|
|
12278
|
+
}
|
|
12213
12279
|
encodeContextId(id) {
|
|
12214
12280
|
return encodeURIComponent(id);
|
|
12215
12281
|
}
|
|
@@ -12291,7 +12357,9 @@ var Chat = class {
|
|
|
12291
12357
|
return response;
|
|
12292
12358
|
}
|
|
12293
12359
|
async listDmThreads() {
|
|
12294
|
-
|
|
12360
|
+
const threads = await this.http.get("/chat/dm/threads");
|
|
12361
|
+
this.dmThreadsStore?.setBaseState(threads);
|
|
12362
|
+
return threads;
|
|
12295
12363
|
}
|
|
12296
12364
|
async getDmThread(dmKey) {
|
|
12297
12365
|
return this.http.get(
|
|
@@ -12379,21 +12447,32 @@ var Tips = class {
|
|
|
12379
12447
|
);
|
|
12380
12448
|
}
|
|
12381
12449
|
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
|
-
|
|
12450
|
+
const supportsPresign = !this.wallet.isSignAndSendMode();
|
|
12451
|
+
const prepared = await this.prepare({
|
|
12452
|
+
recipientUsername,
|
|
12453
|
+
amount,
|
|
12454
|
+
supportsPresign
|
|
12455
|
+
});
|
|
12456
|
+
const transfer = await this.wallet.signAndDispatch(prepared.transaction, {
|
|
12457
|
+
onSigned: (signedTxBase64) => this.wallet.submitTransfer(
|
|
12458
|
+
signedTxBase64,
|
|
12459
|
+
senderAddress,
|
|
12460
|
+
prepared.recipientAddress,
|
|
12461
|
+
prepared.amount,
|
|
12462
|
+
"USDC",
|
|
12463
|
+
false,
|
|
12464
|
+
prepared.recipientUsername
|
|
12465
|
+
),
|
|
12466
|
+
onSignedAndSent: (sig) => this.wallet.confirmTransferSignature(
|
|
12467
|
+
sig,
|
|
12468
|
+
senderAddress,
|
|
12469
|
+
prepared.recipientAddress,
|
|
12470
|
+
prepared.amount,
|
|
12471
|
+
"USDC",
|
|
12472
|
+
false,
|
|
12473
|
+
prepared.recipientUsername
|
|
12474
|
+
)
|
|
12475
|
+
});
|
|
12397
12476
|
const message = await this.chat.broadcastGlobalTip(
|
|
12398
12477
|
prepared.recipientUserId,
|
|
12399
12478
|
prepared.amount
|
|
@@ -12711,13 +12790,15 @@ var Wallet = class {
|
|
|
12711
12790
|
);
|
|
12712
12791
|
}
|
|
12713
12792
|
try {
|
|
12793
|
+
const supportsPresign = !this.isSignAndSendMode();
|
|
12714
12794
|
const response = await this.http.post(
|
|
12715
12795
|
"/wallets/transfer/prepare",
|
|
12716
12796
|
{
|
|
12717
12797
|
senderAddress,
|
|
12718
12798
|
recipient,
|
|
12719
12799
|
amount: amountMinor,
|
|
12720
|
-
token
|
|
12800
|
+
token,
|
|
12801
|
+
supportsPresign
|
|
12721
12802
|
}
|
|
12722
12803
|
);
|
|
12723
12804
|
this.logger.debug("Transfer prepared", {
|
|
@@ -12801,6 +12882,29 @@ var Wallet = class {
|
|
|
12801
12882
|
throw error;
|
|
12802
12883
|
}
|
|
12803
12884
|
}
|
|
12885
|
+
/**
|
|
12886
|
+
* Sign a prepared transaction and invoke the appropriate backend callback
|
|
12887
|
+
* based on the configured signer — without exposing the mode decision to callers.
|
|
12888
|
+
* - Embedded wallet (signAndSend): signs+sends, calls onSignedAndSent(signature)
|
|
12889
|
+
* - Injected wallet (signTransaction): signs locally, calls onSigned(signedTxBase64)
|
|
12890
|
+
*/
|
|
12891
|
+
async signAndDispatch(unsignedTxBase64, handlers) {
|
|
12892
|
+
if (!this.signer) {
|
|
12893
|
+
throw new Error(
|
|
12894
|
+
"No signer configured. Call setSigner() with a WalletSigner implementation first."
|
|
12895
|
+
);
|
|
12896
|
+
}
|
|
12897
|
+
const unsignedTx = Transaction2.from(base64ToBytes(unsignedTxBase64));
|
|
12898
|
+
if (this.isSignAndSendMode()) {
|
|
12899
|
+
const sig = await this.signAndSendTransaction(unsignedTx);
|
|
12900
|
+
return handlers.onSignedAndSent(sig);
|
|
12901
|
+
}
|
|
12902
|
+
const signedTx = await this.signTransaction(unsignedTx);
|
|
12903
|
+
const signedBase64 = bytesToBase64(
|
|
12904
|
+
signedTx.serialize({ requireAllSignatures: false })
|
|
12905
|
+
);
|
|
12906
|
+
return handlers.onSigned(signedBase64);
|
|
12907
|
+
}
|
|
12804
12908
|
/**
|
|
12805
12909
|
* Full transfer flow in one call: prepare -> sign -> submit
|
|
12806
12910
|
* Recipient can be username, .sol domain, or Solana address (resolved by backend).
|
|
@@ -12817,7 +12921,7 @@ var Wallet = class {
|
|
|
12817
12921
|
amount,
|
|
12818
12922
|
token
|
|
12819
12923
|
);
|
|
12820
|
-
const unsignedTx =
|
|
12924
|
+
const unsignedTx = Transaction2.from(base64ToBytes(prepared.transaction));
|
|
12821
12925
|
let submitted;
|
|
12822
12926
|
if (this.isSignAndSendMode()) {
|
|
12823
12927
|
const signature = await this.signAndSendTransaction(unsignedTx);
|
|
@@ -12882,12 +12986,32 @@ var Escrow = class {
|
|
|
12882
12986
|
* initializes depositStatus, and prepares the unsigned transaction in one call.
|
|
12883
12987
|
* Eliminates one HTTP round-trip vs startDeposits() + prepareDepositTransaction().
|
|
12884
12988
|
*/
|
|
12885
|
-
async prepareAndStartDeposit(lobbyId) {
|
|
12989
|
+
async prepareAndStartDeposit(lobbyId, supportsPresign) {
|
|
12990
|
+
const presign = supportsPresign ?? !this.wallet.isSignAndSendMode();
|
|
12886
12991
|
return this.http.post(
|
|
12887
12992
|
`/escrow/lobby/${lobbyId}/deposit/prepare-and-start`,
|
|
12888
|
-
{}
|
|
12993
|
+
{ supportsPresign: presign }
|
|
12889
12994
|
);
|
|
12890
12995
|
}
|
|
12996
|
+
/**
|
|
12997
|
+
* Sign and submit a prepared (unsigned) deposit transaction using the
|
|
12998
|
+
* configured signer. Automatically picks the right path:
|
|
12999
|
+
* - Embedded wallet: signAndSendTransaction → confirmDepositSignature
|
|
13000
|
+
* - Injected wallet: signTransaction → submitDeposit
|
|
13001
|
+
* Returns the on-chain signature.
|
|
13002
|
+
*/
|
|
13003
|
+
async signAndSubmitPreparedDeposit(lobbyId, unsignedTxBase64) {
|
|
13004
|
+
return this.wallet.signAndDispatch(unsignedTxBase64, {
|
|
13005
|
+
onSigned: async (signedTxBase64) => {
|
|
13006
|
+
const result = await this.submitDeposit(lobbyId, signedTxBase64);
|
|
13007
|
+
return result.signature;
|
|
13008
|
+
},
|
|
13009
|
+
onSignedAndSent: async (signature) => {
|
|
13010
|
+
await this.confirmDepositSignature(lobbyId, signature);
|
|
13011
|
+
return signature;
|
|
13012
|
+
}
|
|
13013
|
+
});
|
|
13014
|
+
}
|
|
12891
13015
|
/**
|
|
12892
13016
|
* Submit a signed deposit transaction
|
|
12893
13017
|
* The transaction will be submitted to the Solana network and confirmed
|
|
@@ -12930,8 +13054,12 @@ var Escrow = class {
|
|
|
12930
13054
|
"No signer configured. Use sdk.wallet.setSigner(...) first."
|
|
12931
13055
|
);
|
|
12932
13056
|
}
|
|
12933
|
-
const
|
|
12934
|
-
const
|
|
13057
|
+
const supportsPresign = !this.wallet.isSignAndSendMode();
|
|
13058
|
+
const { transaction } = await this.prepareAndStartDeposit(
|
|
13059
|
+
lobbyId,
|
|
13060
|
+
supportsPresign
|
|
13061
|
+
);
|
|
13062
|
+
const unsignedTx = Transaction3.from(base64ToBytes(transaction));
|
|
12935
13063
|
let signature;
|
|
12936
13064
|
if (this.wallet.isSignAndSendMode()) {
|
|
12937
13065
|
signature = await this.wallet.signAndSendTransaction(unsignedTx);
|
|
@@ -12965,8 +13093,13 @@ var Escrow = class {
|
|
|
12965
13093
|
};
|
|
12966
13094
|
}
|
|
12967
13095
|
/**
|
|
12968
|
-
*
|
|
12969
|
-
*
|
|
13096
|
+
* Deposit for a lobby and wait until the calling user's own deposit is confirmed.
|
|
13097
|
+
* Ideal for agents: returns as soon as your deposit is on-chain without waiting
|
|
13098
|
+
* for the other player. The server auto-joins the queue when all players deposit.
|
|
13099
|
+
*
|
|
13100
|
+
* Confirmation is handled asynchronously by the transaction queue processor.
|
|
13101
|
+
* This method polls the deposit status endpoint until the signature appears as
|
|
13102
|
+
* confirmed (up to 60 seconds).
|
|
12970
13103
|
*
|
|
12971
13104
|
* Automatically uses signAndSendTransaction or signTransaction based on signer capability.
|
|
12972
13105
|
*/
|
|
@@ -12976,26 +13109,45 @@ var Escrow = class {
|
|
|
12976
13109
|
"No signer configured. Use sdk.wallet.setSigner(...) first."
|
|
12977
13110
|
);
|
|
12978
13111
|
}
|
|
12979
|
-
const
|
|
12980
|
-
const
|
|
13112
|
+
const supportsPresign2 = !this.wallet.isSignAndSendMode();
|
|
13113
|
+
const { transaction } = await this.prepareAndStartDeposit(
|
|
13114
|
+
lobbyId,
|
|
13115
|
+
supportsPresign2
|
|
13116
|
+
);
|
|
13117
|
+
const unsignedTx = Transaction3.from(base64ToBytes(transaction));
|
|
12981
13118
|
let signature;
|
|
12982
13119
|
if (this.wallet.isSignAndSendMode()) {
|
|
12983
13120
|
signature = await this.wallet.signAndSendTransaction(unsignedTx);
|
|
12984
|
-
await this.
|
|
12985
|
-
`/escrow/lobby/${lobbyId}/deposit/confirm-signature?awaitConfirmation=true`,
|
|
12986
|
-
{ signature }
|
|
12987
|
-
);
|
|
13121
|
+
await this.confirmDepositSignature(lobbyId, signature);
|
|
12988
13122
|
} else {
|
|
12989
13123
|
const signedTx = await this.wallet.signTransaction(unsignedTx);
|
|
12990
13124
|
const signedBase64 = bytesToBase64(
|
|
12991
13125
|
signedTx.serialize({ requireAllSignatures: false })
|
|
12992
13126
|
);
|
|
12993
|
-
const
|
|
12994
|
-
|
|
12995
|
-
|
|
12996
|
-
|
|
13127
|
+
const submitResult = await this.submitDeposit(lobbyId, signedBase64);
|
|
13128
|
+
signature = submitResult.signature;
|
|
13129
|
+
}
|
|
13130
|
+
const MAX_WAIT_MS = 6e4;
|
|
13131
|
+
const POLL_INTERVAL_MS = 1e3;
|
|
13132
|
+
const startTime = Date.now();
|
|
13133
|
+
while (Date.now() - startTime < MAX_WAIT_MS) {
|
|
13134
|
+
const status = await this.getDepositStatus(lobbyId);
|
|
13135
|
+
const myDeposit = status.deposits.find(
|
|
13136
|
+
(d) => d.transactionHash === signature
|
|
13137
|
+
);
|
|
13138
|
+
if (myDeposit?.status === "confirmed") {
|
|
13139
|
+
return {
|
|
13140
|
+
signature,
|
|
13141
|
+
status: "confirmed",
|
|
13142
|
+
canProceedToQueue: status.canProceedToQueue
|
|
13143
|
+
};
|
|
13144
|
+
}
|
|
13145
|
+
if (status.allConfirmed && status.canProceedToQueue) {
|
|
13146
|
+
return { signature, status: "confirmed", canProceedToQueue: true };
|
|
13147
|
+
}
|
|
13148
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
12997
13149
|
}
|
|
12998
|
-
return { signature, status: "
|
|
13150
|
+
return { signature, status: "pending", canProceedToQueue: false };
|
|
12999
13151
|
}
|
|
13000
13152
|
async claimLobbyDepositRefund(lobbyId, depositSignature) {
|
|
13001
13153
|
return this.http.post(
|
|
@@ -13337,9 +13489,10 @@ var Support = class {
|
|
|
13337
13489
|
}
|
|
13338
13490
|
};
|
|
13339
13491
|
var Markets = class {
|
|
13340
|
-
constructor(http, logger2) {
|
|
13492
|
+
constructor(http, logger2, wallet) {
|
|
13341
13493
|
this.http = http;
|
|
13342
13494
|
this.logger = logger2;
|
|
13495
|
+
this.wallet = wallet;
|
|
13343
13496
|
}
|
|
13344
13497
|
/**
|
|
13345
13498
|
* Get the prediction market state for a game.
|
|
@@ -13381,6 +13534,35 @@ var Markets = class {
|
|
|
13381
13534
|
{ signedTransaction, outcomeId, amountMinor }
|
|
13382
13535
|
);
|
|
13383
13536
|
}
|
|
13537
|
+
/**
|
|
13538
|
+
* Confirm a buy order that was already broadcast by the client (signAndSendTransaction path).
|
|
13539
|
+
*/
|
|
13540
|
+
async confirmBuyOrderSignature(gameId, depositSignature, outcomeId, amountMinor) {
|
|
13541
|
+
return this.http.post(
|
|
13542
|
+
`/games/${gameId}/market/orders/buy/confirm-signature`,
|
|
13543
|
+
{ depositSignature, outcomeId, amountMinor }
|
|
13544
|
+
);
|
|
13545
|
+
}
|
|
13546
|
+
/**
|
|
13547
|
+
* One-call buy order: prepare → sign → submit, automatically choosing
|
|
13548
|
+
* the right signing path (embedded vs injected wallet).
|
|
13549
|
+
*/
|
|
13550
|
+
async buy(gameId, outcomeId, amountMinor) {
|
|
13551
|
+
if (!this.wallet?.hasSigner()) {
|
|
13552
|
+
throw new Error(
|
|
13553
|
+
"No signer configured. Use sdk.wallet.setSigner(...) first."
|
|
13554
|
+
);
|
|
13555
|
+
}
|
|
13556
|
+
const { transaction } = await this.prepareBuyOrder(
|
|
13557
|
+
gameId,
|
|
13558
|
+
outcomeId,
|
|
13559
|
+
amountMinor
|
|
13560
|
+
);
|
|
13561
|
+
return this.wallet.signAndDispatch(transaction, {
|
|
13562
|
+
onSigned: (signedTxBase64) => this.submitBuyOrder(gameId, signedTxBase64, outcomeId, amountMinor),
|
|
13563
|
+
onSignedAndSent: (sig) => this.confirmBuyOrderSignature(gameId, sig, outcomeId, amountMinor)
|
|
13564
|
+
});
|
|
13565
|
+
}
|
|
13384
13566
|
/**
|
|
13385
13567
|
* Sell shares back to the AMM pool.
|
|
13386
13568
|
* @param gameId - The game ID
|
|
@@ -13429,6 +13611,20 @@ var Markets = class {
|
|
|
13429
13611
|
return this.http.get(`/games/admin/markets${qs ? `?${qs}` : ""}`);
|
|
13430
13612
|
}
|
|
13431
13613
|
};
|
|
13614
|
+
var NoopAnalyticsClient = class {
|
|
13615
|
+
userLoggedIn(_user, _meta) {
|
|
13616
|
+
}
|
|
13617
|
+
userLoggedOut() {
|
|
13618
|
+
}
|
|
13619
|
+
sessionRestored(_user) {
|
|
13620
|
+
}
|
|
13621
|
+
track(_event, _properties) {
|
|
13622
|
+
}
|
|
13623
|
+
setUserProperties(_properties) {
|
|
13624
|
+
}
|
|
13625
|
+
group(_groupType, _groupKey, _properties) {
|
|
13626
|
+
}
|
|
13627
|
+
};
|
|
13432
13628
|
var BaseWsTransport = class {
|
|
13433
13629
|
constructor() {
|
|
13434
13630
|
this.roomRefs = /* @__PURE__ */ new Map();
|
|
@@ -13516,21 +13712,35 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
|
|
|
13516
13712
|
this.accessToken = null;
|
|
13517
13713
|
this.reconnectAttempts = 0;
|
|
13518
13714
|
this.reconnectTimer = null;
|
|
13519
|
-
this.
|
|
13520
|
-
this.
|
|
13521
|
-
this.reconnectDelay = 1e3;
|
|
13522
|
-
this.reconnectDelayMax = 3e4;
|
|
13715
|
+
this.needsReconnect = false;
|
|
13716
|
+
this.visibilityHandler = null;
|
|
13523
13717
|
this.wildcardHandlers = /* @__PURE__ */ new Set();
|
|
13524
13718
|
this.eventHandlers = /* @__PURE__ */ new Map();
|
|
13525
13719
|
this.registeredEvents = /* @__PURE__ */ new Set();
|
|
13526
13720
|
this.wildcardRegistered = false;
|
|
13527
13721
|
this.url = url2;
|
|
13722
|
+
if (typeof document !== "undefined") {
|
|
13723
|
+
this.visibilityHandler = () => {
|
|
13724
|
+
if (document.visibilityState === "visible" && !this.connectionState.connected) {
|
|
13725
|
+
this.reconnectImmediately();
|
|
13726
|
+
}
|
|
13727
|
+
};
|
|
13728
|
+
document.addEventListener("visibilitychange", this.visibilityHandler);
|
|
13729
|
+
}
|
|
13528
13730
|
}
|
|
13529
13731
|
connect() {
|
|
13732
|
+
if (this.reconnectTimer !== null) {
|
|
13733
|
+
clearTimeout(this.reconnectTimer);
|
|
13734
|
+
this.reconnectTimer = null;
|
|
13735
|
+
}
|
|
13736
|
+
this.reconnectAttempts = 0;
|
|
13530
13737
|
this.ensureSocket();
|
|
13531
13738
|
}
|
|
13532
13739
|
disconnect() {
|
|
13533
|
-
this.
|
|
13740
|
+
if (this.visibilityHandler && typeof document !== "undefined") {
|
|
13741
|
+
document.removeEventListener("visibilitychange", this.visibilityHandler);
|
|
13742
|
+
this.visibilityHandler = null;
|
|
13743
|
+
}
|
|
13534
13744
|
if (!this.socket) return;
|
|
13535
13745
|
this.socket.disconnect();
|
|
13536
13746
|
this.socket = null;
|
|
@@ -13629,13 +13839,13 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
|
|
|
13629
13839
|
reconnection: false
|
|
13630
13840
|
});
|
|
13631
13841
|
socket.on("connect", () => {
|
|
13632
|
-
this.clearPeriodicReconnect();
|
|
13633
13842
|
this.updateConnectionState({
|
|
13634
13843
|
connected: true,
|
|
13635
13844
|
connecting: false,
|
|
13636
13845
|
error: null
|
|
13637
13846
|
});
|
|
13638
|
-
const wasReconnect = this.
|
|
13847
|
+
const wasReconnect = this.needsReconnect;
|
|
13848
|
+
this.needsReconnect = false;
|
|
13639
13849
|
this.reconnectAttempts = 0;
|
|
13640
13850
|
this.onReconnect();
|
|
13641
13851
|
if (wasReconnect) {
|
|
@@ -13646,6 +13856,7 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
|
|
|
13646
13856
|
});
|
|
13647
13857
|
socket.on("disconnect", (reason) => {
|
|
13648
13858
|
this.updateConnectionState({ connected: false, connecting: false });
|
|
13859
|
+
this.needsReconnect = true;
|
|
13649
13860
|
this.clearSocketForReconnect();
|
|
13650
13861
|
if (reason === "io server disconnect") {
|
|
13651
13862
|
this.handleAuthFailure("io server disconnect");
|
|
@@ -13655,6 +13866,7 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
|
|
|
13655
13866
|
});
|
|
13656
13867
|
socket.on("connect_error", (error) => {
|
|
13657
13868
|
const message = error?.message || "connect_error";
|
|
13869
|
+
this.needsReconnect = true;
|
|
13658
13870
|
this.clearSocketForReconnect();
|
|
13659
13871
|
if (message.includes("unauthorized") || message.includes("401")) {
|
|
13660
13872
|
this.handleAuthFailure(message);
|
|
@@ -13682,31 +13894,31 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
|
|
|
13682
13894
|
this.socket.removeAllListeners();
|
|
13683
13895
|
this.socket = null;
|
|
13684
13896
|
}
|
|
13685
|
-
|
|
13686
|
-
if (this.
|
|
13687
|
-
|
|
13688
|
-
this.
|
|
13897
|
+
reconnectImmediately() {
|
|
13898
|
+
if (this.reconnectTimer !== null) {
|
|
13899
|
+
clearTimeout(this.reconnectTimer);
|
|
13900
|
+
this.reconnectTimer = null;
|
|
13689
13901
|
}
|
|
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);
|
|
13902
|
+
this.reconnectAttempts = 0;
|
|
13903
|
+
this.ensureSocket();
|
|
13698
13904
|
}
|
|
13699
13905
|
scheduleReconnect() {
|
|
13700
13906
|
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
|
-
);
|
|
13907
|
+
const attempt = this.reconnectAttempts;
|
|
13709
13908
|
this.reconnectAttempts += 1;
|
|
13909
|
+
let delay;
|
|
13910
|
+
if (attempt < _StandaloneWsTransport2.FAST_RETRY_LIMIT) {
|
|
13911
|
+
delay = _StandaloneWsTransport2.FAST_RETRY_DELAY_MS;
|
|
13912
|
+
} else {
|
|
13913
|
+
const backoffAttempt = attempt - _StandaloneWsTransport2.FAST_RETRY_LIMIT;
|
|
13914
|
+
delay = Math.min(
|
|
13915
|
+
_StandaloneWsTransport2.FAST_RETRY_DELAY_MS * Math.pow(2, backoffAttempt),
|
|
13916
|
+
_StandaloneWsTransport2.MAX_BACKOFF_MS
|
|
13917
|
+
);
|
|
13918
|
+
if (attempt === _StandaloneWsTransport2.FAST_RETRY_LIMIT) {
|
|
13919
|
+
this.dispatchEvent("connection:slow-retry", { timestamp: Date.now() });
|
|
13920
|
+
}
|
|
13921
|
+
}
|
|
13710
13922
|
this.reconnectTimer = setTimeout(() => {
|
|
13711
13923
|
this.reconnectTimer = null;
|
|
13712
13924
|
this.ensureSocket();
|
|
@@ -13767,7 +13979,9 @@ var _StandaloneWsTransport = class _StandaloneWsTransport2 extends BaseWsTranspo
|
|
|
13767
13979
|
this.wildcardRegistered = true;
|
|
13768
13980
|
}
|
|
13769
13981
|
};
|
|
13770
|
-
_StandaloneWsTransport.
|
|
13982
|
+
_StandaloneWsTransport.FAST_RETRY_LIMIT = 60;
|
|
13983
|
+
_StandaloneWsTransport.FAST_RETRY_DELAY_MS = 1e3;
|
|
13984
|
+
_StandaloneWsTransport.MAX_BACKOFF_MS = 3e4;
|
|
13771
13985
|
var StandaloneWsTransport = _StandaloneWsTransport;
|
|
13772
13986
|
function createSdkStore(initial) {
|
|
13773
13987
|
const store = createStore()(subscribeWithSelector(() => initial));
|
|
@@ -13788,14 +14002,16 @@ function createLobbyStore(transport2) {
|
|
|
13788
14002
|
depositStatusByLobbyId: {}
|
|
13789
14003
|
});
|
|
13790
14004
|
const setBaseState = (lobbies) => {
|
|
13791
|
-
|
|
13792
|
-
|
|
13793
|
-
|
|
13794
|
-
|
|
13795
|
-
|
|
13796
|
-
|
|
13797
|
-
|
|
13798
|
-
|
|
14005
|
+
store.updateState((state) => {
|
|
14006
|
+
const lobbiesById = { ...state.lobbiesById };
|
|
14007
|
+
for (const lobby of lobbies) {
|
|
14008
|
+
const existing = lobbiesById[lobby.id];
|
|
14009
|
+
if (!existing || lobby.updatedAt >= existing.updatedAt) {
|
|
14010
|
+
lobbiesById[lobby.id] = lobby;
|
|
14011
|
+
}
|
|
14012
|
+
}
|
|
14013
|
+
return { ...state, lobbiesById };
|
|
14014
|
+
});
|
|
13799
14015
|
};
|
|
13800
14016
|
const applyWsEvent = (event) => {
|
|
13801
14017
|
switch (event.event) {
|
|
@@ -13925,12 +14141,23 @@ function createGameStore(transport2) {
|
|
|
13925
14141
|
gamesById: {},
|
|
13926
14142
|
spectatorCounts: {}
|
|
13927
14143
|
});
|
|
14144
|
+
const STATUS_RANK = {
|
|
14145
|
+
active: 0,
|
|
14146
|
+
completed: 1,
|
|
14147
|
+
abandoned: 1
|
|
14148
|
+
};
|
|
13928
14149
|
const setBaseState = (games) => {
|
|
13929
|
-
|
|
13930
|
-
|
|
13931
|
-
|
|
13932
|
-
|
|
13933
|
-
|
|
14150
|
+
store.updateState((state) => {
|
|
14151
|
+
const gamesById = { ...state.gamesById };
|
|
14152
|
+
for (const game of games) {
|
|
14153
|
+
const existing = gamesById[game.gameId];
|
|
14154
|
+
if (existing && STATUS_RANK[existing.status] > STATUS_RANK[game.status]) {
|
|
14155
|
+
continue;
|
|
14156
|
+
}
|
|
14157
|
+
gamesById[game.gameId] = game;
|
|
14158
|
+
}
|
|
14159
|
+
return { ...state, gamesById };
|
|
14160
|
+
});
|
|
13934
14161
|
};
|
|
13935
14162
|
const applyWsEvent = (event) => {
|
|
13936
14163
|
switch (event.event) {
|
|
@@ -14668,7 +14895,38 @@ function createNotificationsStore() {
|
|
|
14668
14895
|
appNotifications: []
|
|
14669
14896
|
}));
|
|
14670
14897
|
};
|
|
14671
|
-
|
|
14898
|
+
const markAsRead = (id) => {
|
|
14899
|
+
store.updateState((state) => ({
|
|
14900
|
+
...state,
|
|
14901
|
+
appNotifications: state.appNotifications.map(
|
|
14902
|
+
(n) => n.id === id ? { ...n, read: true } : n
|
|
14903
|
+
)
|
|
14904
|
+
}));
|
|
14905
|
+
};
|
|
14906
|
+
const markAllAsRead = () => {
|
|
14907
|
+
store.updateState((state) => ({
|
|
14908
|
+
...state,
|
|
14909
|
+
appNotifications: state.appNotifications.map((n) => ({
|
|
14910
|
+
...n,
|
|
14911
|
+
read: true
|
|
14912
|
+
}))
|
|
14913
|
+
}));
|
|
14914
|
+
};
|
|
14915
|
+
const dismiss = (id) => {
|
|
14916
|
+
store.updateState((state) => ({
|
|
14917
|
+
...state,
|
|
14918
|
+
appNotifications: state.appNotifications.filter((n) => n.id !== id)
|
|
14919
|
+
}));
|
|
14920
|
+
};
|
|
14921
|
+
return {
|
|
14922
|
+
store,
|
|
14923
|
+
applyWsEvent,
|
|
14924
|
+
setListFromApi,
|
|
14925
|
+
clear,
|
|
14926
|
+
markAsRead,
|
|
14927
|
+
markAllAsRead,
|
|
14928
|
+
dismiss
|
|
14929
|
+
};
|
|
14672
14930
|
}
|
|
14673
14931
|
function createFriendsStore() {
|
|
14674
14932
|
const store = createSdkStore({
|
|
@@ -15015,6 +15273,7 @@ var SDK = class {
|
|
|
15015
15273
|
constructor(config) {
|
|
15016
15274
|
const baseUrl = config.baseUrl || "http://localhost:3000";
|
|
15017
15275
|
const logger2 = config.logger || logger;
|
|
15276
|
+
this.analytics = config.analytics ?? new NoopAnalyticsClient();
|
|
15018
15277
|
this.http = config.httpClient || new HttpClient(
|
|
15019
15278
|
baseUrl,
|
|
15020
15279
|
config.storage,
|
|
@@ -15047,14 +15306,18 @@ var SDK = class {
|
|
|
15047
15306
|
this.referrals = new Referrals(this.http, logger2);
|
|
15048
15307
|
this.reports = new Reports(this.http, logger2);
|
|
15049
15308
|
this.support = new Support(this.http, logger2);
|
|
15050
|
-
this.markets = new Markets(this.http, logger2);
|
|
15309
|
+
this.markets = new Markets(this.http, logger2, this.wallet);
|
|
15051
15310
|
this.wsTransport = config.wsTransport || new StandaloneWsTransport(baseUrl);
|
|
15052
15311
|
this.wsTransport.setAppId(config.appId);
|
|
15053
15312
|
this.lobbyStore = createLobbyStore(this.wsTransport);
|
|
15313
|
+
this.lobbies.setLobbyStore(this.lobbyStore);
|
|
15054
15314
|
this.gameStore = createGameStore(this.wsTransport);
|
|
15315
|
+
this.games.setGameStore(this.gameStore);
|
|
15055
15316
|
this.gameActionsStore = createGameActionsStore(this.wsTransport);
|
|
15317
|
+
this.games.setGameActionsStore(this.gameActionsStore);
|
|
15056
15318
|
this.chatStore = createChatStore(this.wsTransport);
|
|
15057
15319
|
this.dmThreadsStore = createDmThreadsStore();
|
|
15320
|
+
this.chat.setDmThreadsStore(this.dmThreadsStore);
|
|
15058
15321
|
this.notificationsStore = createNotificationsStore();
|
|
15059
15322
|
this.friendsStore = createFriendsStore();
|
|
15060
15323
|
this.notifications = new Notifications(
|
|
@@ -15084,6 +15347,29 @@ var SDK = class {
|
|
|
15084
15347
|
this.wsTransport.connect();
|
|
15085
15348
|
await this.wsTransport.waitUntilConnected(timeoutMs);
|
|
15086
15349
|
}
|
|
15350
|
+
/**
|
|
15351
|
+
* Handle the full deposit-to-queue lifecycle for a lobby.
|
|
15352
|
+
*
|
|
15353
|
+
* After the deposit is confirmed, the backend may not have processed the
|
|
15354
|
+
* `lobby.deposit.allConfirmed` event yet, so the lobby can still be in
|
|
15355
|
+
* 'preparing' status. This method re-fetches the lobby and explicitly
|
|
15356
|
+
* joins the queue if needed, then pushes the latest state to the store
|
|
15357
|
+
* so the UI transitions immediately.
|
|
15358
|
+
*/
|
|
15359
|
+
async depositAndJoinQueue(lobbyId) {
|
|
15360
|
+
const result = await this.escrow.depositForLobby(lobbyId);
|
|
15361
|
+
if (!result.canProceedToQueue) {
|
|
15362
|
+
const lobby2 = await this.lobbies.getLobby(lobbyId);
|
|
15363
|
+
this.lobbyStore.setBaseState([lobby2]);
|
|
15364
|
+
return { ...result, lobby: lobby2 };
|
|
15365
|
+
}
|
|
15366
|
+
let lobby = await this.lobbies.getLobby(lobbyId);
|
|
15367
|
+
if (lobby.status === "preparing") {
|
|
15368
|
+
lobby = await this.lobbies.joinQueue(lobbyId);
|
|
15369
|
+
}
|
|
15370
|
+
this.lobbyStore.setBaseState([lobby]);
|
|
15371
|
+
return { ...result, lobby };
|
|
15372
|
+
}
|
|
15087
15373
|
};
|
|
15088
15374
|
var import_utils = __toESM2(require_dist(), 1);
|
|
15089
15375
|
var export_MICRO_UNITS = import_utils.MICRO_UNITS;
|
|
@@ -15097,7 +15383,9 @@ import bs58 from "bs58";
|
|
|
15097
15383
|
var DimAgentClient = class {
|
|
15098
15384
|
sdk;
|
|
15099
15385
|
agentConfig;
|
|
15386
|
+
externalSignerMode;
|
|
15100
15387
|
keypair;
|
|
15388
|
+
_externalAddress = null;
|
|
15101
15389
|
config;
|
|
15102
15390
|
authenticated = false;
|
|
15103
15391
|
userId = null;
|
|
@@ -15111,21 +15399,26 @@ var DimAgentClient = class {
|
|
|
15111
15399
|
constructor(config) {
|
|
15112
15400
|
this.config = config;
|
|
15113
15401
|
this.agentConfig = config.agentConfig;
|
|
15114
|
-
|
|
15115
|
-
|
|
15402
|
+
this.externalSignerMode = !config.walletPrivateKey;
|
|
15403
|
+
if (config.walletPrivateKey) {
|
|
15404
|
+
const secretKeyBytes = bs58.decode(config.walletPrivateKey);
|
|
15405
|
+
this.keypair = Keypair.fromSecretKey(secretKeyBytes);
|
|
15406
|
+
} else {
|
|
15407
|
+
this.keypair = null;
|
|
15408
|
+
}
|
|
15116
15409
|
this.sdk = new SDK({
|
|
15117
15410
|
appId: "dim-agents",
|
|
15118
15411
|
baseUrl: config.apiUrl || "https://api.dim.cool",
|
|
15119
15412
|
storage: new NodeStorage(),
|
|
15120
15413
|
autoPay: {
|
|
15121
|
-
enabled:
|
|
15414
|
+
enabled: !this.externalSignerMode,
|
|
15122
15415
|
maxAmountMinor: 25e3,
|
|
15123
15416
|
maxRetries: 1
|
|
15124
15417
|
}
|
|
15125
15418
|
});
|
|
15126
15419
|
}
|
|
15127
15420
|
get walletAddress() {
|
|
15128
|
-
return this.keypair
|
|
15421
|
+
return this.keypair?.publicKey.toBase58() ?? this._externalAddress ?? "";
|
|
15129
15422
|
}
|
|
15130
15423
|
get isAuthenticated() {
|
|
15131
15424
|
return this.authenticated;
|
|
@@ -15134,17 +15427,23 @@ var DimAgentClient = class {
|
|
|
15134
15427
|
return this.userId;
|
|
15135
15428
|
}
|
|
15136
15429
|
async authenticate() {
|
|
15430
|
+
if (!this.keypair) {
|
|
15431
|
+
throw new Error(
|
|
15432
|
+
"No keypair configured. Use dim_request_auth_message + dim_complete_login for external signer mode."
|
|
15433
|
+
);
|
|
15434
|
+
}
|
|
15435
|
+
const keypair = this.keypair;
|
|
15137
15436
|
this.sdk.wallet.setSigner({
|
|
15138
15437
|
address: this.walletAddress,
|
|
15139
15438
|
signMessage: async (message) => {
|
|
15140
15439
|
const signature = import_tweetnacl.default.sign.detached(
|
|
15141
15440
|
new TextEncoder().encode(message),
|
|
15142
|
-
|
|
15441
|
+
keypair.secretKey
|
|
15143
15442
|
);
|
|
15144
15443
|
return Buffer.from(signature).toString("base64");
|
|
15145
15444
|
},
|
|
15146
15445
|
signTransaction: async (transaction) => {
|
|
15147
|
-
transaction.partialSign(
|
|
15446
|
+
transaction.partialSign(keypair);
|
|
15148
15447
|
return transaction;
|
|
15149
15448
|
}
|
|
15150
15449
|
});
|
|
@@ -15198,10 +15497,45 @@ var DimAgentClient = class {
|
|
|
15198
15497
|
get pendingEventCount() {
|
|
15199
15498
|
return this.eventQueue.length;
|
|
15200
15499
|
}
|
|
15201
|
-
/** Get the Keypair for transaction signing. */
|
|
15500
|
+
/** Get the Keypair for transaction signing (keypair mode only). */
|
|
15202
15501
|
getKeypair() {
|
|
15502
|
+
if (!this.keypair) {
|
|
15503
|
+
throw new Error(
|
|
15504
|
+
"No keypair configured \u2014 running in external signer mode."
|
|
15505
|
+
);
|
|
15506
|
+
}
|
|
15203
15507
|
return this.keypair;
|
|
15204
15508
|
}
|
|
15509
|
+
/** Request the auth handshake message for a given address (external signer mode). */
|
|
15510
|
+
async requestAuthMessage(address) {
|
|
15511
|
+
return this.sdk.auth.generateHandshake(address);
|
|
15512
|
+
}
|
|
15513
|
+
/**
|
|
15514
|
+
* Complete authentication using an externally provided signature (external signer mode).
|
|
15515
|
+
* @param address - Solana wallet address (base58)
|
|
15516
|
+
* @param signatureBase58 - Detached ed25519 signature in base58 (from sign_solana_message)
|
|
15517
|
+
*/
|
|
15518
|
+
async completeAuth(address, signatureBase58) {
|
|
15519
|
+
const signatureBytes = bs58.decode(signatureBase58);
|
|
15520
|
+
const signedMessage = Buffer.from(signatureBytes).toString("base64");
|
|
15521
|
+
const response = await this.sdk.auth.loginWithExternalSignature(
|
|
15522
|
+
address,
|
|
15523
|
+
signedMessage,
|
|
15524
|
+
{
|
|
15525
|
+
referralCode: this.config.referralCode,
|
|
15526
|
+
walletMeta: { type: "phantom-embedded", provider: "injected" }
|
|
15527
|
+
}
|
|
15528
|
+
);
|
|
15529
|
+
this.sdk.wsTransport.setAccessToken(response.access_token);
|
|
15530
|
+
this._externalAddress = address;
|
|
15531
|
+
this.authenticated = true;
|
|
15532
|
+
this.userId = response.user.id;
|
|
15533
|
+
return {
|
|
15534
|
+
userId: response.user.id,
|
|
15535
|
+
username: response.user.username,
|
|
15536
|
+
accessToken: response.access_token
|
|
15537
|
+
};
|
|
15538
|
+
}
|
|
15205
15539
|
// ── Game chat cursors ────────────────────────────────────────────────
|
|
15206
15540
|
/** Get the last-seen chat timestamp for a game. */
|
|
15207
15541
|
getGameChatCursor(gameId) {
|
|
@@ -15329,6 +15663,77 @@ async function login(client) {
|
|
|
15329
15663
|
};
|
|
15330
15664
|
}
|
|
15331
15665
|
}
|
|
15666
|
+
async function requestAuthMessage(client, args) {
|
|
15667
|
+
try {
|
|
15668
|
+
const { message } = await client.requestAuthMessage(args.address);
|
|
15669
|
+
return {
|
|
15670
|
+
data: {
|
|
15671
|
+
message,
|
|
15672
|
+
address: args.address,
|
|
15673
|
+
nextStep: 'Sign this message with sign_solana_message (networkId: "solana:mainnet"), then call dim_complete_login with the address and the base58 signature.'
|
|
15674
|
+
}
|
|
15675
|
+
};
|
|
15676
|
+
} catch (error) {
|
|
15677
|
+
return {
|
|
15678
|
+
error: `Failed to get auth message: ${error instanceof Error ? error.message : String(error)}`,
|
|
15679
|
+
isError: true
|
|
15680
|
+
};
|
|
15681
|
+
}
|
|
15682
|
+
}
|
|
15683
|
+
async function completeLogin(client, args) {
|
|
15684
|
+
try {
|
|
15685
|
+
const result = await client.completeAuth(args.address, args.signature);
|
|
15686
|
+
client.startEventListeners();
|
|
15687
|
+
const nextSteps = [];
|
|
15688
|
+
if (result.username == null || result.username === "") {
|
|
15689
|
+
nextSteps.push("No username set \u2014 call dim_set_username to claim one");
|
|
15690
|
+
}
|
|
15691
|
+
nextSteps.push("Check your balance with dim_get_balance");
|
|
15692
|
+
nextSteps.push("Explore available games with dim_list_games");
|
|
15693
|
+
try {
|
|
15694
|
+
const summary = await client.sdk.referrals.getSummary();
|
|
15695
|
+
if (!summary.hasReferrer) {
|
|
15696
|
+
nextSteps.push(
|
|
15697
|
+
"No referrer yet \u2014 call dim_apply_referral_code for 10% fee discount"
|
|
15698
|
+
);
|
|
15699
|
+
}
|
|
15700
|
+
if (result.username) {
|
|
15701
|
+
nextSteps.push(
|
|
15702
|
+
`Share your referral code "${result.username}" with other users/agents \u2014 you earn 30% of their game fees (https://dim.cool/?ref=${result.username})`
|
|
15703
|
+
);
|
|
15704
|
+
}
|
|
15705
|
+
} catch {
|
|
15706
|
+
}
|
|
15707
|
+
const response = {
|
|
15708
|
+
success: true,
|
|
15709
|
+
userId: result.userId,
|
|
15710
|
+
username: result.username ?? null,
|
|
15711
|
+
walletAddress: client.walletAddress,
|
|
15712
|
+
walletNote: "DIM is noncustodial. Signing is handled by your external wallet for this session.",
|
|
15713
|
+
safetyRules: SAFETY_RULES,
|
|
15714
|
+
nextSteps
|
|
15715
|
+
};
|
|
15716
|
+
const ac = client.agentConfig;
|
|
15717
|
+
const dailyLimit = ac?.dailySpendLimit ?? 20;
|
|
15718
|
+
response.agentConfig = {
|
|
15719
|
+
autoAcceptFriendRequests: ac?.autoAcceptFriendRequests ?? false,
|
|
15720
|
+
autoReplyDms: ac?.autoReplyDms ?? false,
|
|
15721
|
+
autoPlayGames: ac?.autoPlayGames ?? false,
|
|
15722
|
+
maxBetPerGame: ac?.maxBetPerGame ?? 1,
|
|
15723
|
+
dailySpendLimit: dailyLimit,
|
|
15724
|
+
autoJoinGlobalChat: ac?.autoJoinGlobalChat ?? false,
|
|
15725
|
+
autoPromoteReferrals: ac?.autoPromoteReferrals ?? false,
|
|
15726
|
+
dailySpentSoFar: client.dailySpentDollars,
|
|
15727
|
+
dailyRemaining: dailyLimit - client.dailySpentDollars
|
|
15728
|
+
};
|
|
15729
|
+
return { data: response };
|
|
15730
|
+
} catch (error) {
|
|
15731
|
+
return {
|
|
15732
|
+
error: `Login failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
15733
|
+
isError: true
|
|
15734
|
+
};
|
|
15735
|
+
}
|
|
15736
|
+
}
|
|
15332
15737
|
async function getProfile(client) {
|
|
15333
15738
|
try {
|
|
15334
15739
|
if (!client.currentUserId) {
|
|
@@ -15457,10 +15862,19 @@ async function listFriends(client, args) {
|
|
|
15457
15862
|
};
|
|
15458
15863
|
}
|
|
15459
15864
|
}
|
|
15460
|
-
async function getIncomingFriendRequests(client) {
|
|
15865
|
+
async function getIncomingFriendRequests(client, args) {
|
|
15461
15866
|
try {
|
|
15462
|
-
const result = await client.sdk.users.getIncomingFriendRequests(
|
|
15463
|
-
|
|
15867
|
+
const result = await client.sdk.users.getIncomingFriendRequests({
|
|
15868
|
+
limit: args?.limit,
|
|
15869
|
+
cursor: args?.cursor
|
|
15870
|
+
});
|
|
15871
|
+
return {
|
|
15872
|
+
data: {
|
|
15873
|
+
items: result.items,
|
|
15874
|
+
nextCursor: result.nextCursor,
|
|
15875
|
+
hasMore: !!result.nextCursor
|
|
15876
|
+
}
|
|
15877
|
+
};
|
|
15464
15878
|
} catch (error) {
|
|
15465
15879
|
return {
|
|
15466
15880
|
error: `Failed to get requests: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -15581,6 +15995,42 @@ async function sendUsdc(client, args) {
|
|
|
15581
15995
|
const spendErr = client.checkSpendLimit(args.amount);
|
|
15582
15996
|
if (spendErr) return { error: spendErr, isError: true };
|
|
15583
15997
|
const amountMinor = Math.round(args.amount * 1e6);
|
|
15998
|
+
if (client.externalSignerMode) {
|
|
15999
|
+
const senderAddress = client.walletAddress;
|
|
16000
|
+
if (!senderAddress) {
|
|
16001
|
+
return {
|
|
16002
|
+
error: "Wallet address not set. Call dim_complete_login first.",
|
|
16003
|
+
isError: true
|
|
16004
|
+
};
|
|
16005
|
+
}
|
|
16006
|
+
const prepared = await client.sdk.wallet.prepareTransfer(
|
|
16007
|
+
senderAddress,
|
|
16008
|
+
args.recipient,
|
|
16009
|
+
amountMinor
|
|
16010
|
+
);
|
|
16011
|
+
return {
|
|
16012
|
+
data: {
|
|
16013
|
+
needsSigning: true,
|
|
16014
|
+
unsignedTx: prepared.transaction,
|
|
16015
|
+
fee: prepared.fee,
|
|
16016
|
+
totalAmount: prepared.totalAmount,
|
|
16017
|
+
recipientAddress: prepared.recipientAddress,
|
|
16018
|
+
ataCreated: prepared.ataCreated ?? false,
|
|
16019
|
+
confirmWith: {
|
|
16020
|
+
tool: "dim_confirm_send_usdc",
|
|
16021
|
+
params: {
|
|
16022
|
+
recipientAddress: prepared.recipientAddress,
|
|
16023
|
+
amount: amountMinor,
|
|
16024
|
+
fee: prepared.fee,
|
|
16025
|
+
token: "USDC",
|
|
16026
|
+
ataCreated: prepared.ataCreated ?? false,
|
|
16027
|
+
recipientInput: args.recipient
|
|
16028
|
+
}
|
|
16029
|
+
},
|
|
16030
|
+
hint: "Sign and broadcast unsignedTx with send_solana_transaction, then call dim_confirm_send_usdc with the signature and the confirmWith.params."
|
|
16031
|
+
}
|
|
16032
|
+
};
|
|
16033
|
+
}
|
|
15584
16034
|
const result = await client.sdk.wallet.send(args.recipient, amountMinor);
|
|
15585
16035
|
client.recordSpend(amountMinor);
|
|
15586
16036
|
return {
|
|
@@ -15601,11 +16051,74 @@ async function sendUsdc(client, args) {
|
|
|
15601
16051
|
};
|
|
15602
16052
|
}
|
|
15603
16053
|
}
|
|
16054
|
+
async function confirmSendUsdc(client, args) {
|
|
16055
|
+
try {
|
|
16056
|
+
const senderAddress = client.walletAddress;
|
|
16057
|
+
const result = await client.sdk.wallet.confirmTransferSignature(
|
|
16058
|
+
args.signature,
|
|
16059
|
+
senderAddress,
|
|
16060
|
+
args.recipientAddress,
|
|
16061
|
+
args.amount,
|
|
16062
|
+
args.token || "USDC",
|
|
16063
|
+
args.ataCreated,
|
|
16064
|
+
args.recipientInput
|
|
16065
|
+
);
|
|
16066
|
+
client.recordSpend(args.amount);
|
|
16067
|
+
return {
|
|
16068
|
+
data: {
|
|
16069
|
+
success: true,
|
|
16070
|
+
signature: result.signature,
|
|
16071
|
+
status: result.status,
|
|
16072
|
+
recipientAddress: args.recipientAddress
|
|
16073
|
+
}
|
|
16074
|
+
};
|
|
16075
|
+
} catch (error) {
|
|
16076
|
+
return {
|
|
16077
|
+
error: `Failed to confirm USDC transfer: ${error instanceof Error ? error.message : String(error)}`,
|
|
16078
|
+
isError: true
|
|
16079
|
+
};
|
|
16080
|
+
}
|
|
16081
|
+
}
|
|
15604
16082
|
async function tipUser(client, args) {
|
|
15605
16083
|
try {
|
|
15606
16084
|
const spendErr = client.checkSpendLimit(args.amount);
|
|
15607
16085
|
if (spendErr) return { error: spendErr, isError: true };
|
|
15608
16086
|
const amountMinor = Math.round(args.amount * 1e6);
|
|
16087
|
+
if (client.externalSignerMode) {
|
|
16088
|
+
const senderAddress = client.walletAddress;
|
|
16089
|
+
if (!senderAddress) {
|
|
16090
|
+
return {
|
|
16091
|
+
error: "Wallet address not set. Call dim_complete_login first.",
|
|
16092
|
+
isError: true
|
|
16093
|
+
};
|
|
16094
|
+
}
|
|
16095
|
+
const prepared = await client.sdk.tips.prepare({
|
|
16096
|
+
recipientUsername: args.recipientUsername,
|
|
16097
|
+
amount: amountMinor,
|
|
16098
|
+
supportsPresign: false
|
|
16099
|
+
});
|
|
16100
|
+
return {
|
|
16101
|
+
data: {
|
|
16102
|
+
needsSigning: true,
|
|
16103
|
+
unsignedTx: prepared.transaction,
|
|
16104
|
+
fee: prepared.fee,
|
|
16105
|
+
totalAmount: prepared.totalAmount,
|
|
16106
|
+
recipientAddress: prepared.recipientAddress,
|
|
16107
|
+
confirmWith: {
|
|
16108
|
+
tool: "dim_confirm_tip_user",
|
|
16109
|
+
params: {
|
|
16110
|
+
recipientAddress: prepared.recipientAddress,
|
|
16111
|
+
recipientUserId: prepared.recipientUserId,
|
|
16112
|
+
recipientUsername: prepared.recipientUsername,
|
|
16113
|
+
amount: prepared.amount,
|
|
16114
|
+
fee: prepared.fee,
|
|
16115
|
+
ataCreated: false
|
|
16116
|
+
}
|
|
16117
|
+
},
|
|
16118
|
+
hint: "Sign and broadcast unsignedTx with send_solana_transaction, then call dim_confirm_tip_user with the signature and the confirmWith.params."
|
|
16119
|
+
}
|
|
16120
|
+
};
|
|
16121
|
+
}
|
|
15609
16122
|
const result = await client.sdk.tips.send(
|
|
15610
16123
|
args.recipientUsername,
|
|
15611
16124
|
amountMinor
|
|
@@ -15628,6 +16141,38 @@ async function tipUser(client, args) {
|
|
|
15628
16141
|
};
|
|
15629
16142
|
}
|
|
15630
16143
|
}
|
|
16144
|
+
async function confirmTipUser(client, args) {
|
|
16145
|
+
try {
|
|
16146
|
+
const senderAddress = client.walletAddress;
|
|
16147
|
+
await client.sdk.wallet.confirmTransferSignature(
|
|
16148
|
+
args.signature,
|
|
16149
|
+
senderAddress,
|
|
16150
|
+
args.recipientAddress,
|
|
16151
|
+
args.amount,
|
|
16152
|
+
"USDC",
|
|
16153
|
+
args.ataCreated ?? false,
|
|
16154
|
+
args.recipientUsername
|
|
16155
|
+
);
|
|
16156
|
+
await client.sdk.tips.broadcast({
|
|
16157
|
+
recipientUserId: args.recipientUserId,
|
|
16158
|
+
amount: args.amount
|
|
16159
|
+
});
|
|
16160
|
+
client.recordSpend(args.amount);
|
|
16161
|
+
return {
|
|
16162
|
+
data: {
|
|
16163
|
+
success: true,
|
|
16164
|
+
signature: args.signature,
|
|
16165
|
+
recipient: args.recipientUsername,
|
|
16166
|
+
broadcastedToGlobalChat: true
|
|
16167
|
+
}
|
|
16168
|
+
};
|
|
16169
|
+
} catch (error) {
|
|
16170
|
+
return {
|
|
16171
|
+
error: `Failed to confirm tip: ${error instanceof Error ? error.message : String(error)}`,
|
|
16172
|
+
isError: true
|
|
16173
|
+
};
|
|
16174
|
+
}
|
|
16175
|
+
}
|
|
15631
16176
|
async function getWalletActivity(client, args) {
|
|
15632
16177
|
try {
|
|
15633
16178
|
const activity = await client.sdk.wallet.getActivity({
|
|
@@ -15734,6 +16279,7 @@ function buildRpsContext(state, agentUserId) {
|
|
|
15734
16279
|
roundHistory: state.roundHistory,
|
|
15735
16280
|
phase: roundState?.phase,
|
|
15736
16281
|
timeRemaining: roundState?.timeRemaining,
|
|
16282
|
+
...state.bufferEndsAt != null && { bufferEndsAt: state.bufferEndsAt },
|
|
15737
16283
|
moveFormat: 'dim_submit_action: gameType="rock-paper-scissors", action="play", payload={ action: "rock"|"paper"|"scissors" }'
|
|
15738
16284
|
};
|
|
15739
16285
|
}
|
|
@@ -15746,6 +16292,7 @@ function buildChessContext(state, agentUserId) {
|
|
|
15746
16292
|
yourColor,
|
|
15747
16293
|
whiteTimeMs: state.whiteTimeMs,
|
|
15748
16294
|
blackTimeMs: state.blackTimeMs,
|
|
16295
|
+
...state.bufferEndsAt != null && { bufferEndsAt: state.bufferEndsAt },
|
|
15749
16296
|
moveFormat: 'dim_submit_action: gameType="chess", action="move", payload={ from: "e2", to: "e4" }'
|
|
15750
16297
|
};
|
|
15751
16298
|
}
|
|
@@ -15955,6 +16502,23 @@ async function createLobby(client, args) {
|
|
|
15955
16502
|
}
|
|
15956
16503
|
async function depositForLobby(client, args) {
|
|
15957
16504
|
try {
|
|
16505
|
+
if (client.externalSignerMode) {
|
|
16506
|
+
const { transaction } = await client.sdk.escrow.prepareAndStartDeposit(
|
|
16507
|
+
args.lobbyId,
|
|
16508
|
+
false
|
|
16509
|
+
);
|
|
16510
|
+
return {
|
|
16511
|
+
data: {
|
|
16512
|
+
needsSigning: true,
|
|
16513
|
+
unsignedTx: transaction,
|
|
16514
|
+
confirmWith: {
|
|
16515
|
+
tool: "dim_confirm_lobby_deposit",
|
|
16516
|
+
params: { lobbyId: args.lobbyId }
|
|
16517
|
+
},
|
|
16518
|
+
hint: "Sign and broadcast unsignedTx with send_solana_transaction, then call dim_confirm_lobby_deposit with the lobbyId and signature."
|
|
16519
|
+
}
|
|
16520
|
+
};
|
|
16521
|
+
}
|
|
15958
16522
|
const result = await client.sdk.escrow.depositForLobbySync(args.lobbyId);
|
|
15959
16523
|
return {
|
|
15960
16524
|
data: {
|
|
@@ -15971,6 +16535,44 @@ async function depositForLobby(client, args) {
|
|
|
15971
16535
|
};
|
|
15972
16536
|
}
|
|
15973
16537
|
}
|
|
16538
|
+
async function confirmLobbyDeposit(client, args) {
|
|
16539
|
+
try {
|
|
16540
|
+
await client.sdk.escrow.confirmDepositSignature(
|
|
16541
|
+
args.lobbyId,
|
|
16542
|
+
args.signature
|
|
16543
|
+
);
|
|
16544
|
+
const MAX_WAIT_MS = 6e4;
|
|
16545
|
+
const POLL_INTERVAL_MS = 1e3;
|
|
16546
|
+
const startTime = Date.now();
|
|
16547
|
+
while (Date.now() - startTime < MAX_WAIT_MS) {
|
|
16548
|
+
const status = await client.sdk.escrow.getDepositStatus(args.lobbyId);
|
|
16549
|
+
if (status.canProceedToQueue) {
|
|
16550
|
+
return {
|
|
16551
|
+
data: {
|
|
16552
|
+
signature: args.signature,
|
|
16553
|
+
status: "confirmed",
|
|
16554
|
+
canProceedToQueue: true,
|
|
16555
|
+
hint: "Deposit confirmed. Call dim_join_queue to enter matchmaking."
|
|
16556
|
+
}
|
|
16557
|
+
};
|
|
16558
|
+
}
|
|
16559
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
16560
|
+
}
|
|
16561
|
+
return {
|
|
16562
|
+
data: {
|
|
16563
|
+
signature: args.signature,
|
|
16564
|
+
status: "pending",
|
|
16565
|
+
canProceedToQueue: false,
|
|
16566
|
+
hint: "Deposit submitted but not yet confirmed on-chain. Try dim_confirm_lobby_deposit again in a few seconds."
|
|
16567
|
+
}
|
|
16568
|
+
};
|
|
16569
|
+
} catch (error) {
|
|
16570
|
+
return {
|
|
16571
|
+
error: `Failed to confirm deposit: ${error instanceof Error ? error.message : String(error)}`,
|
|
16572
|
+
isError: true
|
|
16573
|
+
};
|
|
16574
|
+
}
|
|
16575
|
+
}
|
|
15974
16576
|
async function leaveLobby(client, args) {
|
|
15975
16577
|
try {
|
|
15976
16578
|
const result = await client.sdk.lobbies.leaveLobby(args.lobbyId);
|
|
@@ -16040,7 +16642,7 @@ async function joinQueue(client, args) {
|
|
|
16040
16642
|
gameId,
|
|
16041
16643
|
matched: !!matched,
|
|
16042
16644
|
...spectateUrl && { spectateUrl },
|
|
16043
|
-
hint: matched ? `Game
|
|
16645
|
+
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
16646
|
}
|
|
16045
16647
|
};
|
|
16046
16648
|
} catch (error) {
|
|
@@ -16115,6 +16717,23 @@ async function donateToPot(client, args) {
|
|
|
16115
16717
|
const spendErr = client.checkSpendLimit(args.amount);
|
|
16116
16718
|
if (spendErr) return { error: spendErr, isError: true };
|
|
16117
16719
|
const amountMinor = Math.round(args.amount * 1e6);
|
|
16720
|
+
if (client.externalSignerMode) {
|
|
16721
|
+
const prepared = await client.sdk.games.prepareGameDonation(
|
|
16722
|
+
args.gameId,
|
|
16723
|
+
amountMinor
|
|
16724
|
+
);
|
|
16725
|
+
return {
|
|
16726
|
+
data: {
|
|
16727
|
+
needsSigning: true,
|
|
16728
|
+
unsignedTx: prepared.transaction,
|
|
16729
|
+
confirmWith: {
|
|
16730
|
+
tool: "dim_confirm_donate_to_pot",
|
|
16731
|
+
params: { gameId: args.gameId, amount: amountMinor }
|
|
16732
|
+
},
|
|
16733
|
+
hint: "Sign and broadcast unsignedTx with send_solana_transaction, then call dim_confirm_donate_to_pot with the signature."
|
|
16734
|
+
}
|
|
16735
|
+
};
|
|
16736
|
+
}
|
|
16118
16737
|
const result = await client.sdk.games.sendDonation(
|
|
16119
16738
|
args.gameId,
|
|
16120
16739
|
amountMinor
|
|
@@ -16138,6 +16757,29 @@ async function donateToPot(client, args) {
|
|
|
16138
16757
|
};
|
|
16139
16758
|
}
|
|
16140
16759
|
}
|
|
16760
|
+
async function confirmDonateToPot(client, args) {
|
|
16761
|
+
try {
|
|
16762
|
+
const result = await client.sdk.games.confirmGameDonationSignature(
|
|
16763
|
+
args.gameId,
|
|
16764
|
+
args.amount,
|
|
16765
|
+
args.signature
|
|
16766
|
+
);
|
|
16767
|
+
client.recordSpend(args.amount);
|
|
16768
|
+
return {
|
|
16769
|
+
data: {
|
|
16770
|
+
success: true,
|
|
16771
|
+
gameId: args.gameId,
|
|
16772
|
+
signature: result.signature,
|
|
16773
|
+
status: result.status
|
|
16774
|
+
}
|
|
16775
|
+
};
|
|
16776
|
+
} catch (error) {
|
|
16777
|
+
return {
|
|
16778
|
+
error: `Failed to confirm donation: ${error instanceof Error ? error.message : String(error)}`,
|
|
16779
|
+
isError: true
|
|
16780
|
+
};
|
|
16781
|
+
}
|
|
16782
|
+
}
|
|
16141
16783
|
async function getGame(client, args) {
|
|
16142
16784
|
try {
|
|
16143
16785
|
const game = await client.sdk.games.getGame(args.gameId);
|
|
@@ -16149,6 +16791,12 @@ async function getGame(client, args) {
|
|
|
16149
16791
|
};
|
|
16150
16792
|
}
|
|
16151
16793
|
}
|
|
16794
|
+
function isInStartingPhase(state) {
|
|
16795
|
+
const roundState = state.roundState;
|
|
16796
|
+
if (roundState?.phase === "starting") return true;
|
|
16797
|
+
if (state.status === "active" && state.currentPlayerId == null) return true;
|
|
16798
|
+
return false;
|
|
16799
|
+
}
|
|
16152
16800
|
async function gameLoop(client, args) {
|
|
16153
16801
|
try {
|
|
16154
16802
|
const { gameId } = args;
|
|
@@ -16166,7 +16814,7 @@ async function gameLoop(client, args) {
|
|
|
16166
16814
|
if (state?.status === "completed") {
|
|
16167
16815
|
return buildGameLoopReturn(client, gameId, state, "completed");
|
|
16168
16816
|
}
|
|
16169
|
-
if (state?.currentPlayerId === client.currentUserId) {
|
|
16817
|
+
if (state?.currentPlayerId === client.currentUserId && !isInStartingPhase(state)) {
|
|
16170
16818
|
return buildGameLoopReturn(client, gameId, state, "your-turn");
|
|
16171
16819
|
}
|
|
16172
16820
|
await raceTimeout(
|
|
@@ -16792,10 +17440,10 @@ async function checkNotifications(client) {
|
|
|
16792
17440
|
isError: true
|
|
16793
17441
|
};
|
|
16794
17442
|
}
|
|
16795
|
-
const [notifications, dmThreads,
|
|
17443
|
+
const [notifications, dmThreads, friendRequestsPage] = await Promise.all([
|
|
16796
17444
|
client.sdk.notifications.list({ page: 1, limit: 20 }),
|
|
16797
17445
|
client.sdk.chat.listDmThreads(),
|
|
16798
|
-
client.sdk.users.getIncomingFriendRequests()
|
|
17446
|
+
client.sdk.users.getIncomingFriendRequests({ limit: 20 })
|
|
16799
17447
|
]);
|
|
16800
17448
|
const unreadDms = dmThreads.filter(
|
|
16801
17449
|
(t) => (t.unreadCount ?? 0) > 0
|
|
@@ -16807,9 +17455,10 @@ async function checkNotifications(client) {
|
|
|
16807
17455
|
unreadNotificationCount: notifications.unreadCount,
|
|
16808
17456
|
notifications: notifications.notifications.filter((n) => !n.read),
|
|
16809
17457
|
unreadDmThreads: unreadDms,
|
|
16810
|
-
incomingFriendRequests:
|
|
17458
|
+
incomingFriendRequests: friendRequestsPage.items,
|
|
17459
|
+
hasMoreIncomingFriendRequests: !!friendRequestsPage.nextCursor,
|
|
16811
17460
|
pendingWsEvents: client.pendingEventCount,
|
|
16812
|
-
hint: "Use dim_get_pending_events to drain buffered real-time events."
|
|
17461
|
+
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
17462
|
}
|
|
16814
17463
|
};
|
|
16815
17464
|
} catch (error) {
|
|
@@ -17225,6 +17874,36 @@ async function getMyStats(client) {
|
|
|
17225
17874
|
|
|
17226
17875
|
// ../dim-agent-core/src/tools/index.ts
|
|
17227
17876
|
var TOOL_DEFINITIONS = [
|
|
17877
|
+
// ── External signer mode (auth) ──────────────────────────────────────
|
|
17878
|
+
{
|
|
17879
|
+
name: "dim_request_auth_message",
|
|
17880
|
+
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.",
|
|
17881
|
+
params: {
|
|
17882
|
+
address: {
|
|
17883
|
+
type: "string",
|
|
17884
|
+
description: "Your Solana wallet address (base58)",
|
|
17885
|
+
required: true
|
|
17886
|
+
}
|
|
17887
|
+
},
|
|
17888
|
+
execute: (c, a) => requestAuthMessage(c, a)
|
|
17889
|
+
},
|
|
17890
|
+
{
|
|
17891
|
+
name: "dim_complete_login",
|
|
17892
|
+
description: "External wallet login step 2: provide the wallet address and base58 signature from sign_solana_message to complete authentication with DIM.",
|
|
17893
|
+
params: {
|
|
17894
|
+
address: {
|
|
17895
|
+
type: "string",
|
|
17896
|
+
description: "Your Solana wallet address (base58)",
|
|
17897
|
+
required: true
|
|
17898
|
+
},
|
|
17899
|
+
signature: {
|
|
17900
|
+
type: "string",
|
|
17901
|
+
description: "Base58-encoded signature returned by sign_solana_message",
|
|
17902
|
+
required: true
|
|
17903
|
+
}
|
|
17904
|
+
},
|
|
17905
|
+
execute: (c, a) => completeLogin(c, a)
|
|
17906
|
+
},
|
|
17228
17907
|
// ── Auth ─────────────────────────────────────────────────────────────
|
|
17229
17908
|
{
|
|
17230
17909
|
name: "dim_login",
|
|
@@ -17378,9 +18057,23 @@ var TOOL_DEFINITIONS = [
|
|
|
17378
18057
|
},
|
|
17379
18058
|
{
|
|
17380
18059
|
name: "dim_get_incoming_friend_requests",
|
|
17381
|
-
description: "List pending incoming friend requests.",
|
|
17382
|
-
params: {
|
|
17383
|
-
|
|
18060
|
+
description: "List pending incoming friend requests. Returns { items, nextCursor, hasMore }. Pass cursor from a previous response to page through results.",
|
|
18061
|
+
params: {
|
|
18062
|
+
limit: {
|
|
18063
|
+
type: "number",
|
|
18064
|
+
description: "Maximum number of requests to return (1\u2013100, default 50).",
|
|
18065
|
+
required: false
|
|
18066
|
+
},
|
|
18067
|
+
cursor: {
|
|
18068
|
+
type: "string",
|
|
18069
|
+
description: "Pagination cursor from a previous response nextCursor field. Omit for the first page.",
|
|
18070
|
+
required: false
|
|
18071
|
+
}
|
|
18072
|
+
},
|
|
18073
|
+
execute: (c, a) => getIncomingFriendRequests(
|
|
18074
|
+
c,
|
|
18075
|
+
a
|
|
18076
|
+
)
|
|
17384
18077
|
},
|
|
17385
18078
|
// ── Chat ─────────────────────────────────────────────────────────────
|
|
17386
18079
|
{
|
|
@@ -17506,6 +18199,90 @@ var TOOL_DEFINITIONS = [
|
|
|
17506
18199
|
},
|
|
17507
18200
|
execute: (c, a) => tipUser(c, a)
|
|
17508
18201
|
},
|
|
18202
|
+
{
|
|
18203
|
+
name: "dim_confirm_send_usdc",
|
|
18204
|
+
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.",
|
|
18205
|
+
params: {
|
|
18206
|
+
signature: {
|
|
18207
|
+
type: "string",
|
|
18208
|
+
description: "On-chain transaction signature returned by send_solana_transaction",
|
|
18209
|
+
required: true
|
|
18210
|
+
},
|
|
18211
|
+
recipientAddress: {
|
|
18212
|
+
type: "string",
|
|
18213
|
+
description: "Recipient Solana address (from confirmWith.params)",
|
|
18214
|
+
required: true
|
|
18215
|
+
},
|
|
18216
|
+
amount: {
|
|
18217
|
+
type: "number",
|
|
18218
|
+
description: "Amount in USDC minor units (from confirmWith.params)",
|
|
18219
|
+
required: true
|
|
18220
|
+
},
|
|
18221
|
+
fee: {
|
|
18222
|
+
type: "number",
|
|
18223
|
+
description: "Fee in minor units (from confirmWith.params)"
|
|
18224
|
+
},
|
|
18225
|
+
token: {
|
|
18226
|
+
type: "string",
|
|
18227
|
+
description: "Token type: USDC or SOL (default: USDC)"
|
|
18228
|
+
},
|
|
18229
|
+
ataCreated: {
|
|
18230
|
+
type: "string",
|
|
18231
|
+
description: "Whether a new ATA was created (from confirmWith.params)"
|
|
18232
|
+
},
|
|
18233
|
+
recipientInput: {
|
|
18234
|
+
type: "string",
|
|
18235
|
+
description: "Original recipient input (from confirmWith.params)"
|
|
18236
|
+
}
|
|
18237
|
+
},
|
|
18238
|
+
execute: (c, a) => confirmSendUsdc(
|
|
18239
|
+
c,
|
|
18240
|
+
a
|
|
18241
|
+
)
|
|
18242
|
+
},
|
|
18243
|
+
{
|
|
18244
|
+
name: "dim_confirm_tip_user",
|
|
18245
|
+
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.",
|
|
18246
|
+
params: {
|
|
18247
|
+
signature: {
|
|
18248
|
+
type: "string",
|
|
18249
|
+
description: "On-chain transaction signature returned by send_solana_transaction",
|
|
18250
|
+
required: true
|
|
18251
|
+
},
|
|
18252
|
+
recipientAddress: {
|
|
18253
|
+
type: "string",
|
|
18254
|
+
description: "Recipient Solana address (from confirmWith.params)",
|
|
18255
|
+
required: true
|
|
18256
|
+
},
|
|
18257
|
+
recipientUserId: {
|
|
18258
|
+
type: "string",
|
|
18259
|
+
description: "Recipient user ID (from confirmWith.params)",
|
|
18260
|
+
required: true
|
|
18261
|
+
},
|
|
18262
|
+
recipientUsername: {
|
|
18263
|
+
type: "string",
|
|
18264
|
+
description: "Recipient username (from confirmWith.params)",
|
|
18265
|
+
required: true
|
|
18266
|
+
},
|
|
18267
|
+
amount: {
|
|
18268
|
+
type: "number",
|
|
18269
|
+
description: "Amount in USDC minor units (from confirmWith.params)",
|
|
18270
|
+
required: true
|
|
18271
|
+
},
|
|
18272
|
+
fee: {
|
|
18273
|
+
type: "number",
|
|
18274
|
+
description: "Fee in minor units (from confirmWith.params)"
|
|
18275
|
+
},
|
|
18276
|
+
ataCreated: {
|
|
18277
|
+
type: "string",
|
|
18278
|
+
description: "Whether a new ATA was created (from confirmWith.params)"
|
|
18279
|
+
}
|
|
18280
|
+
},
|
|
18281
|
+
execute: (c, a) => confirmTipUser(
|
|
18282
|
+
c,
|
|
18283
|
+
a
|
|
18284
|
+
)
|
|
18285
|
+
},
|
|
17509
18286
|
{
|
|
17510
18287
|
name: "dim_get_wallet_activity",
|
|
17511
18288
|
description: "Get recent wallet transaction activity (deposits, payouts, transfers, refunds) and your DIM wallet address. Highlights any claimable items.",
|
|
@@ -17602,6 +18379,23 @@ var TOOL_DEFINITIONS = [
|
|
|
17602
18379
|
},
|
|
17603
18380
|
execute: (c, a) => depositForLobby(c, a)
|
|
17604
18381
|
},
|
|
18382
|
+
{
|
|
18383
|
+
name: "dim_confirm_lobby_deposit",
|
|
18384
|
+
description: "External wallet: confirm a lobby deposit after signing and broadcasting the transaction. Polls until the deposit is confirmed on-chain, then returns canProceedToQueue.",
|
|
18385
|
+
params: {
|
|
18386
|
+
lobbyId: {
|
|
18387
|
+
type: "string",
|
|
18388
|
+
description: "The lobby ID (from confirmWith.params)",
|
|
18389
|
+
required: true
|
|
18390
|
+
},
|
|
18391
|
+
signature: {
|
|
18392
|
+
type: "string",
|
|
18393
|
+
description: "On-chain transaction signature returned by send_solana_transaction",
|
|
18394
|
+
required: true
|
|
18395
|
+
}
|
|
18396
|
+
},
|
|
18397
|
+
execute: (c, a) => confirmLobbyDeposit(c, a)
|
|
18398
|
+
},
|
|
17605
18399
|
{
|
|
17606
18400
|
name: "dim_leave_lobby",
|
|
17607
18401
|
description: "Leave a lobby. Use this to exit a lobby you created or joined. Returns when you have left.",
|
|
@@ -17694,6 +18488,31 @@ var TOOL_DEFINITIONS = [
|
|
|
17694
18488
|
},
|
|
17695
18489
|
execute: (c, a) => gameLoop(c, a)
|
|
17696
18490
|
},
|
|
18491
|
+
{
|
|
18492
|
+
name: "dim_confirm_donate_to_pot",
|
|
18493
|
+
description: "External wallet: confirm a game pot donation after signing and broadcasting the transaction.",
|
|
18494
|
+
params: {
|
|
18495
|
+
signature: {
|
|
18496
|
+
type: "string",
|
|
18497
|
+
description: "On-chain transaction signature returned by send_solana_transaction",
|
|
18498
|
+
required: true
|
|
18499
|
+
},
|
|
18500
|
+
gameId: {
|
|
18501
|
+
type: "string",
|
|
18502
|
+
description: "The game ID (from confirmWith.params)",
|
|
18503
|
+
required: true
|
|
18504
|
+
},
|
|
18505
|
+
amount: {
|
|
18506
|
+
type: "number",
|
|
18507
|
+
description: "Amount in USDC minor units (from confirmWith.params)",
|
|
18508
|
+
required: true
|
|
18509
|
+
}
|
|
18510
|
+
},
|
|
18511
|
+
execute: (c, a) => confirmDonateToPot(
|
|
18512
|
+
c,
|
|
18513
|
+
a
|
|
18514
|
+
)
|
|
18515
|
+
},
|
|
17697
18516
|
{
|
|
17698
18517
|
name: "dim_request_rematch",
|
|
17699
18518
|
description: "Request a rematch after a completed game. If both players request, a lobby is created automatically server-side.",
|
|
@@ -18517,12 +19336,11 @@ if (cliArgs[0] === "init-wallet") {
|
|
|
18517
19336
|
var walletPrivateKey = await resolveWalletPrivateKey(cliArgs);
|
|
18518
19337
|
if (!walletPrivateKey) {
|
|
18519
19338
|
console.error(
|
|
18520
|
-
"
|
|
19339
|
+
"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
19340
|
);
|
|
18522
|
-
process.exit(1);
|
|
18523
19341
|
}
|
|
18524
19342
|
var { server } = createDimMcpServer({
|
|
18525
|
-
walletPrivateKey,
|
|
19343
|
+
walletPrivateKey: walletPrivateKey ?? void 0,
|
|
18526
19344
|
apiUrl: process.env.DIM_API_URL || "https://api.dim.cool",
|
|
18527
19345
|
referralCode: process.env.DIM_REFERRAL_CODE
|
|
18528
19346
|
});
|