@swarm-tips/client 0.2.0 → 0.2.2
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/SBOM.spdx.json +5 -5
- package/dist/coordination-game/protocol.js +11 -2
- package/dist/coordination-game/solana.d.ts +17 -1
- package/dist/coordination-game/solana.js +49 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/shillbot/api.d.ts +5 -0
- package/dist/shillbot/api.js +25 -2
- package/dist/shillbot/switchboard.js +19 -7
- package/dist/swarm/index.js +1 -1
- package/package.json +2 -1
package/SBOM.spdx.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"spdxVersion": "SPDX-2.3",
|
|
3
3
|
"dataLicense": "CC0-1.0",
|
|
4
4
|
"SPDXID": "SPDXRef-DOCUMENT",
|
|
5
|
-
"name": "@swarm-tips/client-0.2.
|
|
6
|
-
"documentNamespace": "https://swarm.tips/sbom/client/0.2.
|
|
5
|
+
"name": "@swarm-tips/client-0.2.2",
|
|
6
|
+
"documentNamespace": "https://swarm.tips/sbom/client/0.2.2",
|
|
7
7
|
"creationInfo": {
|
|
8
|
-
"created": "2026-09-
|
|
8
|
+
"created": "2026-09-08T07:02:42.781Z",
|
|
9
9
|
"creators": [
|
|
10
10
|
"Tool: sdk/client/scripts/generate-sbom.mjs",
|
|
11
11
|
"Organization: Swarm Tips"
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
{
|
|
16
16
|
"name": "@swarm-tips/client",
|
|
17
17
|
"SPDXID": "SPDXRef-Package",
|
|
18
|
-
"versionInfo": "0.2.
|
|
19
|
-
"downloadLocation": "https://www.npmjs.com/package/@swarm-tips/client/v/0.2.
|
|
18
|
+
"versionInfo": "0.2.2",
|
|
19
|
+
"downloadLocation": "https://www.npmjs.com/package/@swarm-tips/client/v/0.2.2",
|
|
20
20
|
"filesAnalyzed": false,
|
|
21
21
|
"licenseConcluded": "MIT",
|
|
22
22
|
"licenseDeclared": "MIT"
|
|
@@ -81,8 +81,17 @@ export function decodeTournament(data) {
|
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
83
|
export function decodeTournamentStats(data) {
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
// Stats readers need only the two u64 fields through byte 80. Accept that
|
|
85
|
+
// prefix so lightweight RPC fixtures and partial account slices do not need
|
|
86
|
+
// the trailing `finalized` byte required by the full decoder.
|
|
87
|
+
if (data.length < 80) {
|
|
88
|
+
throw new RangeError(`Tournament stats data too short: ${data.length} bytes`);
|
|
89
|
+
}
|
|
90
|
+
const view = viewOf(data);
|
|
91
|
+
return {
|
|
92
|
+
prizeLamports: view.getBigUint64(64, true),
|
|
93
|
+
gameCount: Number(view.getBigUint64(72, true)),
|
|
94
|
+
};
|
|
86
95
|
}
|
|
87
96
|
function hasCommitted(commit) {
|
|
88
97
|
return Array.from(commit).some((byte) => byte !== 0);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BN, Program } from "@coral-xyz/anchor";
|
|
2
|
-
import { Connection, Keypair, PublicKey, Transaction,
|
|
2
|
+
import { Connection, Keypair, PublicKey, Transaction, TransactionInstruction, type Commitment, type VersionedTransaction } from "@solana/web3.js";
|
|
3
3
|
import { type CoordinationGame } from "../contracts/index.js";
|
|
4
4
|
import { type GlobalConfigData, type TournamentData } from "./protocol.js";
|
|
5
5
|
export interface SolanaWalletSigner {
|
|
@@ -19,6 +19,9 @@ export interface CoordinationGameSolanaClientOptions {
|
|
|
19
19
|
tournamentId?: bigint;
|
|
20
20
|
commitment?: Commitment;
|
|
21
21
|
}
|
|
22
|
+
export declare const SESSION_FEE_HEADROOM_LAMPORTS = 10000000;
|
|
23
|
+
export declare function sessionFundLamports(stakeLamports: bigint): number;
|
|
24
|
+
export declare function sessionBalanceFloor(stakeLamports: bigint): number;
|
|
22
25
|
export declare function generateSessionKeypair(): Keypair;
|
|
23
26
|
export declare function readStoredSession(storage: SolanaSessionStorage, walletAddress: string, now?: () => number): Keypair | null;
|
|
24
27
|
export declare function storeSession(storage: SolanaSessionStorage, session: Keypair, walletAddress: string, now?: () => number): void;
|
|
@@ -40,6 +43,19 @@ export declare class CoordinationGameSolanaClient {
|
|
|
40
43
|
fetchTournament(tournamentId?: bigint): Promise<TournamentData | null>;
|
|
41
44
|
fetchGlobalConfig(): Promise<GlobalConfigData>;
|
|
42
45
|
buildCreatePlayerSessionInstruction(sessionPublicKey: PublicKey): Promise<TransactionInstruction>;
|
|
46
|
+
buildSessionSetupTransaction(input: {
|
|
47
|
+
sessionPublicKey: PublicKey;
|
|
48
|
+
lamports: number;
|
|
49
|
+
memoNonce?: string;
|
|
50
|
+
}): Promise<Transaction>;
|
|
51
|
+
waitForSignature(signature: string, options?: {
|
|
52
|
+
attempts?: number;
|
|
53
|
+
intervalMs?: number;
|
|
54
|
+
sleep?: (ms: number) => Promise<void>;
|
|
55
|
+
}): Promise<void>;
|
|
56
|
+
fetchPlayerProfiles(tournamentId?: bigint): Promise<Array<{
|
|
57
|
+
account: unknown;
|
|
58
|
+
}>>;
|
|
43
59
|
depositStake(tournament: PublicKey, session?: Keypair | null): Promise<string>;
|
|
44
60
|
buildCreateGameTransaction(input: {
|
|
45
61
|
stake: BN;
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import { AnchorProvider, Program } from "@coral-xyz/anchor";
|
|
2
2
|
import { Buffer } from "buffer";
|
|
3
|
-
import
|
|
3
|
+
import bs58 from "bs58";
|
|
4
|
+
import { Connection, Keypair, PublicKey, SystemProgram, Transaction, TransactionInstruction, } from "@solana/web3.js";
|
|
4
5
|
import { COORDINATION_GAME_IDL } from "../contracts/index.js";
|
|
5
6
|
import { SwarmClientError } from "../errors.js";
|
|
6
|
-
import { decodeGlobalConfig, decodeTournament, escrowPda, gameCounterPda, gamePda, globalConfigPda, playerProfilePda, playerSessionPda, tournamentPda, } from "./protocol.js";
|
|
7
|
+
import { decodeGlobalConfig, decodeTournament, escrowPda, gameCounterPda, gamePda, globalConfigPda, playerProfilePda, playerSessionPda, tournamentPda, u64LE, } from "./protocol.js";
|
|
7
8
|
const SESSION_STORAGE_KEY = "coordination-session";
|
|
8
9
|
const SESSION_TTL_MS = 24 * 60 * 60 * 1000;
|
|
10
|
+
const MEMO_PROGRAM_ID = new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr");
|
|
11
|
+
export const SESSION_FEE_HEADROOM_LAMPORTS = 10_000_000;
|
|
12
|
+
export function sessionFundLamports(stakeLamports) {
|
|
13
|
+
return Number(stakeLamports) + SESSION_FEE_HEADROOM_LAMPORTS;
|
|
14
|
+
}
|
|
15
|
+
export function sessionBalanceFloor(stakeLamports) {
|
|
16
|
+
return Number(stakeLamports) + SESSION_FEE_HEADROOM_LAMPORTS / 2;
|
|
17
|
+
}
|
|
9
18
|
function walletForSession(session) {
|
|
10
19
|
return {
|
|
11
20
|
publicKey: session.publicKey,
|
|
@@ -105,6 +114,44 @@ export class CoordinationGameSolanaClient {
|
|
|
105
114
|
const wallet = this.requireWallet();
|
|
106
115
|
return this.program(wallet).methods.createPlayerSession().accountsPartial({ sessionKey: sessionPublicKey }).instruction();
|
|
107
116
|
}
|
|
117
|
+
async buildSessionSetupTransaction(input) {
|
|
118
|
+
const wallet = this.requireWallet();
|
|
119
|
+
const transaction = new Transaction().add(SystemProgram.transfer({
|
|
120
|
+
fromPubkey: wallet.publicKey,
|
|
121
|
+
toPubkey: input.sessionPublicKey,
|
|
122
|
+
lamports: input.lamports,
|
|
123
|
+
}));
|
|
124
|
+
if (input.memoNonce) {
|
|
125
|
+
transaction.add(new TransactionInstruction({
|
|
126
|
+
keys: [],
|
|
127
|
+
programId: MEMO_PROGRAM_ID,
|
|
128
|
+
data: Buffer.from(input.memoNonce, "utf8"),
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
const authority = playerSessionPda(wallet.publicKey, input.sessionPublicKey)[0];
|
|
132
|
+
if ((await this.connection.getAccountInfo(authority)) === null) {
|
|
133
|
+
transaction.add(await this.buildCreatePlayerSessionInstruction(input.sessionPublicKey));
|
|
134
|
+
}
|
|
135
|
+
return transaction;
|
|
136
|
+
}
|
|
137
|
+
async waitForSignature(signature, options = {}) {
|
|
138
|
+
const sleep = options.sleep ?? ((ms) => new Promise((resolve) => globalThis.setTimeout(resolve, ms)));
|
|
139
|
+
const attempts = options.attempts ?? 6;
|
|
140
|
+
for (let index = 0; index < attempts; index += 1) {
|
|
141
|
+
await sleep(options.intervalMs ?? 1_500);
|
|
142
|
+
const status = await this.connection.getSignatureStatus(signature);
|
|
143
|
+
const confirmation = status.value?.confirmationStatus;
|
|
144
|
+
if (confirmation === "confirmed" || confirmation === "finalized")
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
throw new SwarmClientError({ code: "TIMEOUT", operation: "solana.waitForSignature", message: `transaction ${signature} did not confirm after ${attempts} polls`, retryable: true });
|
|
148
|
+
}
|
|
149
|
+
async fetchPlayerProfiles(tournamentId = this.tournamentId) {
|
|
150
|
+
const profiles = await this.program().account.playerProfile.all([{
|
|
151
|
+
memcmp: { offset: 40, bytes: bs58.encode(u64LE(tournamentId)) },
|
|
152
|
+
}]);
|
|
153
|
+
return profiles;
|
|
154
|
+
}
|
|
108
155
|
async depositStake(tournament, session) {
|
|
109
156
|
const wallet = this.requireWallet();
|
|
110
157
|
const escrow = escrowPda(this.tournamentId, wallet.publicKey)[0];
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The unified client package version. Functional APIs live in typed subpaths. */
|
|
2
|
-
export declare const CLIENT_VERSION: "0.2.
|
|
2
|
+
export declare const CLIENT_VERSION: "0.2.2";
|
|
3
3
|
export declare const CLIENT_MANIFEST_SCHEMA: "swarm.client-manifest/v1";
|
|
4
4
|
export { SwarmClientError } from "./errors.js";
|
|
5
5
|
export type { SwarmClientErrorCode, SwarmClientErrorOptions } from "./errors.js";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/** The unified client package version. Functional APIs live in typed subpaths. */
|
|
2
|
-
export const CLIENT_VERSION = "0.2.
|
|
2
|
+
export const CLIENT_VERSION = "0.2.2";
|
|
3
3
|
export const CLIENT_MANIFEST_SCHEMA = "swarm.client-manifest/v1";
|
|
4
4
|
export { SwarmClientError } from "./errors.js";
|
package/dist/shillbot/api.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Connection, type BlockhashWithExpiryBlockHeight } from "@solana/web3.js";
|
|
2
2
|
import { HttpClient, type HttpClientOptions } from "../http.js";
|
|
3
3
|
import { type Action, type TransactionInspection, type WalletSignCallback } from "./index.js";
|
|
4
|
+
export type { Action } from "./index.js";
|
|
4
5
|
export interface PreparedTransactionResponse {
|
|
5
6
|
message?: string;
|
|
6
7
|
task_id?: string;
|
|
@@ -57,6 +58,7 @@ export interface PreparedTransactionExpectation {
|
|
|
57
58
|
escrowLamports?: bigint;
|
|
58
59
|
allowedProgramIds?: string[];
|
|
59
60
|
}
|
|
61
|
+
export declare function permittedPreparedProgramIds(action: Action, network: "mainnet" | "devnet"): string[];
|
|
60
62
|
/**
|
|
61
63
|
* Fail-closed local validation for a server-prepared Shillbot transaction.
|
|
62
64
|
* The network is an explicit part of the expectation; callers must construct
|
|
@@ -69,4 +71,7 @@ export declare function signBroadcastPreparedTransaction(input: {
|
|
|
69
71
|
expected: PreparedTransactionExpectation;
|
|
70
72
|
sign: WalletSignCallback;
|
|
71
73
|
confirmation?: BlockhashWithExpiryBlockHeight;
|
|
74
|
+
timeoutMs?: number;
|
|
75
|
+
setTimeout?: typeof globalThis.setTimeout;
|
|
76
|
+
clearTimeout?: typeof globalThis.clearTimeout;
|
|
72
77
|
}): Promise<string>;
|
package/dist/shillbot/api.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Buffer } from "buffer";
|
|
2
|
-
import { ComputeBudgetProgram, SystemProgram } from "@solana/web3.js";
|
|
2
|
+
import { ComputeBudgetProgram, Secp256k1Program, SystemProgram } from "@solana/web3.js";
|
|
3
3
|
import { HttpClient } from "../http.js";
|
|
4
4
|
import { SwarmClientError } from "../errors.js";
|
|
5
5
|
import { SHILLBOT_PROGRAM_ID, inspectTransaction, shillbotInstructionDiscriminator, signAndBroadcast, } from "./index.js";
|
|
@@ -28,6 +28,15 @@ export class ShillbotApiClient {
|
|
|
28
28
|
buildFinalize(id) { return this.post(`/tasks/${encodeURIComponent(id)}/build-finalize`); }
|
|
29
29
|
confirmTask(id, body) { return this.post(`/tasks/${encodeURIComponent(id)}/confirm`, body); }
|
|
30
30
|
}
|
|
31
|
+
const SWITCHBOARD_PROGRAM_IDS = {
|
|
32
|
+
mainnet: "SBondMDrcV3K4kxZR1HNVT7osZxAHVHgYXL5Ze1oMUv",
|
|
33
|
+
devnet: "Aio4gaXjXzJNVLtzwtNVmSqGKpANtXhybbkhtAC94ji2",
|
|
34
|
+
};
|
|
35
|
+
export function permittedPreparedProgramIds(action, network) {
|
|
36
|
+
return action === "verify"
|
|
37
|
+
? [SWITCHBOARD_PROGRAM_IDS[network], Secp256k1Program.programId.toBase58()]
|
|
38
|
+
: [];
|
|
39
|
+
}
|
|
31
40
|
function lifecycleName(action) {
|
|
32
41
|
if (action === "create")
|
|
33
42
|
return "create_task";
|
|
@@ -67,6 +76,7 @@ export function validatePreparedTransaction(encoded, expected) {
|
|
|
67
76
|
shillbotId,
|
|
68
77
|
ComputeBudgetProgram.programId.toBase58(),
|
|
69
78
|
SystemProgram.programId.toBase58(),
|
|
79
|
+
...permittedPreparedProgramIds(expected.action, expected.network),
|
|
70
80
|
...(expected.allowedProgramIds ?? []),
|
|
71
81
|
]);
|
|
72
82
|
const unrelated = inspected.instructions.find((instruction) => !alwaysAllowed.has(instruction.program_id));
|
|
@@ -87,5 +97,18 @@ export function validatePreparedTransaction(encoded, expected) {
|
|
|
87
97
|
}
|
|
88
98
|
export async function signBroadcastPreparedTransaction(input) {
|
|
89
99
|
validatePreparedTransaction(input.transaction, input.expected);
|
|
90
|
-
|
|
100
|
+
const timeoutMs = input.timeoutMs ?? 60_000;
|
|
101
|
+
let timeout;
|
|
102
|
+
try {
|
|
103
|
+
return await Promise.race([
|
|
104
|
+
signAndBroadcast(input.connection, input.transaction, input.sign, input.confirmation),
|
|
105
|
+
new Promise((_, reject) => {
|
|
106
|
+
timeout = (input.setTimeout ?? globalThis.setTimeout)(() => reject(new SwarmClientError({ code: "TIMEOUT", operation: "shillbot.signBroadcastPreparedTransaction", message: `Transaction did not confirm within ${timeoutMs}ms`, retryable: true })), timeoutMs);
|
|
107
|
+
}),
|
|
108
|
+
]);
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
if (timeout !== undefined)
|
|
112
|
+
(input.clearTimeout ?? globalThis.clearTimeout)(timeout);
|
|
113
|
+
}
|
|
91
114
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { AnchorProvider, Program } from "@coral-xyz/anchor";
|
|
2
|
+
import BN from "bn.js";
|
|
2
3
|
import { Buffer } from "buffer";
|
|
3
4
|
import { OracleJob } from "@switchboard-xyz/common/protos";
|
|
4
5
|
import { ComputeBudgetProgram, Keypair, PublicKey, Secp256k1Program, SystemProgram, TransactionInstruction, TransactionMessage, VersionedTransaction, } from "@solana/web3.js";
|
|
@@ -14,7 +15,6 @@ const SWITCHBOARD_PROGRAMS = {
|
|
|
14
15
|
};
|
|
15
16
|
const CROSSBAR_URL = "https://crossbar.switchboard.xyz";
|
|
16
17
|
const SWITCHBOARD_GATEWAY_API_VERSION = "1.0.0";
|
|
17
|
-
const anchorRuntime = anchor.default ?? anchor;
|
|
18
18
|
async function jsonResponse(response, description) {
|
|
19
19
|
if (!response.ok) {
|
|
20
20
|
throw new Error(`${description} failed with HTTP ${response.status}`);
|
|
@@ -120,11 +120,23 @@ function associatedTokenAddress(mint, owner) {
|
|
|
120
120
|
export async function buildSwitchboardCrankAndVerify(request) {
|
|
121
121
|
const switchboardProgramId = SWITCHBOARD_PROGRAMS[request.network];
|
|
122
122
|
const dummy = Keypair.generate();
|
|
123
|
-
const
|
|
124
|
-
|
|
123
|
+
const signDummy = async (transaction) => {
|
|
124
|
+
if ("partialSign" in transaction)
|
|
125
|
+
transaction.partialSign(dummy);
|
|
126
|
+
else
|
|
127
|
+
transaction.sign([dummy]);
|
|
128
|
+
return transaction;
|
|
129
|
+
};
|
|
130
|
+
const dummyWallet = {
|
|
131
|
+
publicKey: dummy.publicKey,
|
|
132
|
+
signTransaction: signDummy,
|
|
133
|
+
signAllTransactions: async (transactions) => Promise.all(transactions.map(signDummy)),
|
|
134
|
+
};
|
|
135
|
+
const provider = new AnchorProvider(request.connection, dummyWallet, { commitment: "confirmed" });
|
|
136
|
+
const idl = await Program.fetchIdl(switchboardProgramId, provider);
|
|
125
137
|
if (!idl)
|
|
126
138
|
throw new Error("failed to fetch Switchboard IDL");
|
|
127
|
-
const program = new
|
|
139
|
+
const program = new Program(idl, provider);
|
|
128
140
|
const switchboardAccounts = program.account;
|
|
129
141
|
const feedData = (await switchboardAccounts.pullFeedAccountData.fetch(request.feed));
|
|
130
142
|
const feedHashHex = Buffer.from(feedData.feedHash).toString("hex");
|
|
@@ -152,8 +164,8 @@ export async function buildSwitchboardCrankAndVerify(request) {
|
|
|
152
164
|
const oracleStats = oraclePubkeys.map((oracle) => PublicKey.findProgramAddressSync([Buffer.from("OracleStats"), oracle.toBuffer()], switchboardProgramId)[0]);
|
|
153
165
|
const feeds = response.median_responses.map((median) => median.feed_hash === feedHashHex ? request.feed : PublicKey.default);
|
|
154
166
|
const submit = program.instruction.pullFeedSubmitResponseConsensus({
|
|
155
|
-
slot: new
|
|
156
|
-
values: response.median_responses.map((median) => new
|
|
167
|
+
slot: new BN(response.slot),
|
|
168
|
+
values: response.median_responses.map((median) => new BN(median.value)),
|
|
157
169
|
}, {
|
|
158
170
|
accounts: {
|
|
159
171
|
queue: queuePubkey,
|
package/dist/swarm/index.js
CHANGED
|
@@ -49,7 +49,7 @@ export class SwarmClient {
|
|
|
49
49
|
async toolCount() {
|
|
50
50
|
const headers = { "Content-Type": "application/json", Accept: "application/json, text/event-stream" };
|
|
51
51
|
try {
|
|
52
|
-
const init = await this.fetchImpl(`${this.baseUrl}/mcp`, { method: "POST", headers, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: "2025-03-26", capabilities: {}, clientInfo: { name: "swarm-client", version: "0.2.
|
|
52
|
+
const init = await this.fetchImpl(`${this.baseUrl}/mcp`, { method: "POST", headers, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: "2025-03-26", capabilities: {}, clientInfo: { name: "swarm-client", version: "0.2.2" } } }) });
|
|
53
53
|
if (!init.ok)
|
|
54
54
|
return null;
|
|
55
55
|
const session = init.headers.get("mcp-session-id");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swarm-tips/client",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Typed browser and Node.js clients for Swarm Tips, Shillbot, and Coordination Game",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -96,6 +96,7 @@
|
|
|
96
96
|
"@coral-xyz/anchor": "^0.32.1",
|
|
97
97
|
"@solana/web3.js": "^1.98.4",
|
|
98
98
|
"@switchboard-xyz/common": "^5.7.0",
|
|
99
|
+
"bn.js": "^5.2.2",
|
|
99
100
|
"bs58": "^6.0.0",
|
|
100
101
|
"buffer": "^6.0.3",
|
|
101
102
|
"js-sha256": "^0.11.0"
|