@swarm-tips/client 0.0.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -1
- package/SBOM.spdx.json +1558 -0
- package/dist/cli/swarm-tx.d.ts +2 -0
- package/dist/cli/swarm-tx.js +46 -0
- package/dist/cli/vow-verify.d.ts +2 -0
- package/dist/cli/vow-verify.js +69 -0
- package/dist/contracts/generated/coordination_game.d.ts +6107 -0
- package/dist/contracts/generated/coordination_game.js +1 -0
- package/dist/contracts/generated/shillbot.d.ts +4209 -0
- package/dist/contracts/generated/shillbot.js +1 -0
- package/dist/contracts/idl/coordination_game.json +6101 -0
- package/dist/contracts/idl/shillbot.json +4203 -0
- package/dist/contracts/index.d.ts +33 -0
- package/dist/contracts/index.js +84 -0
- package/dist/coordination-game/battery.d.ts +40 -0
- package/dist/coordination-game/battery.js +82 -0
- package/dist/coordination-game/index.d.ts +4 -0
- package/dist/coordination-game/index.js +3 -0
- package/dist/coordination-game/outcome-oracle.d.ts +53 -0
- package/dist/coordination-game/outcome-oracle.js +136 -0
- package/dist/evm/index.d.ts +3 -0
- package/dist/evm/index.js +6 -0
- package/dist/evm/sign.d.ts +12 -0
- package/dist/evm/sign.js +23 -0
- package/dist/evm/testing.d.ts +21 -0
- package/dist/evm/testing.js +34 -0
- package/dist/evm/tx.d.ts +27 -0
- package/dist/evm/tx.js +18 -0
- package/dist/evm/wallet.d.ts +58 -0
- package/dist/evm/wallet.js +182 -0
- package/dist/inbox/client.d.ts +189 -0
- package/dist/inbox/client.js +334 -0
- package/dist/inbox/index.d.ts +2 -0
- package/dist/inbox/index.js +4 -0
- package/dist/inbox/solana.d.ts +11 -0
- package/dist/inbox/solana.js +13 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/shillbot/index.d.ts +115 -0
- package/dist/shillbot/index.js +327 -0
- package/dist/shillbot/payout-oracle.d.ts +63 -0
- package/dist/shillbot/payout-oracle.js +172 -0
- package/dist/shillbot/switchboard.d.ts +27 -0
- package/dist/shillbot/switchboard.js +205 -0
- package/dist/vow/decoders/extension.d.ts +62 -0
- package/dist/vow/decoders/extension.js +48 -0
- package/dist/vow/decoders/shillbot.d.ts +59 -0
- package/dist/vow/decoders/shillbot.js +130 -0
- package/dist/vow/discriminator.d.ts +12 -0
- package/dist/vow/discriminator.js +25 -0
- package/dist/vow/index.d.ts +7 -0
- package/dist/vow/index.js +5 -0
- package/dist/vow/schema.d.ts +14 -0
- package/dist/vow/schema.js +132 -0
- package/dist/vow/types.d.ts +107 -0
- package/dist/vow/types.js +1 -0
- package/dist/vow/verify.d.ts +23 -0
- package/dist/vow/verify.js +145 -0
- package/package.json +99 -6
- package/index.js +0 -1
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REST client for the agent-inbox twins on mcp-server
|
|
3
|
+
* (`/internal/inbox/*` on mcp.swarm.tips). The twins share the MCP inbox's
|
|
4
|
+
* storage layer, so shapes mirror the `agent_*` MCP tools exactly
|
|
5
|
+
* (`from_wallet` is CAIP-10, `msg_id` doubles as the paging/ack cursor).
|
|
6
|
+
*
|
|
7
|
+
* Session + request flow (mirrors scripts/seed-inbox.ts):
|
|
8
|
+
*
|
|
9
|
+
* createSession(wallet, signNonce)
|
|
10
|
+
* │
|
|
11
|
+
* ├─ localStorage hit? ──────────────► use cached {session_id, tier}
|
|
12
|
+
* │ │
|
|
13
|
+
* └─ POST /session {wallet} → {nonce} │
|
|
14
|
+
* signNonce(nonce) (ONE wallet popup) │
|
|
15
|
+
* POST /session {wallet,nonce,sig} │
|
|
16
|
+
* → {session_id, tier} → cache ────┤
|
|
17
|
+
* ▼
|
|
18
|
+
* getMessages / send / ack ── X-Inbox-Session header
|
|
19
|
+
* │
|
|
20
|
+
* └─ 401? → drop cache → re-mint (popup) → retry ONCE → else throw
|
|
21
|
+
*
|
|
22
|
+
* The `signNonce` callback is chain-agnostic: it receives the raw nonce
|
|
23
|
+
* string and must return the wire-format signature string. Solana =
|
|
24
|
+
* bs58(ed25519 detached over the nonce UTF-8 bytes) — use `solanaNonceSigner`
|
|
25
|
+
* from this package; EVM = the `personal_sign` hex string as-is.
|
|
26
|
+
*/
|
|
27
|
+
/** Non-2xx responses and client-side boundary rejections. `status` is the
|
|
28
|
+
* HTTP status, or 0 for errors raised before any request was made. */
|
|
29
|
+
export class InboxApiError extends Error {
|
|
30
|
+
status;
|
|
31
|
+
constructor(status, message) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.status = status;
|
|
34
|
+
this.name = "InboxApiError";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const DEFAULT_BASE_URL = "https://mcp.swarm.tips";
|
|
38
|
+
const SESSION_PATH = "/internal/inbox/session";
|
|
39
|
+
const MAX_PAGE_LIMIT = 50;
|
|
40
|
+
const MAX_BODY_BYTES = 4096;
|
|
41
|
+
function sessionCacheKey(wallet) {
|
|
42
|
+
return `swarm-inbox-session:${wallet}`;
|
|
43
|
+
}
|
|
44
|
+
function defaultStorage() {
|
|
45
|
+
try {
|
|
46
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
47
|
+
return window.localStorage;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
// Blocked storage (Safari private mode) — run uncached.
|
|
52
|
+
console.debug("[inbox-client] localStorage unavailable", e);
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
async function parseBody(res) {
|
|
57
|
+
const text = await res.text();
|
|
58
|
+
if (!res.ok) {
|
|
59
|
+
let message = text || res.statusText || `HTTP ${res.status}`;
|
|
60
|
+
try {
|
|
61
|
+
const parsed = JSON.parse(text);
|
|
62
|
+
message = parsed.error ?? parsed.message ?? message;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Non-JSON error body — keep the raw text.
|
|
66
|
+
}
|
|
67
|
+
throw new InboxApiError(res.status, message);
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
return JSON.parse(text);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
throw new InboxApiError(res.status, "invalid JSON in inbox response");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export class InboxClient {
|
|
77
|
+
baseUrl;
|
|
78
|
+
storage;
|
|
79
|
+
fetchFn;
|
|
80
|
+
session = null;
|
|
81
|
+
wallet = null;
|
|
82
|
+
signNonce = null;
|
|
83
|
+
constructor(opts = {}) {
|
|
84
|
+
this.baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
85
|
+
this.storage = opts.storage === undefined ? defaultStorage() : opts.storage;
|
|
86
|
+
this.fetchFn =
|
|
87
|
+
opts.fetchFn ??
|
|
88
|
+
((input, init) => fetch(input, init));
|
|
89
|
+
}
|
|
90
|
+
/** True when a cached session exists for `wallet` — opening the inbox will
|
|
91
|
+
* not prompt a wallet signature. */
|
|
92
|
+
hasCachedSession(wallet) {
|
|
93
|
+
return this.readCachedSession(wallet) !== null;
|
|
94
|
+
}
|
|
95
|
+
/** Mint (or restore from cache) the inbox session for `wallet`. Signing
|
|
96
|
+
* happens at most once per call — zero times on a cache hit. The wallet +
|
|
97
|
+
* signer are retained for transparent re-mint when a request 401s. */
|
|
98
|
+
async createSession(wallet, signNonce) {
|
|
99
|
+
if (!wallet)
|
|
100
|
+
throw new InboxApiError(0, "wallet is required");
|
|
101
|
+
this.wallet = wallet;
|
|
102
|
+
this.signNonce = signNonce;
|
|
103
|
+
const cached = this.readCachedSession(wallet);
|
|
104
|
+
if (cached) {
|
|
105
|
+
this.session = cached;
|
|
106
|
+
return cached;
|
|
107
|
+
}
|
|
108
|
+
return this.mintSession();
|
|
109
|
+
}
|
|
110
|
+
/** Drop the in-memory and cached session (e.g. on wallet disconnect). */
|
|
111
|
+
clearSession(wallet) {
|
|
112
|
+
const key = wallet ?? this.wallet;
|
|
113
|
+
this.session = null;
|
|
114
|
+
if (key && this.storage) {
|
|
115
|
+
try {
|
|
116
|
+
this.storage.removeItem(sessionCacheKey(key));
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
console.debug("[inbox-client] session cache clear failed", e);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/** Read a page of messages, newest first. Omit `threadId` for the whole
|
|
124
|
+
* mailbox; pass the previous page's `next_cursor` to page older. */
|
|
125
|
+
async getMessages(opts = {}) {
|
|
126
|
+
const params = new URLSearchParams();
|
|
127
|
+
if (opts.threadId)
|
|
128
|
+
params.set("thread_id", opts.threadId);
|
|
129
|
+
if (opts.cursor)
|
|
130
|
+
params.set("cursor", opts.cursor);
|
|
131
|
+
if (opts.includeSent)
|
|
132
|
+
params.set("include_sent", "true");
|
|
133
|
+
if (opts.limit !== undefined) {
|
|
134
|
+
if (!Number.isInteger(opts.limit) ||
|
|
135
|
+
opts.limit < 1 ||
|
|
136
|
+
opts.limit > MAX_PAGE_LIMIT) {
|
|
137
|
+
throw new InboxApiError(0, `limit must be an integer in [1, ${MAX_PAGE_LIMIT}]`);
|
|
138
|
+
}
|
|
139
|
+
params.set("limit", String(opts.limit));
|
|
140
|
+
}
|
|
141
|
+
const qs = params.toString();
|
|
142
|
+
const page = await this.authed(`/internal/inbox/messages${qs ? `?${qs}` : ""}`, { method: "GET" });
|
|
143
|
+
return {
|
|
144
|
+
messages: page.messages ?? [],
|
|
145
|
+
next_cursor: page.next_cursor ?? null,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/** Read one thread (e.g. `task:{task_id}` / `game:{id}`), newest first. Pass
|
|
149
|
+
* `includeSent` to fold in the connected wallet's own replies (full
|
|
150
|
+
* two-way conversation). */
|
|
151
|
+
async getThread(threadId, opts = {}) {
|
|
152
|
+
if (!threadId)
|
|
153
|
+
throw new InboxApiError(0, "threadId is required");
|
|
154
|
+
return this.getMessages({ ...opts, threadId });
|
|
155
|
+
}
|
|
156
|
+
/** Send a message. Rejects empty / oversize bodies client-side; quota and
|
|
157
|
+
* mute rejections surface as InboxApiError with the server's message. */
|
|
158
|
+
async send(toWallet, body, threadId, intent) {
|
|
159
|
+
if (!toWallet)
|
|
160
|
+
throw new InboxApiError(0, "toWallet is required");
|
|
161
|
+
const bytes = new TextEncoder().encode(body).length;
|
|
162
|
+
if (bytes === 0 || bytes > MAX_BODY_BYTES) {
|
|
163
|
+
throw new InboxApiError(0, `body must be 1..${MAX_BODY_BYTES} bytes (got ${bytes})`);
|
|
164
|
+
}
|
|
165
|
+
const payload = { to_wallet: toWallet, body };
|
|
166
|
+
if (threadId)
|
|
167
|
+
payload["thread_id"] = threadId;
|
|
168
|
+
if (intent)
|
|
169
|
+
payload["intent"] = intent;
|
|
170
|
+
return this.authed("/internal/inbox/send", {
|
|
171
|
+
method: "POST",
|
|
172
|
+
body: JSON.stringify(payload),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
/** Advance the read watermark up to a msg_id cursor. NOTE: the watermark is
|
|
176
|
+
* mailbox-global — acking a cursor also acks every older message. */
|
|
177
|
+
async ack(upToCursor) {
|
|
178
|
+
if (!upToCursor)
|
|
179
|
+
throw new InboxApiError(0, "upToCursor is required");
|
|
180
|
+
await this.authed("/internal/inbox/ack", {
|
|
181
|
+
method: "POST",
|
|
182
|
+
body: JSON.stringify({ up_to_cursor: upToCursor }),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
// -- topic boards ---------------------------------------------------------
|
|
186
|
+
/** Read a page of a public topic board, newest first. OPEN — no session
|
|
187
|
+
* needed. `minTrust` drops posts whose author trust is below it
|
|
188
|
+
* server-side. Hidden (auto-moderated) posts are always dropped. */
|
|
189
|
+
async readTopic(topicId, opts = {}) {
|
|
190
|
+
if (!topicId)
|
|
191
|
+
throw new InboxApiError(0, "topicId is required");
|
|
192
|
+
const params = new URLSearchParams({ topic_id: topicId });
|
|
193
|
+
if (opts.cursor)
|
|
194
|
+
params.set("cursor", opts.cursor);
|
|
195
|
+
if (opts.minTrust !== undefined)
|
|
196
|
+
params.set("min_trust", String(opts.minTrust));
|
|
197
|
+
if (opts.limit !== undefined) {
|
|
198
|
+
if (!Number.isInteger(opts.limit) ||
|
|
199
|
+
opts.limit < 1 ||
|
|
200
|
+
opts.limit > MAX_PAGE_LIMIT) {
|
|
201
|
+
throw new InboxApiError(0, `limit must be an integer in [1, ${MAX_PAGE_LIMIT}]`);
|
|
202
|
+
}
|
|
203
|
+
params.set("limit", String(opts.limit));
|
|
204
|
+
}
|
|
205
|
+
const page = await this.getUnauthed(`/internal/topics/read?${params.toString()}`);
|
|
206
|
+
return {
|
|
207
|
+
topic_id: page.topic_id ?? topicId,
|
|
208
|
+
posts: page.posts ?? [],
|
|
209
|
+
next_cursor: page.next_cursor ?? null,
|
|
210
|
+
filtered_hidden: page.filtered_hidden,
|
|
211
|
+
filtered_below_min_trust: page.filtered_below_min_trust,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/** Publish a post to a topic board. Session-gated (same X-Inbox-Session as
|
|
215
|
+
* inbox send). Rejects empty / oversize bodies client-side; quota
|
|
216
|
+
* rejections surface as InboxApiError with the server's message. */
|
|
217
|
+
async publishPost(topicId, body, opts = {}) {
|
|
218
|
+
if (!topicId)
|
|
219
|
+
throw new InboxApiError(0, "topicId is required");
|
|
220
|
+
const bytes = new TextEncoder().encode(body).length;
|
|
221
|
+
if (bytes === 0 || bytes > MAX_BODY_BYTES) {
|
|
222
|
+
throw new InboxApiError(0, `body must be 1..${MAX_BODY_BYTES} bytes (got ${bytes})`);
|
|
223
|
+
}
|
|
224
|
+
const payload = { topic_id: topicId, body };
|
|
225
|
+
if (opts.replyTo)
|
|
226
|
+
payload["reply_to"] = opts.replyTo;
|
|
227
|
+
if (opts.intent)
|
|
228
|
+
payload["intent"] = opts.intent;
|
|
229
|
+
if (opts.refId)
|
|
230
|
+
payload["ref_id"] = opts.refId;
|
|
231
|
+
return this.authed("/internal/topics/publish", {
|
|
232
|
+
method: "POST",
|
|
233
|
+
body: JSON.stringify(payload),
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
/** Report a post for moderation. Session-gated. Idempotent per reporter —
|
|
237
|
+
* a repeat report returns `already_reported: true`. */
|
|
238
|
+
async reportPost(topicId, postId) {
|
|
239
|
+
if (!topicId)
|
|
240
|
+
throw new InboxApiError(0, "topicId is required");
|
|
241
|
+
if (!postId)
|
|
242
|
+
throw new InboxApiError(0, "postId is required");
|
|
243
|
+
return this.authed("/internal/topics/report", {
|
|
244
|
+
method: "POST",
|
|
245
|
+
body: JSON.stringify({ topic_id: topicId, post_id: postId }),
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
// -- internals ------------------------------------------------------------
|
|
249
|
+
async getUnauthed(path) {
|
|
250
|
+
const res = await this.fetchFn(`${this.baseUrl}${path}`, { method: "GET" });
|
|
251
|
+
return parseBody(res);
|
|
252
|
+
}
|
|
253
|
+
async mintSession() {
|
|
254
|
+
const { wallet, signNonce } = this;
|
|
255
|
+
if (!wallet || !signNonce) {
|
|
256
|
+
throw new InboxApiError(0, "createSession(wallet, signNonce) must be called first");
|
|
257
|
+
}
|
|
258
|
+
const challenge = await this.postUnauthed(SESSION_PATH, { wallet });
|
|
259
|
+
if (!challenge.nonce) {
|
|
260
|
+
throw new InboxApiError(0, "session challenge returned no nonce");
|
|
261
|
+
}
|
|
262
|
+
const signature = await signNonce(challenge.nonce);
|
|
263
|
+
const verified = await this.postUnauthed(SESSION_PATH, { wallet, nonce: challenge.nonce, signature });
|
|
264
|
+
if (!verified.session_id) {
|
|
265
|
+
throw new InboxApiError(0, "session verify returned no session_id");
|
|
266
|
+
}
|
|
267
|
+
const session = {
|
|
268
|
+
session_id: verified.session_id,
|
|
269
|
+
tier: verified.tier ?? "session",
|
|
270
|
+
};
|
|
271
|
+
this.session = session;
|
|
272
|
+
this.writeCachedSession(wallet, session);
|
|
273
|
+
return session;
|
|
274
|
+
}
|
|
275
|
+
async postUnauthed(path, body) {
|
|
276
|
+
const res = await this.fetchFn(`${this.baseUrl}${path}`, {
|
|
277
|
+
method: "POST",
|
|
278
|
+
headers: { "Content-Type": "application/json" },
|
|
279
|
+
body: JSON.stringify(body),
|
|
280
|
+
});
|
|
281
|
+
return parseBody(res);
|
|
282
|
+
}
|
|
283
|
+
async authed(path, init) {
|
|
284
|
+
const session = this.session;
|
|
285
|
+
if (!session) {
|
|
286
|
+
throw new InboxApiError(0, "no inbox session — call createSession first");
|
|
287
|
+
}
|
|
288
|
+
const res = await this.doFetch(path, init, session.session_id);
|
|
289
|
+
if (res.status === 401) {
|
|
290
|
+
// Cached session expired or was invalidated server-side: drop it,
|
|
291
|
+
// re-mint (one signer callback → one wallet popup), retry ONCE.
|
|
292
|
+
this.clearSession();
|
|
293
|
+
const fresh = await this.mintSession();
|
|
294
|
+
const retry = await this.doFetch(path, init, fresh.session_id);
|
|
295
|
+
return parseBody(retry);
|
|
296
|
+
}
|
|
297
|
+
return parseBody(res);
|
|
298
|
+
}
|
|
299
|
+
doFetch(path, init, sessionId) {
|
|
300
|
+
const headers = { "X-Inbox-Session": sessionId };
|
|
301
|
+
if (init.body !== undefined)
|
|
302
|
+
headers["Content-Type"] = "application/json";
|
|
303
|
+
return this.fetchFn(`${this.baseUrl}${path}`, { ...init, headers });
|
|
304
|
+
}
|
|
305
|
+
readCachedSession(wallet) {
|
|
306
|
+
if (!this.storage)
|
|
307
|
+
return null;
|
|
308
|
+
try {
|
|
309
|
+
const raw = this.storage.getItem(sessionCacheKey(wallet));
|
|
310
|
+
if (!raw)
|
|
311
|
+
return null;
|
|
312
|
+
const parsed = JSON.parse(raw);
|
|
313
|
+
if (typeof parsed.session_id !== "string" ||
|
|
314
|
+
parsed.session_id.length === 0) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
return { session_id: parsed.session_id, tier: parsed.tier ?? "session" };
|
|
318
|
+
}
|
|
319
|
+
catch (e) {
|
|
320
|
+
console.debug("[inbox-client] session cache read failed", e);
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
writeCachedSession(wallet, session) {
|
|
325
|
+
if (!this.storage)
|
|
326
|
+
return;
|
|
327
|
+
try {
|
|
328
|
+
this.storage.setItem(sessionCacheKey(wallet), JSON.stringify(session));
|
|
329
|
+
}
|
|
330
|
+
catch (e) {
|
|
331
|
+
console.debug("[inbox-client] session cache write failed", e);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { InboxClient, InboxApiError, type InboxClientOpts, type InboxMessage, type InboxSession, type MessagePage, type NonceSigner, type SendReceipt, type TopicId, type TopicPost, type TopicPage, type PublishReceipt, type ReportReceipt, } from "./client.js";
|
|
2
|
+
export { solanaNonceSigner } from "./solana.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { NonceSigner } from "./client.js";
|
|
2
|
+
/**
|
|
3
|
+
* Adapt a Solana wallet-adapter `signMessage` (bytes → detached ed25519
|
|
4
|
+
* signature bytes) into the NonceSigner the REST session expects: the raw
|
|
5
|
+
* nonce UTF-8 signed, bs58-encoded — exactly what scripts/seed-inbox.ts and
|
|
6
|
+
* the MCP `agent_verify_wallet` phase-2 send.
|
|
7
|
+
*
|
|
8
|
+
* EVM wallets do not use this adapter: `personal_sign(nonce)` already returns
|
|
9
|
+
* the hex string the server verifies, so pass it through unchanged.
|
|
10
|
+
*/
|
|
11
|
+
export declare function solanaNonceSigner(signMessage: (message: Uint8Array) => Promise<Uint8Array>): NonceSigner;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import bs58 from "bs58";
|
|
2
|
+
/**
|
|
3
|
+
* Adapt a Solana wallet-adapter `signMessage` (bytes → detached ed25519
|
|
4
|
+
* signature bytes) into the NonceSigner the REST session expects: the raw
|
|
5
|
+
* nonce UTF-8 signed, bs58-encoded — exactly what scripts/seed-inbox.ts and
|
|
6
|
+
* the MCP `agent_verify_wallet` phase-2 send.
|
|
7
|
+
*
|
|
8
|
+
* EVM wallets do not use this adapter: `personal_sign(nonce)` already returns
|
|
9
|
+
* the hex string the server verifies, so pass it through unchanged.
|
|
10
|
+
*/
|
|
11
|
+
export function solanaNonceSigner(signMessage) {
|
|
12
|
+
return async (nonce) => bs58.encode(await signMessage(new TextEncoder().encode(nonce)));
|
|
13
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Connection, Transaction, TransactionInstruction, VersionedTransaction, type BlockhashWithExpiryBlockHeight } from "@solana/web3.js";
|
|
2
|
+
export { COORDINATION_GAME_IDL, COORDINATION_GAME_PROGRAM_ID, SHILLBOT_IDL, SHILLBOT_PROGRAM_ID, agentStatePda, buildShillbotInstruction, clientStatePda, globalStatePda, shillbotInstructionDiscriminator, taskPda, } from "../contracts/index.js";
|
|
3
|
+
export type { BuildShillbotInstructionRequest, CoordinationGame, Shillbot, } from "../contracts/index.js";
|
|
4
|
+
export declare const SCHEMA_VERSION: "swarm.shillbot.transaction-intent/v1";
|
|
5
|
+
export type Action = "create" | "claim" | "submit" | "approve" | "verify" | "finalize";
|
|
6
|
+
export type Risk = "earn" | "spend" | "escrow_control";
|
|
7
|
+
export type Network = "mainnet" | "devnet";
|
|
8
|
+
export type TransactionVersion = "legacy" | "v0";
|
|
9
|
+
export interface TransactionIntent {
|
|
10
|
+
version: typeof SCHEMA_VERSION;
|
|
11
|
+
action: Action;
|
|
12
|
+
network: Network;
|
|
13
|
+
wallet: string;
|
|
14
|
+
task_pda: string;
|
|
15
|
+
program_id: string;
|
|
16
|
+
fee_payer: string;
|
|
17
|
+
accounts: string[];
|
|
18
|
+
signers: string[];
|
|
19
|
+
movements: Array<{
|
|
20
|
+
asset: string;
|
|
21
|
+
from: string;
|
|
22
|
+
to: string;
|
|
23
|
+
amount?: string;
|
|
24
|
+
condition: string;
|
|
25
|
+
}>;
|
|
26
|
+
risk: Risk;
|
|
27
|
+
arguments: Record<string, string | number | boolean>;
|
|
28
|
+
digest: string;
|
|
29
|
+
}
|
|
30
|
+
export interface BuiltTransaction {
|
|
31
|
+
unsigned_tx: string;
|
|
32
|
+
transaction_intent: TransactionIntent;
|
|
33
|
+
}
|
|
34
|
+
export interface CommonBuild {
|
|
35
|
+
action: Action;
|
|
36
|
+
wallet: string;
|
|
37
|
+
network: Network;
|
|
38
|
+
recentBlockhash: string;
|
|
39
|
+
version?: TransactionVersion;
|
|
40
|
+
}
|
|
41
|
+
export interface CreateBuild extends CommonBuild {
|
|
42
|
+
action: "create";
|
|
43
|
+
nonce: string;
|
|
44
|
+
escrowLamports: string;
|
|
45
|
+
contentHash: string;
|
|
46
|
+
deadline: string;
|
|
47
|
+
submitMargin: string;
|
|
48
|
+
claimBuffer: string;
|
|
49
|
+
platform: number;
|
|
50
|
+
attestationDelayOverride: number;
|
|
51
|
+
challengeWindowOverride: number;
|
|
52
|
+
verificationTimeoutOverride: number;
|
|
53
|
+
requiresApproval: boolean;
|
|
54
|
+
verificationKind: number;
|
|
55
|
+
}
|
|
56
|
+
export interface ClaimBuild extends CommonBuild {
|
|
57
|
+
action: "claim";
|
|
58
|
+
taskPda: string;
|
|
59
|
+
sponsor?: string;
|
|
60
|
+
/** Authorized repayment destination when sponsorship has an open advance. */
|
|
61
|
+
payoutTo?: string;
|
|
62
|
+
}
|
|
63
|
+
export interface ApproveBuild extends CommonBuild {
|
|
64
|
+
action: "approve";
|
|
65
|
+
taskPda: string;
|
|
66
|
+
}
|
|
67
|
+
export interface SubmitBuild extends CommonBuild {
|
|
68
|
+
action: "submit";
|
|
69
|
+
taskPda: string;
|
|
70
|
+
contentId: string;
|
|
71
|
+
sponsor?: string;
|
|
72
|
+
}
|
|
73
|
+
export interface VerifyBuild extends CommonBuild {
|
|
74
|
+
action: "verify";
|
|
75
|
+
taskPda: string;
|
|
76
|
+
switchboardFeed: string;
|
|
77
|
+
compositeScore: string;
|
|
78
|
+
verificationHash: string;
|
|
79
|
+
/** The exact Switchboard crank instructions obtained from its gateway SDK. */
|
|
80
|
+
crankInstructions: TransactionInstruction[];
|
|
81
|
+
}
|
|
82
|
+
export interface FinalizeBuild extends CommonBuild {
|
|
83
|
+
action: "finalize";
|
|
84
|
+
taskPda: string;
|
|
85
|
+
agent: string;
|
|
86
|
+
client: string;
|
|
87
|
+
treasury: string;
|
|
88
|
+
}
|
|
89
|
+
export type BuildRequest = CreateBuild | ClaimBuild | ApproveBuild | SubmitBuild | VerifyBuild | FinalizeBuild;
|
|
90
|
+
export declare function buildInstruction(request: BuildRequest): TransactionInstruction;
|
|
91
|
+
export declare function intentDigest(intent: Omit<TransactionIntent, "digest">): string;
|
|
92
|
+
export declare function buildTransaction(request: BuildRequest): BuiltTransaction;
|
|
93
|
+
export interface TransactionInspection {
|
|
94
|
+
version: TransactionVersion;
|
|
95
|
+
fee_payer: string;
|
|
96
|
+
signers: string[];
|
|
97
|
+
instructions: Array<{
|
|
98
|
+
program_id: string;
|
|
99
|
+
accounts: string[];
|
|
100
|
+
data_base64: string;
|
|
101
|
+
}>;
|
|
102
|
+
}
|
|
103
|
+
export declare function inspectTransaction(encoded: string): TransactionInspection;
|
|
104
|
+
export declare function verifyIntent(encoded: string, intent: TransactionIntent): TransactionInspection;
|
|
105
|
+
export type WalletSignCallback = (transaction: Transaction | VersionedTransaction) => Promise<Transaction | VersionedTransaction>;
|
|
106
|
+
export declare function signAndBroadcast(connection: Connection, encodedUnsigned: string, sign: WalletSignCallback, confirmation?: BlockhashWithExpiryBlockHeight): Promise<string>;
|
|
107
|
+
export declare const buildCreate: (request: Omit<CreateBuild, "action">) => BuiltTransaction;
|
|
108
|
+
export declare const buildClaim: (request: Omit<ClaimBuild, "action">) => BuiltTransaction;
|
|
109
|
+
export declare const buildSubmit: (request: Omit<SubmitBuild, "action">) => BuiltTransaction;
|
|
110
|
+
export declare const buildApprove: (request: Omit<ApproveBuild, "action">) => BuiltTransaction;
|
|
111
|
+
export declare const buildVerify: (request: Omit<VerifyBuild, "action">) => BuiltTransaction;
|
|
112
|
+
export declare const buildFinalize: (request: Omit<FinalizeBuild, "action">) => BuiltTransaction;
|
|
113
|
+
export { buildSwitchboardCrankAndVerify } from "./switchboard.js";
|
|
114
|
+
export type { SwitchboardVerifyRequest } from "./switchboard.js";
|
|
115
|
+
export * from "./payout-oracle.js";
|