suprafx-agent-sdk 0.3.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.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +452 -0
  3. package/dist/bin/suprafx-mcp.d.ts +15 -0
  4. package/dist/bin/suprafx-mcp.js +238 -0
  5. package/dist/bin/suprafx-mcp.js.map +1 -0
  6. package/dist/src/asset-registry.d.ts +61 -0
  7. package/dist/src/asset-registry.js +118 -0
  8. package/dist/src/asset-registry.js.map +1 -0
  9. package/dist/src/client.d.ts +227 -0
  10. package/dist/src/client.js +282 -0
  11. package/dist/src/client.js.map +1 -0
  12. package/dist/src/derive-ids.d.ts +112 -0
  13. package/dist/src/derive-ids.js +361 -0
  14. package/dist/src/derive-ids.js.map +1 -0
  15. package/dist/src/event-bcs.d.ts +341 -0
  16. package/dist/src/event-bcs.js +767 -0
  17. package/dist/src/event-bcs.js.map +1 -0
  18. package/dist/src/index.d.ts +26 -0
  19. package/dist/src/index.js +26 -0
  20. package/dist/src/index.js.map +1 -0
  21. package/dist/src/mcp/config.d.ts +32 -0
  22. package/dist/src/mcp/config.js +101 -0
  23. package/dist/src/mcp/config.js.map +1 -0
  24. package/dist/src/mcp/lifecycle.d.ts +109 -0
  25. package/dist/src/mcp/lifecycle.js +170 -0
  26. package/dist/src/mcp/lifecycle.js.map +1 -0
  27. package/dist/src/mcp/preflight.d.ts +36 -0
  28. package/dist/src/mcp/preflight.js +291 -0
  29. package/dist/src/mcp/preflight.js.map +1 -0
  30. package/dist/src/mcp/server.d.ts +20 -0
  31. package/dist/src/mcp/server.js +235 -0
  32. package/dist/src/mcp/server.js.map +1 -0
  33. package/dist/src/mcp/tools.d.ts +51 -0
  34. package/dist/src/mcp/tools.js +1022 -0
  35. package/dist/src/mcp/tools.js.map +1 -0
  36. package/dist/src/sign-event.d.ts +185 -0
  37. package/dist/src/sign-event.js +331 -0
  38. package/dist/src/sign-event.js.map +1 -0
  39. package/dist/src/signer.d.ts +89 -0
  40. package/dist/src/signer.js +226 -0
  41. package/dist/src/signer.js.map +1 -0
  42. package/package.json +65 -0
