sui.ski 0.1.0
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/AGENTS.md +311 -0
- package/CLAUDE.md +292 -0
- package/CODEBASE_GUIDE.md +217 -0
- package/README.md +77 -0
- package/biome.json +28 -0
- package/package.json +73 -0
- package/scripts/deploy-messaging-mainnet.sh +184 -0
- package/scripts/extract-suins-object.ts +180 -0
- package/scripts/full-deploy.sh +26 -0
- package/scripts/obsidian.ts +243 -0
- package/scripts/set-suins-contenthash.ts +130 -0
- package/scripts/setup-ika-dwallet.ts +338 -0
- package/scripts/transfer-upgrade-cap-from-nft.ts +86 -0
- package/src/durable-objects/wallet-session.ts +333 -0
- package/src/handlers/app.ts +1430 -0
- package/src/handlers/authenticated-events.ts +267 -0
- package/src/handlers/dashboard.ts +1659 -0
- package/src/handlers/landing.ts +6751 -0
- package/src/handlers/mcp.ts +556 -0
- package/src/handlers/messaging-sdk.ts +220 -0
- package/src/handlers/profile.css.ts +9332 -0
- package/src/handlers/profile.ts +12640 -0
- package/src/handlers/register2.ts +2811 -0
- package/src/handlers/ski-sign.ts +1901 -0
- package/src/handlers/ski.ts +314 -0
- package/src/handlers/thunder.ts +940 -0
- package/src/handlers/vault.ts +284 -0
- package/src/handlers/wallet-api.ts +169 -0
- package/src/handlers/x402-register.ts +601 -0
- package/src/index.test.ts +55 -0
- package/src/index.ts +512 -0
- package/src/resolvers/content.ts +231 -0
- package/src/resolvers/rpc.ts +222 -0
- package/src/resolvers/suins.ts +266 -0
- package/src/sdk/messaging.ts +279 -0
- package/src/types.ts +230 -0
- package/src/utils/agent-keypair.ts +40 -0
- package/src/utils/authenticated-events.ts +280 -0
- package/src/utils/cache.ts +82 -0
- package/src/utils/media-pack.ts +27 -0
- package/src/utils/mmr.ts +181 -0
- package/src/utils/ns-price.ts +529 -0
- package/src/utils/og-image.ts +141 -0
- package/src/utils/onchain-activity.ts +211 -0
- package/src/utils/onchain-listing.ts +39 -0
- package/src/utils/premium.ts +29 -0
- package/src/utils/pricing.ts +291 -0
- package/src/utils/pyth-price-info.ts +63 -0
- package/src/utils/response.ts +204 -0
- package/src/utils/rpc.ts +25 -0
- package/src/utils/shared-wallet-js.ts +166 -0
- package/src/utils/social.ts +152 -0
- package/src/utils/status.ts +39 -0
- package/src/utils/subdomain.ts +116 -0
- package/src/utils/surflux-grpc.ts +241 -0
- package/src/utils/swap-transactions.ts +1222 -0
- package/src/utils/thunder-css.ts +1341 -0
- package/src/utils/thunder-js.ts +5046 -0
- package/src/utils/transactions.ts +65 -0
- package/src/utils/vault.ts +18 -0
- package/src/utils/wallet-kit-js.ts +2312 -0
- package/src/utils/wallet-session-js.ts +192 -0
- package/src/utils/wallet-tx-js.ts +2287 -0
- package/src/utils/wallet-ui-js.ts +3057 -0
- package/src/utils/x402-middleware.ts +428 -0
- package/src/utils/x402-sui.ts +171 -0
- package/src/utils/zksend-js.ts +166 -0
- package/tsconfig.json +22 -0
- package/workers/x402-multichain/src/index.ts +237 -0
- package/workers/x402-multichain/src/types.ts +80 -0
- package/workers/x402-multichain/tsconfig.json +20 -0
- package/workers/x402-multichain/wrangler.toml +11 -0
- package/wrangler.toml +84 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# Agent Guidelines for Sui-ski Gateway
|
|
2
|
+
|
|
3
|
+
## Thunder-First Development
|
|
4
|
+
|
|
5
|
+
Every feature touches Thunder. The `#primary` channel is the source of truth for a user's on-chain activity through the gateway. If you're building a new action (registration, swap, transfer, etc.), it MUST emit a Thunder message.
|
|
6
|
+
|
|
7
|
+
**Core principle:** The WebMCP format ensures all messages are machine-readable. AI agents can parse the `#primary` channel to reconstruct a user's complete activity history.
|
|
8
|
+
|
|
9
|
+
**When building any PTB-producing feature:**
|
|
10
|
+
1. Define the `ThunderAction` for the action (tool name, input fields)
|
|
11
|
+
2. Call `appendThunderMessage()` as the last step in PTB construction
|
|
12
|
+
3. Account for ~0.001 SUI gas overhead from the message call
|
|
13
|
+
4. Handle the no-channel case (first action triggers channel creation instead)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Core Architecture
|
|
18
|
+
|
|
19
|
+
### Subdomain Routing (`src/utils/subdomain.ts`)
|
|
20
|
+
|
|
21
|
+
| Pattern | Route | Handler |
|
|
22
|
+
| ------- | ----- | ------- |
|
|
23
|
+
| `sui.ski` | root | Landing page, API routes |
|
|
24
|
+
| `my.sui.ski` | dashboard | User's names management |
|
|
25
|
+
| `rpc.sui.ski` | rpc | Read-only JSON-RPC proxy |
|
|
26
|
+
| `{name}.sui.ski` | suins | SuiNS profile + Thunder feed |
|
|
27
|
+
| `{pkg}--{name}.sui.ski` | mvr | MVR package resolution |
|
|
28
|
+
| `ipfs-{cid}.sui.ski` | content | IPFS gateway |
|
|
29
|
+
| `walrus-{blobId}.sui.ski` | content | Walrus aggregator |
|
|
30
|
+
| `app.sui.ski` | app | Messaging/chat application |
|
|
31
|
+
| `.t.` prefix | testnet | Override to testnet |
|
|
32
|
+
| `.d.` prefix | devnet | Override to devnet |
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Wallet Bridge Policy
|
|
37
|
+
|
|
38
|
+
All wallet interaction on any `*.sui.ski` subdomain MUST be routed through `https://sui.ski/sign` using an invisible iframe bridge.
|
|
39
|
+
|
|
40
|
+
- Subdomains may perform local wallet discovery only to collect wallet name/icon hints.
|
|
41
|
+
- Subdomains MUST forward those hints to the bridge (`postMessage`) and let the bridge own connect/sign/disconnect.
|
|
42
|
+
- Subdomains MUST NOT directly call extension connect/sign APIs for trad wallets.
|
|
43
|
+
- New wallet flows must keep the bridge path as the only signing authority outside `sui.ski`.
|
|
44
|
+
- **Solution: If a wallet is unavailable in iframe context, use a same-tab top-frame handoff to `https://sui.ski/sign` for connect/sign and then return to the subdomain. Do not use popup fallback.**
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Thunder Integration Points
|
|
49
|
+
|
|
50
|
+
| File | Thunder Role |
|
|
51
|
+
| ---- | ------------ |
|
|
52
|
+
| `src/handlers/ski-sign.ts` | Appends Thunder message to every PTB before signing |
|
|
53
|
+
| `src/handlers/ski.ts` | Triggers `#primary` channel creation on first .SKI |
|
|
54
|
+
| `src/durable-objects/wallet-session.ts` | Stores `channelId` + `memberCapId` in session |
|
|
55
|
+
| `src/handlers/thunder.ts` | Reads `#primary` channel for activity display |
|
|
56
|
+
| `src/handlers/profile.ts` | Renders Thunder activity feed on profile pages |
|
|
57
|
+
| `src/utils/thunder.ts` | **(NEW)** Thunder message builder + PTB composer |
|
|
58
|
+
| `contracts/storm/sources/registry.move` | Storm on-chain canonical channel registry (`SuiNS NFT -> channel`) |
|
|
59
|
+
| `src/handlers/app.ts` | Exposes Storm config at `/api/app/subscriptions/config` |
|
|
60
|
+
| `src/utils/thunder-js.ts` | Reads/writes Storm mapping; enforces primary channel identity |
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## PTB Composition Checklist
|
|
65
|
+
|
|
66
|
+
When adding or modifying any PTB that goes through `ski-sign.ts`:
|
|
67
|
+
|
|
68
|
+
- [ ] Define the `ThunderAction` with correct `tool` name (`sui:` prefix) and `input` fields
|
|
69
|
+
- [ ] Serialize the action to the WebMCP envelope format (`v: 1`, `method: 'tools/call'`)
|
|
70
|
+
- [ ] Encrypt the serialized bytes with the channel's DEK via Seal
|
|
71
|
+
- [ ] Append `channel::send_message()` as the **last** MoveCall in the PTB
|
|
72
|
+
- [ ] Verify the PTB handles the no-channel edge case (first .SKI creates channel instead)
|
|
73
|
+
- [ ] Test that gas budget includes the message overhead (~0.001 SUI)
|
|
74
|
+
- [ ] Confirm atomicity: action + log both succeed or both revert
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## WebMCP Message Catalog
|
|
79
|
+
|
|
80
|
+
All known Thunder action types:
|
|
81
|
+
|
|
82
|
+
| Tool | Trigger | Input Fields |
|
|
83
|
+
| ---- | ------- | ------------ |
|
|
84
|
+
| `sui:ski` | First key-in | `name`, `address` |
|
|
85
|
+
| `sui:register` | SuiNS registration | `name`, `years`, `paymentMethod` |
|
|
86
|
+
| `sui:transfer` | Object transfer | `objectId`, `recipient` |
|
|
87
|
+
| `sui:swap` | DeepBook swap | `fromCoin`, `toCoin`, `amount` |
|
|
88
|
+
| `sui:stake` | Validator staking | `validator`, `amount` |
|
|
89
|
+
| `sui:message` | Direct message send | `channel`, `recipient` |
|
|
90
|
+
|
|
91
|
+
When adding a new action type: add a row here, implement the `ThunderAction` in `src/utils/thunder.ts`, and wire it into the relevant handler's PTB construction.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Deployment
|
|
96
|
+
|
|
97
|
+
**CRITICAL: Use `npx wrangler deploy` after every change.**
|
|
98
|
+
|
|
99
|
+
Do not use `bun run deploy` (fails with auth token errors). Do not consider work complete until deployment succeeds.
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
npx wrangler deploy # Deploy (MANDATORY after changes)
|
|
103
|
+
bun run dev # Local dev server
|
|
104
|
+
bun test # Run tests
|
|
105
|
+
bun run typecheck # Type checking
|
|
106
|
+
bun run lint # Biome linter
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Code Style
|
|
112
|
+
|
|
113
|
+
Full standards in `CLAUDE.md`. Key points:
|
|
114
|
+
|
|
115
|
+
- **No comments** — self-documenting code
|
|
116
|
+
- **No dead code** — git has history
|
|
117
|
+
- **Fail fast** — `invariant(condition, actionable message)`
|
|
118
|
+
- **Parallel async** — `Promise.all` for independent operations
|
|
119
|
+
- **Single-pass** — avoid chained array methods
|
|
120
|
+
- Naming: constants `UPPER_SNAKE`, functions `camelCase`, types `PascalCase`, files `kebab-case`
|
|
121
|
+
- Formatting: tabs, single quotes, no semicolons
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Key Files
|
|
126
|
+
|
|
127
|
+
### Thunder Path (action flow)
|
|
128
|
+
|
|
129
|
+
| File | Purpose |
|
|
130
|
+
| ---- | ------- |
|
|
131
|
+
| `src/handlers/ski.ts` | .SKI key-in entry point, wallet connection |
|
|
132
|
+
| `src/handlers/ski-sign.ts` | PTB signing bridge, Thunder message injection |
|
|
133
|
+
| `src/handlers/thunder.ts` | Thunder API, MCP bridge, channel/message endpoints |
|
|
134
|
+
| `src/utils/thunder-js.ts` | Thunder chat runtime UI + SDK transaction orchestration |
|
|
135
|
+
| `src/utils/thunder-css.ts` | Thunder UI styling |
|
|
136
|
+
| `src/durable-objects/wallet-session.ts` | Session state: address, channel/member caps, auth |
|
|
137
|
+
|
|
138
|
+
### Transaction Building
|
|
139
|
+
|
|
140
|
+
| File | Purpose |
|
|
141
|
+
| ---- | ------- |
|
|
142
|
+
| `src/utils/swap-transactions.ts` | DeepBook swap + SuiNS registration PTBs |
|
|
143
|
+
| `src/utils/transactions.ts` | Shared transaction helpers |
|
|
144
|
+
| `src/utils/ns-price.ts` | NS token pricing via DeepBook pools |
|
|
145
|
+
| `src/utils/pricing.ts` | SuiNS pricing + renewal calculations |
|
|
146
|
+
| `src/utils/grace-vault-transactions.ts` | Grace Vault transaction assembly |
|
|
147
|
+
|
|
148
|
+
### Messaging + App Surfaces
|
|
149
|
+
|
|
150
|
+
| File | Purpose |
|
|
151
|
+
| ---- | ------- |
|
|
152
|
+
| `src/handlers/app.ts` | `app.sui.ski` shell + `/api/app/*` config/messaging helpers |
|
|
153
|
+
| `src/handlers/profile.ts` | Profile page rendering + Thunder embed |
|
|
154
|
+
| `src/handlers/messaging-sdk.ts` | Messaging SDK status/config API |
|
|
155
|
+
| `src/sdk/messaging.ts` | Shared SDK constants/config/version pins |
|
|
156
|
+
|
|
157
|
+
### Resolution Layer
|
|
158
|
+
|
|
159
|
+
| File | Purpose |
|
|
160
|
+
| ---- | ------- |
|
|
161
|
+
| `src/resolvers/suins.ts` | SuiNS resolution |
|
|
162
|
+
| `src/resolvers/content.ts` | IPFS/Walrus content resolution |
|
|
163
|
+
| `src/resolvers/rpc.ts` | Read-only RPC proxy |
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Current Progress Log (2026-02-17)
|
|
168
|
+
|
|
169
|
+
### Thunder stability and UX
|
|
170
|
+
|
|
171
|
+
- Done (2026-02-18): Enforced bridge-only wallet interaction on subdomains (`sui.ski/sign` iframe) and added local wallet hint forwarding from subdomains to bridge discovery/connect/sign paths.
|
|
172
|
+
- Done: Robust Sui + messaging SDK module resolution in browser init path.
|
|
173
|
+
- Done: Fixed package-ID mismatch to mainnet messaging package `0xbcdf...39f9` in live runtime/config paths.
|
|
174
|
+
- Done: Added signer result hydration to recover missing transaction effects/object changes.
|
|
175
|
+
- Done: Removed channel-label collapsing that mixed independent channels under one label.
|
|
176
|
+
- Done: Added local cache reset control for channel meta/dismissed keys.
|
|
177
|
+
|
|
178
|
+
### Hardening completed
|
|
179
|
+
|
|
180
|
+
- Done: Duplicate-channel prevention at source.
|
|
181
|
+
- Added on-chain membership guard before any bootstrap/create flow, so existing membership blocks new channel creation.
|
|
182
|
+
- Removed “dismiss on failed burn” behavior to prevent hidden channels from spawning replacement channels.
|
|
183
|
+
- Shifted duplicate channels into explicit “extras” management UI, with on-chain cleanup action.
|
|
184
|
+
- Done: Reduced first-message signing path.
|
|
185
|
+
- Added bootstrap path using `createChannelFlow()` + append `sendMessage()` to key-attach PTB, reducing first send from 3 signatures to 2 when SDK builders are available.
|
|
186
|
+
- Done: Channel-key self-heal for broken legacy channels.
|
|
187
|
+
- Added on-chain `add_encrypted_key` repair flow as an explicit user action (`Repair key`) when signer has required member capability.
|
|
188
|
+
- Added send fallback routing to next public channel with a valid encrypted key when active channel is unrecoverable.
|
|
189
|
+
- Added member-cap permission selection logic so repair uses a cap with `EditEncryptionKey` instead of arbitrary owned caps.
|
|
190
|
+
- Disabled automatic repair during send to keep one send-click to one transaction attempt.
|
|
191
|
+
- Done: Strict send path (no implicit fallback/switching).
|
|
192
|
+
- Removed automatic channel switching during send.
|
|
193
|
+
- Removed “best sendable channel” fallback logic.
|
|
194
|
+
- Added explicit burn wording and scope for non-primary channels only.
|
|
195
|
+
|
|
196
|
+
### Storm registry rollout
|
|
197
|
+
|
|
198
|
+
- Done: Added `contracts/storm` Move package with shared `Registry` dynamic fields keyed by SuiNS NFT object ID.
|
|
199
|
+
- `set_channel_for_nft<T: key>(registry, nft, channel_id)`
|
|
200
|
+
- `clear_channel_for_nft<T: key>(registry, nft)`
|
|
201
|
+
- Done: Added worker env wiring for Storm (`STORM_PACKAGE_ID`, `STORM_REGISTRY_ID`) and included Storm config in `/api/app/subscriptions/config`.
|
|
202
|
+
- Done: Thunder runtime now resolves canonical channel from Storm mapping first and pins primary channel identity to it.
|
|
203
|
+
- Done: Added channel-gear action `Set primary` to update Storm mapping explicitly on-chain.
|
|
204
|
+
- Done: Bootstrap create+attach+send PTB now appends Storm mapping write when configured, so channel creation + first message + canonical registry update are bundled into one signed finalize PTB.
|
|
205
|
+
- Done: Extra-channel burn paths attempt Storm mapping clear when burned channel matches current mapped primary.
|
|
206
|
+
|
|
207
|
+
### Cleanup policy
|
|
208
|
+
|
|
209
|
+
- Rule: “Remove” means on-chain delete (`member_cap::transfer_to_recipient`) whenever capability requirements are met.
|
|
210
|
+
- Rule: If deletion caps are missing, do not silently hide; return explicit undeletable state and recovery guidance.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Full App Surface Map
|
|
215
|
+
|
|
216
|
+
### Entry + routing
|
|
217
|
+
|
|
218
|
+
| File | Surface |
|
|
219
|
+
| ---- | ------- |
|
|
220
|
+
| `src/index.ts` | Global routing, host/subdomain dispatch, API wiring, media/OG routes |
|
|
221
|
+
| `src/types.ts` | Worker env/type contracts |
|
|
222
|
+
| `src/utils/subdomain.ts` | Host parsing + network override logic |
|
|
223
|
+
|
|
224
|
+
### Durable object
|
|
225
|
+
|
|
226
|
+
| File | Surface |
|
|
227
|
+
| ---- | ------- |
|
|
228
|
+
| `src/durable-objects/wallet-session.ts` | Wallet session lifecycle, wallet linkage, messaging caps/session state |
|
|
229
|
+
|
|
230
|
+
### Handlers
|
|
231
|
+
|
|
232
|
+
| File | Surface |
|
|
233
|
+
| ---- | ------- |
|
|
234
|
+
| `src/handlers/landing.ts` | Root landing page + shared API routes |
|
|
235
|
+
| `src/handlers/profile.ts` | SuiNS profile pages + embedded Thunder |
|
|
236
|
+
| `src/handlers/app.ts` | App shell + API namespaces (`/api/app`, `/api/agents`, `/api/ika`, `/api/llm`) |
|
|
237
|
+
| `src/handlers/thunder.ts` | Thunder API + MCP proxy + x402 hints |
|
|
238
|
+
| `src/handlers/ski.ts` | `.SKI` action UI |
|
|
239
|
+
| `src/handlers/ski-sign.ts` | Wallet sign bridge and PTB payload signing |
|
|
240
|
+
| `src/handlers/dashboard.ts` | `my.sui.ski` dashboard |
|
|
241
|
+
| `src/handlers/vault.ts` | Vault routes |
|
|
242
|
+
| `src/handlers/grace-vault-agent.ts` | Grace Vault agent routes |
|
|
243
|
+
| `src/handlers/x402-register.ts` | x402 register agent routes |
|
|
244
|
+
| `src/handlers/mcp.ts` | Sui MCP server wiring |
|
|
245
|
+
| `src/handlers/messaging-sdk.ts` | Messaging SDK informational endpoints |
|
|
246
|
+
| `src/handlers/authenticated-events.ts` | Authenticated events ingestion |
|
|
247
|
+
| `src/handlers/register2.ts` | SuiNS register tx build/submit |
|
|
248
|
+
| `src/handlers/wallet-api.ts` | Wallet challenge/connect/check/disconnect |
|
|
249
|
+
|
|
250
|
+
### Resolvers
|
|
251
|
+
|
|
252
|
+
| File | Surface |
|
|
253
|
+
| ---- | ------- |
|
|
254
|
+
| `src/resolvers/suins.ts` | Name lookup / owner/address resolution |
|
|
255
|
+
| `src/resolvers/content.ts` | IPFS/Walrus direct blob routing |
|
|
256
|
+
| `src/resolvers/rpc.ts` | RPC passthrough with env controls |
|
|
257
|
+
|
|
258
|
+
### SDK + protocol utilities
|
|
259
|
+
|
|
260
|
+
| File | Surface |
|
|
261
|
+
| ---- | ------- |
|
|
262
|
+
| `src/sdk/messaging.ts` | Messaging SDK pinning/config/bootstrap URLs |
|
|
263
|
+
| `src/utils/x402-middleware.ts` | x402 request middleware |
|
|
264
|
+
| `src/utils/x402-sui.ts` | x402 Sui settlement helpers |
|
|
265
|
+
| `src/utils/vault.ts` | Vault utility operations |
|
|
266
|
+
| `src/utils/agent-keypair.ts` | Agent keypair derivation/helpers |
|
|
267
|
+
|
|
268
|
+
### Wallet + client runtime
|
|
269
|
+
|
|
270
|
+
| File | Surface |
|
|
271
|
+
| ---- | ------- |
|
|
272
|
+
| `src/utils/wallet-kit-js.ts` | Wallet integration bootstrap |
|
|
273
|
+
| `src/utils/wallet-session-js.ts` | Session sync in browser |
|
|
274
|
+
| `src/utils/wallet-ui-js.ts` | Wallet modal/UI behavior |
|
|
275
|
+
| `src/utils/wallet-tx-js.ts` | Browser tx helper layer |
|
|
276
|
+
| `src/utils/shared-wallet-js.ts` | Shared wallet mount script |
|
|
277
|
+
| `src/utils/thunder-js.ts` | Thunder interaction + messaging SDK write/read loop |
|
|
278
|
+
| `src/utils/thunder-css.ts` | Thunder presentation layer |
|
|
279
|
+
| `src/utils/zksend-js.ts` | zk-send utilities |
|
|
280
|
+
|
|
281
|
+
### Data, pricing, status, and cache
|
|
282
|
+
|
|
283
|
+
| File | Surface |
|
|
284
|
+
| ---- | ------- |
|
|
285
|
+
| `src/utils/cache.ts` | KV/cache helpers |
|
|
286
|
+
| `src/utils/status.ts` | Gateway status assembly |
|
|
287
|
+
| `src/utils/rpc.ts` | RPC URL/env helpers |
|
|
288
|
+
| `src/utils/response.ts` | response helpers |
|
|
289
|
+
| `src/utils/pricing.ts` | registration/renewal pricing |
|
|
290
|
+
| `src/utils/ns-price.ts` | NS market pricing |
|
|
291
|
+
| `src/utils/pyth-price-info.ts` | external price feeds |
|
|
292
|
+
| `src/utils/mmr.ts` | misc on-chain helpers |
|
|
293
|
+
|
|
294
|
+
### On-chain contracts (workspace)
|
|
295
|
+
|
|
296
|
+
| File | Surface |
|
|
297
|
+
| ---- | ------- |
|
|
298
|
+
| `contracts/storm/sources/registry.move` | Storm canonical channel registry for Thunder |
|
|
299
|
+
| `contracts/seal_messaging/sources/access.move` | Seal messaging/access control package |
|
|
300
|
+
| `contracts/mvr/sources/registry.move` | Move package registry |
|
|
301
|
+
|
|
302
|
+
### Media, social, and activity
|
|
303
|
+
|
|
304
|
+
| File | Surface |
|
|
305
|
+
| ---- | ------- |
|
|
306
|
+
| `src/utils/media-pack.ts` | generated static media assets |
|
|
307
|
+
| `src/utils/og-image.ts` | SVG/PNG OG rendering |
|
|
308
|
+
| `src/utils/social.ts` | social metadata + bot detection |
|
|
309
|
+
| `src/utils/onchain-activity.ts` | profile on-chain activity feeds |
|
|
310
|
+
| `src/utils/onchain-listing.ts` | on-chain listing fetch path |
|
|
311
|
+
| `src/utils/surflux-grpc.ts` | gRPC data integration helpers |
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
## The Garbage Goobler
|
|
4
|
+
|
|
5
|
+
You are a garbage goobler. You love to eat dead code and squash bugs. Your preference is to remove unused code, but you are incredibly cautious because if you eat functionality or code that is actually used, YOU DIE.
|
|
6
|
+
|
|
7
|
+
**Garbage Goobler Rules:**
|
|
8
|
+
1. Always verify code is unused before removing (check imports, dynamic imports, type usage)
|
|
9
|
+
2. When in doubt, leave it - death awaits the careless goobler
|
|
10
|
+
3. Run `npx ts-prune --project tsconfig.json` to find potential dead exports
|
|
11
|
+
4. Check for dynamic imports with `grep -r "import\(" src/`
|
|
12
|
+
5. Type-only exports are safe to remove if the type is never imported
|
|
13
|
+
6. After removing code, run `npx wrangler deploy` to verify bundle still works
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## What is Sui-ski
|
|
18
|
+
|
|
19
|
+
Cloudflare Worker gateway for the Sui blockchain. Resolves wildcard subdomains (`*.sui.ski`) to SuiNS names, MVR packages, IPFS/Walrus content, and a read-only RPC proxy. Thunder is the activity spine — every on-chain action is journaled to the user's `#primary` channel.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Thunder
|
|
24
|
+
|
|
25
|
+
Thunder is the structured activity journal for every `.SKI` user. It turns the Sui Stack Messaging SDK into an atomic, encrypted, machine-readable log of every on-chain action a user takes through the gateway.
|
|
26
|
+
|
|
27
|
+
### #primary Channel
|
|
28
|
+
|
|
29
|
+
Every SuiNS name gets exactly one `#primary` channel — an on-chain Sui Stack Messaging channel (shared object) that serves as the user's activity journal.
|
|
30
|
+
|
|
31
|
+
| Property | Value |
|
|
32
|
+
| -------- | ----- |
|
|
33
|
+
| Ownership | Owner holds `CreatorCap` + `MemberCap` with all permissions |
|
|
34
|
+
| Encryption | Seal-encrypted; only owner can read by default |
|
|
35
|
+
| Write access | Owner-only (journal mode — no replies, no conversation) |
|
|
36
|
+
| Read access | Invited members can read but not write |
|
|
37
|
+
| Storage | Channel ID + MemberCap ID stored in wallet session (KV + cookie) |
|
|
38
|
+
| Package | Sui Stack Messaging mainnet: `0x74e34e2e4a2ba60d935db245c0ed93070bbbe23bf1558ae5c6a2a8590c8ad470` |
|
|
39
|
+
|
|
40
|
+
The `#primary` channel is **write-only by owner**. It is a structured, machine-parseable activity log. AI agents can read it to understand user activity. Invited members observe but never write.
|
|
41
|
+
|
|
42
|
+
### .SKI → Thunder Bootstrap
|
|
43
|
+
|
|
44
|
+
First `.SKI` key-in triggers `#primary` channel creation:
|
|
45
|
+
|
|
46
|
+
1. Connect wallet → sign challenge → verify signature
|
|
47
|
+
2. Check for existing `#primary` channel in session
|
|
48
|
+
3. If none exists, build creation PTB:
|
|
49
|
+
- `channel::new()` → `channel::share()` → transfer caps → attach encryption key → send genesis message
|
|
50
|
+
4. Requires 2 transactions: create channel + attach Seal encryption key
|
|
51
|
+
5. Subsequent `.SKI` sessions skip creation, load existing `channelId`/`memberCapId` from session
|
|
52
|
+
|
|
53
|
+
### Action Messages (WebMCP Format)
|
|
54
|
+
|
|
55
|
+
Every Thunder message uses the MCP `tools/call` envelope with `sui:` namespace:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
interface ThunderAction {
|
|
59
|
+
v: 1
|
|
60
|
+
method: 'tools/call'
|
|
61
|
+
tool: string // "sui:register", "sui:transfer", "sui:swap", etc.
|
|
62
|
+
input: Record<string, unknown>
|
|
63
|
+
origin: string // subdomain that initiated (e.g., "alice.sui.ski")
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The message is serialized to bytes, encrypted with the channel's DEK, and passed to `channel::send_message()` as ciphertext within the same PTB as the action.
|
|
68
|
+
|
|
69
|
+
**Message Catalog:**
|
|
70
|
+
|
|
71
|
+
| Tool | Trigger | Key Input Fields |
|
|
72
|
+
| ---- | ------- | ---------------- |
|
|
73
|
+
| `sui:register` | SuiNS registration | name, years, paymentMethod |
|
|
74
|
+
| `sui:transfer` | Object transfer | objectId, recipient |
|
|
75
|
+
| `sui:swap` | DeepBook swap | fromCoin, toCoin, amount |
|
|
76
|
+
| `sui:stake` | Validator staking | validator, amount |
|
|
77
|
+
| `sui:ski` | First key-in | name, address |
|
|
78
|
+
| `sui:message` | Direct message send | channel, recipient |
|
|
79
|
+
|
|
80
|
+
### PTB Composition Rules
|
|
81
|
+
|
|
82
|
+
1. Every PTB built by `ski-sign.ts` MUST include a Thunder `send_message` call
|
|
83
|
+
2. The Thunder message is appended as the **last** MoveCall in the PTB
|
|
84
|
+
3. If the user has no `#primary` channel yet (first action), the PTB creates the channel instead
|
|
85
|
+
4. Gas budget accounts for the extra message call (~0.001 SUI overhead)
|
|
86
|
+
5. Action and log are **atomic** — both succeed or both fail
|
|
87
|
+
|
|
88
|
+
The composer function signature (to be implemented in `src/utils/thunder.ts`):
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
appendThunderMessage(tx: Transaction, channelId: string, memberCapId: string, action: ThunderAction): void
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## **IMPORTANT: Wallet Session Architecture — Cookies, NOT Bridges**
|
|
97
|
+
|
|
98
|
+
**The same Cloudflare Worker serves `sui.ski` AND `*.sui.ski`. Session state flows through `.sui.ski` domain cookies — NOT iframes, NOT popups, NOT postMessage bridges.**
|
|
99
|
+
|
|
100
|
+
### How It Works
|
|
101
|
+
|
|
102
|
+
1. **Connect** — User connects wallet on any page → `POST /api/wallet/connect` with signed challenge → server verifies signature via `verifyPersonalMessageSignature` → creates session in `WalletSession` Durable Object
|
|
103
|
+
2. **Cookies** — Server sets three cookies on **`.sui.ski` domain** (available to ALL subdomains):
|
|
104
|
+
- `session_id` — DO session key
|
|
105
|
+
- `wallet_address` — `0x...` address
|
|
106
|
+
- `wallet_name` — e.g. "Slush"
|
|
107
|
+
3. **Subdomain restore** — Browser sends `.sui.ski` cookies automatically → Worker middleware reads them → calls `DO.getSessionInfo(sessionId)` → injects verified session into rendered HTML
|
|
108
|
+
4. **Client hydration** — Page JS calls `SuiWalletKit.initFromSession(address, walletName)` with server-injected data → wallet UI shows connected state immediately
|
|
109
|
+
|
|
110
|
+
### Rules
|
|
111
|
+
|
|
112
|
+
- **Subdomain wallet connect goes through the hidden iframe bridge (`sui.ski/sign`) so the wallet dialog shows "From sui.ski".** The iframe is invisible — no popups, no visible windows.
|
|
113
|
+
- **NEVER use popups (`window.open`) for wallet connect.** Popups get blocked after async delays (user gesture expires) and are bad UX.
|
|
114
|
+
- **The hidden iframe bridge handles BOTH connect AND transaction signing on subdomains.** The bridge loads `sui.ski/sign` in a 1px offscreen iframe, relays messages via `postMessage`.
|
|
115
|
+
- **On root domain (`sui.ski`), connect goes directly to the extension** — no bridge needed.
|
|
116
|
+
- Session source of truth is the `WalletSession` Durable Object, not browser cookies (cookies are broadcast cache).
|
|
117
|
+
- Sessions expire after 30 days, auto-extend on activity.
|
|
118
|
+
|
|
119
|
+
### Session Files
|
|
120
|
+
|
|
121
|
+
| File | Role |
|
|
122
|
+
|------|------|
|
|
123
|
+
| `src/durable-objects/wallet-session.ts` | Backend DO — session CRUD, challenge creation, verification |
|
|
124
|
+
| `src/handlers/wallet-api.ts` | API routes — `/api/wallet/challenge`, `/connect`, `/disconnect` |
|
|
125
|
+
| `src/utils/wallet-session-js.ts` | Client-side JS — cookie reading, `challengeAndConnect()`, `disconnectWalletSession()` |
|
|
126
|
+
| `src/utils/wallet-kit-js.ts` | Wallet extension connect — `SuiWalletKit.connect()`, `initFromSession()` |
|
|
127
|
+
| `src/index.ts` | Middleware — reads cookies, calls DO, injects session into handler context |
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Directory Structure
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
src/
|
|
135
|
+
├── index.ts # Worker entry, Hono router
|
|
136
|
+
├── types.ts # Shared types (Env, SuiNSRecord, etc.)
|
|
137
|
+
├── handlers/
|
|
138
|
+
│ ├── landing.ts # Root domain, /api/* routes
|
|
139
|
+
│ ├── profile.ts # SuiNS profile pages + Thunder activity feed
|
|
140
|
+
│ ├── app.ts # Messaging/chat WebSocket + REST APIs
|
|
141
|
+
│ ├── ski.ts, ski-sign.ts # .SKI key-in + Thunder bootstrap
|
|
142
|
+
│ ├── thunder.ts # Thunder API routes (reads #primary)
|
|
143
|
+
│ ├── dashboard.ts # my.sui.ski names management
|
|
144
|
+
│ └── mcp.ts # MCP server for AI tools
|
|
145
|
+
├── resolvers/ # SuiNS, MVR, IPFS/Walrus, RPC proxy
|
|
146
|
+
├── utils/
|
|
147
|
+
│ ├── thunder.ts # (NEW) Thunder message builder + PTB composer
|
|
148
|
+
│ ├── swap-transactions.ts # DeepBook swap + registration PTBs
|
|
149
|
+
│ ├── ns-price.ts # NS token pricing via DeepBook
|
|
150
|
+
│ ├── pricing.ts # SuiNS registration pricing
|
|
151
|
+
│ └── wallet-*.ts # Wallet connection UI/JS
|
|
152
|
+
├── durable-objects/
|
|
153
|
+
│ └── wallet-session.ts # WalletSession DO (stores channelId, memberCapId)
|
|
154
|
+
└── client/
|
|
155
|
+
└── wallet-session.ts # Client-side session helper
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Deployment
|
|
161
|
+
|
|
162
|
+
**Use `npx wrangler deploy` NOT `bun run deploy`.** The `bun run deploy` command fails with auth token errors.
|
|
163
|
+
|
|
164
|
+
**Deploy after every change.** Run `npx wrangler deploy` after each code change so the user can test live immediately.
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
npx wrangler deploy # Deploy (MANDATORY after changes)
|
|
168
|
+
bun run dev # Local dev server
|
|
169
|
+
bun test # Run tests
|
|
170
|
+
bun run typecheck # Type checking
|
|
171
|
+
bun run lint # Biome linter
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Code Standards
|
|
177
|
+
|
|
178
|
+
### Rules
|
|
179
|
+
|
|
180
|
+
| Rule | Detail |
|
|
181
|
+
| ---- | ------ |
|
|
182
|
+
| No comments | Code is self-documenting through clear naming |
|
|
183
|
+
| No dead code | Delete unused code; git has history |
|
|
184
|
+
| No magic values | Named constants at module level |
|
|
185
|
+
| No ignored errors | Handle or propagate every error |
|
|
186
|
+
| Fail fast | Validate inputs at function entry with `invariant` |
|
|
187
|
+
|
|
188
|
+
### Patterns
|
|
189
|
+
|
|
190
|
+
- **Parallel async** — `Promise.all` for independent operations, never sequential `await`
|
|
191
|
+
- **Single-pass** — one iteration, not chained `.map().filter().reduce()`
|
|
192
|
+
- **Bitwise byte conversion** — no string intermediates
|
|
193
|
+
- **Pre-compute constants** — computation out of hot paths
|
|
194
|
+
- **Uint8Array** — for all internal binary data
|
|
195
|
+
- **Actionable errors** — include what failed AND what's expected
|
|
196
|
+
|
|
197
|
+
### Naming
|
|
198
|
+
|
|
199
|
+
| Type | Convention | Example |
|
|
200
|
+
| ---- | ---------- | ------- |
|
|
201
|
+
| Constants | `UPPER_SNAKE` | `MAX_RETRIES` |
|
|
202
|
+
| Functions | `camelCase` | `fetchUser` |
|
|
203
|
+
| Types | `PascalCase` | `UserProfile` |
|
|
204
|
+
| Variables | `camelCase` | `userName` |
|
|
205
|
+
| Files | `kebab-case` | `user-profile.ts` |
|
|
206
|
+
|
|
207
|
+
### Anti-Patterns
|
|
208
|
+
|
|
209
|
+
- `as any` or `@ts-ignore` — use proper types
|
|
210
|
+
- `export { x }` at end of file — export inline
|
|
211
|
+
- `console.log` in production — use structured logging
|
|
212
|
+
- Catching errors without handling or re-throwing
|
|
213
|
+
- Mutable state in module scope
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Git Conventions
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
emoji type(scope): subject
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
| Emoji | Type | Use For |
|
|
224
|
+
| ----- | ---- | ------- |
|
|
225
|
+
| ✨ | feat | New feature |
|
|
226
|
+
| 🐛 | fix | Bug fix |
|
|
227
|
+
| 📝 | docs | Documentation |
|
|
228
|
+
| ♻️ | refactor | Code restructure |
|
|
229
|
+
| ⚡ | perf | Performance |
|
|
230
|
+
| ✅ | test | Tests |
|
|
231
|
+
| 📦 | build | Build/dependencies |
|
|
232
|
+
| 🔧 | chore | Maintenance |
|
|
233
|
+
|
|
234
|
+
Lowercase subject, no period, one logical change per commit.
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Key Constants
|
|
239
|
+
|
|
240
|
+
### Token Decimals (Critical)
|
|
241
|
+
|
|
242
|
+
| Token | Decimals | Conversion |
|
|
243
|
+
| ----- | -------- | ---------- |
|
|
244
|
+
| NS | 6 | `nsTokens * 1e6 = nsMist` |
|
|
245
|
+
| SUI | 9 | `suiAmount * 1e9 = suiMist` |
|
|
246
|
+
|
|
247
|
+
When calculating SUI needed to buy NS tokens:
|
|
248
|
+
```typescript
|
|
249
|
+
const nsTokens = Number(nsMist) / 1e6;
|
|
250
|
+
const suiMist = BigInt(Math.ceil(nsTokens * suiPerNs * 1e9));
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Package Addresses
|
|
254
|
+
|
|
255
|
+
| Package | Mainnet ID |
|
|
256
|
+
| ------- | ---------- |
|
|
257
|
+
| Sui Stack Messaging | `0x74e34e2e4a2ba60d935db245c0ed93070bbbe23bf1558ae5c6a2a8590c8ad470` |
|
|
258
|
+
| SuiNS Core V3 | `0x00c2f85e07181b90c140b15c5ce27d863f93c4d9159d2a4e7bdaeb40e286d6f5` |
|
|
259
|
+
| SuiNS Core Object | `0x6e0ddefc0ad98889c04bab9639e512c21766c5e6366f89e696956d9be6952871` |
|
|
260
|
+
| Seal (mainnet vault) | `0xfabfc63a1d67c37d9c0250bc3efeb96a3c56fb20b9a6d92e3b89dd151f54af5c` |
|
|
261
|
+
| Seal Core (mainnet) | `0xcb83a248bda5f7a0a431e6bf9e96d184e604130ec5218696e3f1211113b447b7` |
|
|
262
|
+
| Decay Auction | `0x10dbff33383bdb68d0bbf6aadeed5e2d3911c45b03664130ebb16437954a2f40` |
|
|
263
|
+
|
|
264
|
+
### Subdomain Routing
|
|
265
|
+
|
|
266
|
+
| Pattern | Route |
|
|
267
|
+
| ------- | ----- |
|
|
268
|
+
| `sui.ski` | Landing page |
|
|
269
|
+
| `rpc.sui.ski` | Read-only RPC proxy |
|
|
270
|
+
| `{name}.sui.ski` | SuiNS profile + Thunder feed |
|
|
271
|
+
| `{pkg}--{name}.sui.ski` | MVR package |
|
|
272
|
+
| `ipfs-{cid}.sui.ski` | IPFS content |
|
|
273
|
+
| `walrus-{blobId}.sui.ski` | Walrus blob |
|
|
274
|
+
| `my.sui.ski` | Names dashboard |
|
|
275
|
+
| `app.sui.ski` | Messaging app |
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## SDK References
|
|
280
|
+
|
|
281
|
+
**Sui Stack Messaging SDK (local fork):**
|
|
282
|
+
`/home/brandon/Dev/Contributor/sui-stack-messaging-sdk` — built at `packages/messaging/dist/`. Client-side only; server serves config, all operations go direct to Sui RPC + Walrus.
|
|
283
|
+
|
|
284
|
+
**CDN manifest:** `https://cdn.jsdelivr.net/gh/arbuthnot-eth/sui-stack-messaging-sdk@mainnet-messaging-v2-2026-02-16/cdn/messaging-mainnet.json`
|
|
285
|
+
|
|
286
|
+
**Sui Client (v2.x):** `CoreClient` is abstract — use `SuiGraphQLClient` from `@mysten/sui/graphql` (preferred) or `SuiJsonRpcClient` from `@mysten/sui/jsonRpc`. JSON-RPC deprecated April 2026.
|
|
287
|
+
|
|
288
|
+
**Seal:** Every app deploys its own `seal_approve` Move contract. See `docs/SEAL_UPGRADE_GUIDE.md`.
|
|
289
|
+
|
|
290
|
+
**SuiNS:** Resolution via gRPC-Web (primary) + JSON-RPC fallback. See [docs.suins.io/developer](https://docs.suins.io/developer).
|
|
291
|
+
|
|
292
|
+
**MVR:** See `docs/MVR_IMPROVEMENTS.md`. SDK plugin: `namedPackagesPlugin` from `@mysten/sui/transactions`.
|