@@ -0,0 +1,341 @@
1
+ /**
2
+ * BCS encoders for user-originated `Event` variants.
3
+ *
4
+ * Mirrors the Rust struct schemas in
5
+ * `council-rust/crates/protocol/src/events.rs` byte-for-byte.
6
+ * Cross-language fixture
7
+ * (`lib/council/__fixtures__/user-event-envelope-fixtures.json`)
8
+ * proves equivalence; any drift is caught by
9
+ * `tests/unit/council-event-bcs.test.ts`.
10
+ *
11
+ * ## Why we encode client-side (not server-side)
12
+ *
13
+ * The user signs `event_bcs` directly. If the server encoded those
14
+ * bytes, a malicious or compromised server could mutate the event
15
+ * (UI shows "Withdraw 100 USDC", server encodes "Withdraw 1M USDC")
16
+ * and the user's wallet would sign the malicious bytes without the
17
+ * user knowing — chain accepts the sig as valid because the math
18
+ * works. Client-side encoding makes the bytes-the-user-signs equal
19
+ * the bytes-the-UI-rendered, since both come from the same auditable
20
+ * open-source TS code.
21
+ *
22
+ * Standard practice — Aptos, Sui, Solana, Ethereum all encode user
23
+ * transactions client-side for the same reason.
24
+ *
25
+ * ## Variant index (must match Rust `Event` enum order)
26
+ *
27
+ * BCS encodes a Rust enum as `ULEB128(discriminant) + fields`. The
28
+ * discriminant is the variant's declaration order in the enum:
29
+ *
30
+ * 0 DepositCredited (validator-attested; not signed by user)
31
+ * 1 WithdrawRequested ← user
32
+ * 2 WithdrawFinalized (validator-attested)
33
+ * 3 WithdrawCancelled (lifecycle)
34
+ * 4 SubmitRfq ← user
35
+ * 5 CancelRfq ← user
36
+ * 6 PlaceQuote ← user (maker)
37
+ * 7 AcceptQuote ← user (taker)
38
+ * 8 WithdrawQuote ← user (maker)
39
+ * 9 SlashMaker (lifecycle)
40
+ * 10..19 validator/governance
41
+ * 20 DelegatePolicyCreated ← user (master)
42
+ * 21 DelegatePolicyUpdated ← user (master)
43
+ * 22 DelegatePolicyRevoked ← user (master)
44
+ * 23..30 other (not user-signed)
45
+ *
46
+ * If any of these indices change in Rust, the cross-language fixture
47
+ * will fail at the next `cargo run --example
48
+ * gen_user_event_envelope_fixture` and the TS test will catch it.
49
+ *
50
+ * ## Type sizes (locked at the protocol level)
51
+ *
52
+ * AccountId [u8; 32]
53
+ * AssetId [u8; 32]
54
+ * PairId [u8; 32]
55
+ * RfqId [u8; 16]
56
+ * QuoteId [u8; 16]
57
+ * TradeId [u8; 16]
58
+ * Hash [u8; 32]
59
+ * Amount u128 (16 LE bytes; BCS does NOT use ULEB128 for u128)
60
+ * SequenceNumber u64 (8 LE bytes)
61
+ */
62
+ /** Settlement mode: Platform=0, OnChain=1. Matches Rust `SettlementMode`. */
63
+ export type SettlementMode = "Platform" | "OnChain";
64
+ /** Delegate role: Maker=0, Taker=1, Agent=2. Matches Rust `DelegateRole`. */
65
+ export type DelegateRole = "Maker" | "Taker" | "Agent";
66
+ /**
67
+ * `SubmitRfq` payload. Field order MUST match
68
+ * `council_protocol::events::SubmitRfq` struct declaration.
69
+ */
70
+ /**
71
+ * `DepositCredited` payload — validator-attested L1-deposit credit.
72
+ * Layout matches Rust `council_protocol::events::DepositCredited`
73
+ * field order; BCS is positional so any reorder there must mirror
74
+ * here or the cross-language fixture round-trip breaks.
75
+ */
76
+ export interface DepositCreditedEvent {
77
+ user: Uint8Array;
78
+ asset: Uint8Array;
79
+ amount: bigint;
80
+ source_chain: string;
81
+ source_tx: Uint8Array;
82
+ deposit_id: bigint;
83
+ source_block: bigint;
84
+ attesting_validators: Uint8Array[];
85
+ attestations: Uint8Array[];
86
+ }
87
+ export interface SubmitRfqEvent {
88
+ user: Uint8Array;
89
+ pair: Uint8Array;
90
+ base_asset: Uint8Array;
91
+ quote_asset: Uint8Array;
92
+ size: bigint;
93
+ reference_price: bigint;
94
+ auto_accept: boolean;
95
+ auto_accept_target_rate: bigint;
96
+ allow_partial_fills: boolean;
97
+ min_fill_size: bigint;
98
+ expires_at_ms: bigint;
99
+ rfq_id: Uint8Array;
100
+ user_sequence_number: bigint;
101
+ settlement_mode: SettlementMode;
102
+ }
103
+ /** `CancelRfq` payload. */
104
+ export interface CancelRfqEvent {
105
+ user: Uint8Array;
106
+ rfq_id: Uint8Array;
107
+ reason: string;
108
+ }
109
+ /** `PlaceQuote` payload. Note: `maker`, NOT `user`. */
110
+ export interface PlaceQuoteEvent {
111
+ maker: Uint8Array;
112
+ rfq_id: Uint8Array;
113
+ quote_id: Uint8Array;
114
+ rate: bigint;
115
+ fill_size: bigint;
116
+ user_sequence_number: bigint;
117
+ }
118
+ /** `AcceptQuote` payload. Note: `taker` field, plus `trade_id` (NOT `rfq_id`). */
119
+ export interface AcceptQuoteEvent {
120
+ taker: Uint8Array;
121
+ quote_id: Uint8Array;
122
+ trade_id: Uint8Array;
123
+ user_sequence_number: bigint;
124
+ }
125
+ /** `WithdrawQuote` payload. */
126
+ export interface WithdrawQuoteEvent {
127
+ maker: Uint8Array;
128
+ quote_id: Uint8Array;
129
+ }
130
+ /** `WithdrawRequested` payload. */
131
+ export interface WithdrawRequestedEvent {
132
+ user: Uint8Array;
133
+ asset: Uint8Array;
134
+ amount: bigint;
135
+ /**
136
+ * AssetId the protocol withdrawal fee is paid in. v1: always SUPRA
137
+ * (matches the Rust constant in `protocol/src/fees.rs`). Future
138
+ * USD-pegged variants may switch per chain config.
139
+ *
140
+ * Wire position: AFTER `amount`, BEFORE `withdrawal_id` — must
141
+ * match Rust `events::WithdrawRequested` exactly. Existing BCS
142
+ * fixtures from before this field was added will fail to decode
143
+ * (the explorer reader returns null for them, which the renderer
144
+ * already handles as "unknown event").
145
+ */
146
+ fee_asset: Uint8Array;
147
+ /**
148
+ * Fee amount in `fee_asset` micro-units. dApp computes via
149
+ * `computeWithdrawalFee(amount, asset)`; validator re-computes
150
+ * and rejects on mismatch.
151
+ */
152
+ fee_amount: bigint;
153
+ withdrawal_id: bigint;
154
+ dest_chain: string;
155
+ dest_address: Uint8Array;
156
+ deadline_ms: bigint;
157
+ user_sequence_number: bigint;
158
+ }
159
+ /**
160
+ * One entry of a delegate's per-asset cap map (Option B). Mirrors the
161
+ * Rust `policy::AssetCap` keyed by its `AssetId`.
162
+ *
163
+ * ═══ ZERO SEMANTICS — VERIFIED AGAINST THE CONTRACT, 2026-09-15 ═══
164
+ *
165
+ * `0n` means **NO TRADES ALLOWED** for this asset (fail-closed).
166
+ * It does NOT mean "unlimited".
167
+ * An asset with NO ENTRY is also not tradeable (deny-by-absence),
168
+ * and an empty map authorizes nothing.
169
+ * For "effectively unlimited", use `MAX_CAP` (u64::MAX) below.
170
+ *
171
+ * The validator ORIGINALLY treated `0` as unlimited — fail-OPEN, a
172
+ * whitehat confirmed it exploitable, and it was fixed in
173
+ * `council-rust/crates/protocol/src/policy.rs` on 2026-06-07. This
174
+ * comment kept saying "0n = authorized, unlimited" long after the
175
+ * contract stopped behaving that way, and an onboarding review read it
176
+ * and concluded a zero cap grants unlimited authority. It does the
177
+ * opposite. Keep this comment in step with the contract.
178
+ */
179
+ export interface AssetCapEntry {
180
+ /** 32-byte AssetId. */
181
+ asset: Uint8Array;
182
+ /**
183
+ * Per-trade cap (Amount, micro-units). `0n` = NO TRADES for this
184
+ * asset. Use `MAX_CAP` for effectively unlimited.
185
+ */
186
+ maxTradeSize: bigint;
187
+ /** Cumulative open-earmark cap. Same zero semantics. */
188
+ maxEarmarkTotal: bigint;
189
+ }
190
+ /**
191
+ * The canonical "effectively unlimited" cap sentinel: `u64::MAX`.
192
+ *
193
+ * NOT `u128::MAX` — the validator's `checked_add(earmarks, size)`
194
+ * overflows there. NOT `0n`, which is fail-closed (see above).
195
+ */
196
+ export declare const MAX_CAP: bigint;
197
+ /** `DelegatePolicyCreated` payload. Used for session bootstrap. */
198
+ export interface DelegatePolicyCreatedEvent {
199
+ master: Uint8Array;
200
+ delegate: Uint8Array;
201
+ label: string;
202
+ allowed_roles: DelegateRole[];
203
+ allowed_pairs: Uint8Array[];
204
+ /**
205
+ * Per-asset caps — a BCS `BTreeMap<AssetId, AssetCap>`. DENY
206
+ * semantics: an asset absent from this list is not tradeable.
207
+ * See `docs/handoffs/option-b-per-asset-caps-design.md`.
208
+ */
209
+ asset_caps: AssetCapEntry[];
210
+ /**
211
+ * Batch number at which this delegation EXPIRES — the on-chain
212
+ * session bound (#25 H10). `0n` = never expires. Once consensus
213
+ * reaches this batch, `check_delegate_policy` rejects every
214
+ * delegated action, so a leaked delegate key is time-bounded even
215
+ * if the master never submits a revoke. Carried in the signed
216
+ * envelope so the master cryptographically commits to the lifetime.
217
+ * Wire position: AFTER `asset_caps`, BEFORE `user_sequence_number`
218
+ * — must match Rust `events::DelegatePolicyCreated` exactly.
219
+ */
220
+ expires_at_batch: bigint;
221
+ user_sequence_number: bigint;
222
+ }
223
+ /** Taker on-chain settlement claim. Bumps taker's sequence number;
224
+ * no balance change (claim only — bridge attesters verify later). */
225
+ export interface TakerTxPendingEvent {
226
+ trade_id: Uint8Array;
227
+ taker: Uint8Array;
228
+ source_chain: string;
229
+ source_tx: Uint8Array;
230
+ user_sequence_number: bigint;
231
+ }
232
+ /** Maker on-chain settlement claim. Bumps maker's sequence number. */
233
+ export interface MakerTxPendingEvent {
234
+ trade_id: Uint8Array;
235
+ maker: Uint8Array;
236
+ source_chain: string;
237
+ source_tx: Uint8Array;
238
+ user_sequence_number: bigint;
239
+ }
240
+ /** Master-signed delegate-policy update. Same shape as Created plus
241
+ * an `active` flag inserted between max_earmark_total + sequence. */
242
+ export interface DelegatePolicyUpdatedEvent {
243
+ master: Uint8Array;
244
+ delegate: Uint8Array;
245
+ label: string;
246
+ allowed_roles: DelegateRole[];
247
+ allowed_pairs: Uint8Array[];
248
+ /** Per-asset caps — see `AssetCapEntry`. BCS `BTreeMap<AssetId, AssetCap>`. */
249
+ asset_caps: AssetCapEntry[];
250
+ active: boolean;
251
+ user_sequence_number: bigint;
252
+ }
253
+ /** Master-signed delegate-policy revocation. Removes the policy
254
+ * entirely; in-flight earmarks are NOT auto-cleared. */
255
+ export interface DelegatePolicyRevokedEvent {
256
+ master: Uint8Array;
257
+ delegate: Uint8Array;
258
+ user_sequence_number: bigint;
259
+ }
260
+ /** Master-signed link revocation. Removes (chain, address) from the
261
+ * user's linked_addresses + clears the address_to_master reverse index. */
262
+ export interface LinkedAddressRevokedEvent {
263
+ /** Master account (32 bytes). */
264
+ user: Uint8Array;
265
+ /** Foreign chain id ("eth-sepolia", "supra-testnet", ...). */
266
+ chain: string;
267
+ /** Foreign address bytes (variable length per chain). */
268
+ address: Uint8Array;
269
+ /** Strict-next sequence number for the master. */
270
+ user_sequence_number: bigint;
271
+ }
272
+ /**
273
+ * Tagged-union shape for the user-event encoder. Each `kind` maps
274
+ * 1-1 to a Rust `Event` variant we support; the discriminator
275
+ * determines the BCS variant tag and which struct schema is used
276
+ * for the body.
277
+ */
278
+ export type UserEvent = {
279
+ kind: "DepositCredited";
280
+ payload: DepositCreditedEvent;
281
+ } | {
282
+ kind: "SubmitRfq";
283
+ payload: SubmitRfqEvent;
284
+ } | {
285
+ kind: "CancelRfq";
286
+ payload: CancelRfqEvent;
287
+ } | {
288
+ kind: "PlaceQuote";
289
+ payload: PlaceQuoteEvent;
290
+ } | {
291
+ kind: "AcceptQuote";
292
+ payload: AcceptQuoteEvent;
293
+ } | {
294
+ kind: "WithdrawQuote";
295
+ payload: WithdrawQuoteEvent;
296
+ } | {
297
+ kind: "WithdrawRequested";
298
+ payload: WithdrawRequestedEvent;
299
+ } | {
300
+ kind: "TakerTxPending";
301
+ payload: TakerTxPendingEvent;
302
+ } | {
303
+ kind: "MakerTxPending";
304
+ payload: MakerTxPendingEvent;
305
+ } | {
306
+ kind: "DelegatePolicyCreated";
307
+ payload: DelegatePolicyCreatedEvent;
308
+ } | {
309
+ kind: "DelegatePolicyUpdated";
310
+ payload: DelegatePolicyUpdatedEvent;
311
+ } | {
312
+ kind: "DelegatePolicyRevoked";
313
+ payload: DelegatePolicyRevokedEvent;
314
+ } | {
315
+ kind: "LinkedAddressRevoked";
316
+ payload: LinkedAddressRevokedEvent;
317
+ };
318
+ /**
319
+ * BCS-encode a user-originated `Event`. Returns the bytes the user
320
+ * signs (becomes `event_bcs` in the `SignedEventEnvelope`).
321
+ *
322
+ * Pure function. Same input → same bytes, every time.
323
+ */
324
+ export declare function encodeUserEvent(ev: UserEvent): Uint8Array;
325
+ /**
326
+ * Decode a BCS-encoded `Event` into the corresponding `UserEvent`
327
+ * variant. Reads the variant tag (ULEB128) from the front of the
328
+ * buffer + dispatches.
329
+ *
330
+ * Returns `null` for variants this module doesn't know about
331
+ * (validator-attested events like `DepositCredited`, governance, etc.) —
332
+ * callers should treat that as "decoding deferred to a future
333
+ * version" rather than an error.
334
+ *
335
+ * Throws on truly malformed BCS bytes (truncated, invalid bools, etc).
336
+ *
337
+ * After decoding, `reader.remaining()` should be 0; if not, the
338
+ * payload had trailing bytes (caller-supplied buffer too long, or
339
+ * a struct-field-mismatch bug).
340
+ */
341
+ export declare function decodeUserEvent(bcs: Uint8Array): UserEvent | null;