@arcadiasystems/morse-sdk 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/CHANGELOG.md +35 -0
- package/LICENSE +21 -0
- package/README.md +404 -0
- package/dist/clients.d.ts +12 -0
- package/dist/clients.d.ts.map +1 -0
- package/dist/clients.js +6 -0
- package/dist/clients.js.map +1 -0
- package/dist/codecs.d.ts +45 -0
- package/dist/codecs.d.ts.map +1 -0
- package/dist/codecs.js +132 -0
- package/dist/codecs.js.map +1 -0
- package/dist/compatibility.d.ts +22 -0
- package/dist/compatibility.d.ts.map +1 -0
- package/dist/compatibility.js +22 -0
- package/dist/compatibility.js.map +1 -0
- package/dist/config.d.ts +92 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +85 -0
- package/dist/config.js.map +1 -0
- package/dist/errors.d.ts +101 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +214 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/ops/cap.d.ts +79 -0
- package/dist/ops/cap.d.ts.map +1 -0
- package/dist/ops/cap.js +104 -0
- package/dist/ops/cap.js.map +1 -0
- package/dist/ops/collection.d.ts +50 -0
- package/dist/ops/collection.d.ts.map +1 -0
- package/dist/ops/collection.js +55 -0
- package/dist/ops/collection.js.map +1 -0
- package/dist/ops/entry-from-bytes.d.ts +129 -0
- package/dist/ops/entry-from-bytes.d.ts.map +1 -0
- package/dist/ops/entry-from-bytes.js +137 -0
- package/dist/ops/entry-from-bytes.js.map +1 -0
- package/dist/ops/entry.d.ts +173 -0
- package/dist/ops/entry.d.ts.map +1 -0
- package/dist/ops/entry.js +235 -0
- package/dist/ops/entry.js.map +1 -0
- package/dist/ops/index.d.ts +9 -0
- package/dist/ops/index.d.ts.map +1 -0
- package/dist/ops/index.js +9 -0
- package/dist/ops/index.js.map +1 -0
- package/dist/ops/internal.d.ts +21 -0
- package/dist/ops/internal.d.ts.map +1 -0
- package/dist/ops/internal.js +42 -0
- package/dist/ops/internal.js.map +1 -0
- package/dist/ops/publication.d.ts +74 -0
- package/dist/ops/publication.d.ts.map +1 -0
- package/dist/ops/publication.js +123 -0
- package/dist/ops/publication.js.map +1 -0
- package/dist/ptb/cap.d.ts +36 -0
- package/dist/ptb/cap.d.ts.map +1 -0
- package/dist/ptb/cap.js +44 -0
- package/dist/ptb/cap.js.map +1 -0
- package/dist/ptb/collection.d.ts +27 -0
- package/dist/ptb/collection.d.ts.map +1 -0
- package/dist/ptb/collection.js +33 -0
- package/dist/ptb/collection.js.map +1 -0
- package/dist/ptb/entry.d.ts +104 -0
- package/dist/ptb/entry.d.ts.map +1 -0
- package/dist/ptb/entry.js +155 -0
- package/dist/ptb/entry.js.map +1 -0
- package/dist/ptb/index.d.ts +9 -0
- package/dist/ptb/index.d.ts.map +1 -0
- package/dist/ptb/index.js +9 -0
- package/dist/ptb/index.js.map +1 -0
- package/dist/ptb/internal.d.ts +11 -0
- package/dist/ptb/internal.d.ts.map +1 -0
- package/dist/ptb/internal.js +12 -0
- package/dist/ptb/internal.js.map +1 -0
- package/dist/ptb/publication.d.ts +51 -0
- package/dist/ptb/publication.d.ts.map +1 -0
- package/dist/ptb/publication.js +61 -0
- package/dist/ptb/publication.js.map +1 -0
- package/dist/read/entry-bcs.d.ts +47 -0
- package/dist/read/entry-bcs.d.ts.map +1 -0
- package/dist/read/entry-bcs.js +36 -0
- package/dist/read/entry-bcs.js.map +1 -0
- package/dist/read/index.d.ts +5 -0
- package/dist/read/index.d.ts.map +1 -0
- package/dist/read/index.js +5 -0
- package/dist/read/index.js.map +1 -0
- package/dist/read/reader.d.ts +148 -0
- package/dist/read/reader.d.ts.map +1 -0
- package/dist/read/reader.js +403 -0
- package/dist/read/reader.js.map +1 -0
- package/dist/seal/adapter.d.ts +56 -0
- package/dist/seal/adapter.d.ts.map +1 -0
- package/dist/seal/adapter.js +7 -0
- package/dist/seal/adapter.js.map +1 -0
- package/dist/seal/default-adapter.d.ts +90 -0
- package/dist/seal/default-adapter.d.ts.map +1 -0
- package/dist/seal/default-adapter.js +187 -0
- package/dist/seal/default-adapter.js.map +1 -0
- package/dist/seal/identity.d.ts +31 -0
- package/dist/seal/identity.d.ts.map +1 -0
- package/dist/seal/identity.js +79 -0
- package/dist/seal/identity.js.map +1 -0
- package/dist/seal/index.d.ts +8 -0
- package/dist/seal/index.d.ts.map +1 -0
- package/dist/seal/index.js +7 -0
- package/dist/seal/index.js.map +1 -0
- package/dist/types.d.ts +160 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +33 -0
- package/dist/types.js.map +1 -0
- package/dist/wallets/adapter.d.ts +34 -0
- package/dist/wallets/adapter.d.ts.map +1 -0
- package/dist/wallets/adapter.js +6 -0
- package/dist/wallets/adapter.js.map +1 -0
- package/dist/wallets/index.d.ts +7 -0
- package/dist/wallets/index.d.ts.map +1 -0
- package/dist/wallets/index.js +6 -0
- package/dist/wallets/index.js.map +1 -0
- package/dist/wallets/keypair-adapter.d.ts +31 -0
- package/dist/wallets/keypair-adapter.d.ts.map +1 -0
- package/dist/wallets/keypair-adapter.js +210 -0
- package/dist/wallets/keypair-adapter.js.map +1 -0
- package/dist/wallets/wallet-standard-signer.d.ts +118 -0
- package/dist/wallets/wallet-standard-signer.d.ts.map +1 -0
- package/dist/wallets/wallet-standard-signer.js +207 -0
- package/dist/wallets/wallet-standard-signer.js.map +1 -0
- package/dist/walrus/adapter.d.ts +91 -0
- package/dist/walrus/adapter.d.ts.map +1 -0
- package/dist/walrus/adapter.js +11 -0
- package/dist/walrus/adapter.js.map +1 -0
- package/dist/walrus/default-adapter.d.ts +104 -0
- package/dist/walrus/default-adapter.d.ts.map +1 -0
- package/dist/walrus/default-adapter.js +146 -0
- package/dist/walrus/default-adapter.js.map +1 -0
- package/dist/walrus/default-read-adapter.d.ts +89 -0
- package/dist/walrus/default-read-adapter.d.ts.map +1 -0
- package/dist/walrus/default-read-adapter.js +81 -0
- package/dist/walrus/default-read-adapter.js.map +1 -0
- package/dist/walrus/http-aggregator-read-adapter.d.ts +107 -0
- package/dist/walrus/http-aggregator-read-adapter.d.ts.map +1 -0
- package/dist/walrus/http-aggregator-read-adapter.js +174 -0
- package/dist/walrus/http-aggregator-read-adapter.js.map +1 -0
- package/dist/walrus/http-publisher-write-adapter.d.ts +97 -0
- package/dist/walrus/http-publisher-write-adapter.d.ts.map +1 -0
- package/dist/walrus/http-publisher-write-adapter.js +249 -0
- package/dist/walrus/http-publisher-write-adapter.js.map +1 -0
- package/dist/walrus/index.d.ts +11 -0
- package/dist/walrus/index.d.ts.map +1 -0
- package/dist/walrus/index.js +11 -0
- package/dist/walrus/index.js.map +1 -0
- package/dist/walrus/quilt-patch-id.d.ts +34 -0
- package/dist/walrus/quilt-patch-id.d.ts.map +1 -0
- package/dist/walrus/quilt-patch-id.js +103 -0
- package/dist/walrus/quilt-patch-id.js.map +1 -0
- package/package.json +72 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `morse-sdk` will be documented in this file. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
4
|
+
|
|
5
|
+
## [0.1.0] - 2026-05-10
|
|
6
|
+
|
|
7
|
+
Initial public release. Testnet only; mainnet support is gated on contract freeze and arrives with v1.0.0.
|
|
8
|
+
|
|
9
|
+
### Surface
|
|
10
|
+
|
|
11
|
+
- **Domain ops**: `createPublication`, `createCollection` (blob and quilt modes), `addEntry`, `addEncryptedEntry`, revision lifecycle (append, publish from draft, delete), and cap management (`issuePublisherCap`, `revokePublisherCap`, `destroyPublisherCap`, `transferPublisherCap`).
|
|
12
|
+
- **High-level entry flows** (recommended): `addEntryFromBytes` and `addEncryptedEntryFromBytes` cut wallet popups from 3 to 2 by combining `certify_blob` + `add_entry_to_collection` into a single PTB after `register_blob` + off-chain upload. Optional `onProgress` callback emits coarse-grained phase events (`encrypting`, `uploading`, `submitting`, `complete`) for UI spinners.
|
|
13
|
+
- **Reader**: `RpcPublicationReader` for paginated, type-filtered queries via gRPC; construct via `RpcPublicationReader.fromMorseConfig(config, client)`.
|
|
14
|
+
- **Walrus adapters**: `DefaultWalrusWriteAdapter` (blob and quilt uploads with epoch / deletable knobs, plus the new `startBlobUpload` flow primitive used by the high-level entry flows) and `DefaultWalrusReadAdapter` (symmetric reads — `readBlob`, `readBlobByObjectId`, `readQuiltPatch`, `readBlobRef`).
|
|
15
|
+
- **Seal adapter**: `DefaultSealAdapter` for threshold encryption. Canonical testnet key servers are baked into `morseConfig.sealKeyServers`; `fromMorseConfig` defaults `serverConfigs` and `threshold` from there. Custom server sets remain available via the explicit override path.
|
|
16
|
+
- **Wallet integration**: `WalletStandardSigner.fromAccount(account, callbacks)` for browser dapps. Accepts both raw and Sui-canonical with-flag public-key encodings, supports Ed25519 / Secp256k1 / Secp256r1 / Passkey via address-match disambiguation, refuses MultiSig, and ships a structural decoder for ZkLogin (E2E unverified — see compatibility table in the README).
|
|
17
|
+
- **Error taxonomy**: `MorseError` base with `ValidationError`, `NotFoundError`, `UnauthorizedError`, `TransportError`, `ConfigurationError`, `SealError`, `ContractAbortError`, and `UncertifiedBlobError` (raised by `addEntryFromBytes` when the upload step succeeds but the combined certify+addEntry transaction fails; carries `blobObjectId` and `blobId` for recovery / support). ES2022 `cause` preservation is contract-tested for upstream errors so consumers can narrow with `instanceof` (note: some upstream libraries don't set `.name` on subclasses; always prefer `instanceof` over `.name` for narrowing).
|
|
18
|
+
|
|
19
|
+
### Verified configurations
|
|
20
|
+
|
|
21
|
+
- Slush wallet (Mysten reference) + imported Ed25519 keypair + `@mysten/seal@1.1.3` + `@mysten/walrus@1.1.6` + `@mysten/sui@2.16.2` on testnet (2026-05-10).
|
|
22
|
+
- CLI smoke scripts (`scripts/phase-2-publication.ts` through `scripts/phase-7-encrypted.ts`) cover the full surface end-to-end against the canonical testnet deployment.
|
|
23
|
+
|
|
24
|
+
### Known limitations
|
|
25
|
+
|
|
26
|
+
See `README.md` for the full list. Headlines:
|
|
27
|
+
|
|
28
|
+
- Testnet only; mainnet config arrives with v1.0.0.
|
|
29
|
+
- The Move contract hardcodes `encrypted=false` on `publish_from_draft` / `publish_direct`; encrypted content stays as drafts.
|
|
30
|
+
- `Subscription` access policy is reserved, not enforced.
|
|
31
|
+
- Walrus testnet writes occasionally flake with `NotEnoughBlobConfirmationsError` (rerun); browser-side reads occasionally hit `NoBlobMetadataReceivedError` due to CORS gaps on a subset of testnet storage nodes (CLI smokes are more reliable).
|
|
32
|
+
- Wallet schemes other than Ed25519 ship as decoders with E2E unverified; `WalletStandardSigner.fromAccount` will accept them, but `@mysten/walrus` and `@mysten/seal` round-trip is not yet smoke-tested for those configurations.
|
|
33
|
+
- gRPC client only at v0.1.0; the reader and adapter interfaces are typed against `Pick<SuiGrpcClient, ...>`. JSON-RPC fallback is planned for v0.2.0.
|
|
34
|
+
|
|
35
|
+
[0.1.0]: https://github.com/TheDivic/morse-dcms/releases/tag/v0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Morse
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
# morse-sdk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for [Morse](../morse-contracts), a decentralized content management system on the Sui blockchain. Wraps the Move contract surface, Walrus storage, and Seal threshold encryption behind a typed adapter pattern.
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
Pre-release. Testnet only. The Move contract addresses are baked in via `morseConfig({ network: "testnet" })` and re-pinned on every contract redeploy. Mainnet support arrives once the contracts are frozen.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
bun add @arcadiasystems/morse-sdk @mysten/sui
|
|
13
|
+
# Optional - install only what you use:
|
|
14
|
+
bun add @mysten/walrus # for DefaultWalrusWriteAdapter
|
|
15
|
+
bun add @mysten/seal # for DefaultSealAdapter and encrypted entries
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`@mysten/sui` is required: the SDK takes types from it (`Transaction`, `Signer`) and you construct the gRPC client and keypairs directly. `@mysten/walrus` and `@mysten/seal` are optional peer dependencies; you only pay the install cost for the surface you actually import.
|
|
19
|
+
|
|
20
|
+
## Compatibility
|
|
21
|
+
|
|
22
|
+
morse-sdk is built and tested against specific minor versions of its Mysten substrate. Newer or older versions are not validated and may produce runtime errors. The peer-dependency ranges in `package.json` enforce these bounds — `bun install` will warn if you try to use a different minor.
|
|
23
|
+
|
|
24
|
+
| morse-sdk | `@mysten/sui` | `@mysten/walrus` | `@mysten/seal` | Sui network | Verified |
|
|
25
|
+
| --------- | ------------- | ---------------- | -------------- | ----------- | --------- |
|
|
26
|
+
| 0.1.x | 2.16.2-2.16.x | 1.1.6-1.1.x | 1.1.3-1.1.x | testnet | 2026-05-10 |
|
|
27
|
+
|
|
28
|
+
Mysten ships breaking changes inside major version boundaries. When `@mysten/walrus@1.2.0` (or any minor bump on these libraries) is released, morse-sdk needs a coordinated minor bump and re-verification before the new minor is supported. Pin via `bun add @arcadiasystems/morse-sdk@~0.1.0` if you want patch updates without surprise minors.
|
|
29
|
+
|
|
30
|
+
The verification protocol is documented in [`CONTRIBUTING.md`](./CONTRIBUTING.md): every Mysten dep bump runs the full `scripts/phase-N-*.ts` smoke suite against testnet before the bump lands.
|
|
31
|
+
|
|
32
|
+
### Runtime requirements
|
|
33
|
+
|
|
34
|
+
morse-sdk is ESM-only (`"type": "module"` in `package.json`); CommonJS `require` is not supported.
|
|
35
|
+
|
|
36
|
+
| Runtime | Supported | Notes |
|
|
37
|
+
| ------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
38
|
+
| Bun | >= 1.2.0 | Primary development runtime. Enforced via `engines.bun`. Smoke scripts (`bun run scripts/phase-N-*.ts`) require Bun. |
|
|
39
|
+
| Node | >= 18.0 | Library code uses ES2022 features (private class fields, `Error.cause`), `TextEncoder` / `crypto.getRandomValues` / `BigInt` (all stable on Node 18+). |
|
|
40
|
+
| Browser | Evergreen | Chrome / Edge / Firefox / Safari recent stable. Bundlers (Vite, Webpack, esbuild) handle the rest. No `require`-based polyfills needed. |
|
|
41
|
+
|
|
42
|
+
The SDK does not pull in Node-specific APIs (`fs`, `path`, `process`, `crypto` from `node:crypto`); the public surface is portable across both runtimes. A handful of `@mysten/*` substrate libraries reach into Node-shaped APIs internally — consult their documentation for browser polyfill requirements (typically zero with modern bundlers).
|
|
43
|
+
|
|
44
|
+
## Quick start
|
|
45
|
+
|
|
46
|
+
Setup once at startup:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { SuiGrpcClient } from "@mysten/sui/grpc";
|
|
50
|
+
import {
|
|
51
|
+
KeypairAdapter,
|
|
52
|
+
morseConfig,
|
|
53
|
+
RpcPublicationReader,
|
|
54
|
+
} from "@arcadiasystems/morse-sdk";
|
|
55
|
+
|
|
56
|
+
const config = morseConfig({ network: "testnet" });
|
|
57
|
+
const client = new SuiGrpcClient({ network: "testnet", baseUrl: config.rpcUrl });
|
|
58
|
+
const adapter = KeypairAdapter.fromSecretKey(privateKey, client);
|
|
59
|
+
// Browser apps swap KeypairAdapter for a WalletAdapter impl against the
|
|
60
|
+
// connected wallet's signer - see "Adapter pattern" below.
|
|
61
|
+
const reader = RpcPublicationReader.fromMorseConfig(config, client);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Then create a publication, add an entry, read it back:
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import {
|
|
68
|
+
addEntryFromBytes,
|
|
69
|
+
createCollection,
|
|
70
|
+
createPublication,
|
|
71
|
+
DefaultWalrusWriteAdapter,
|
|
72
|
+
StorageMode,
|
|
73
|
+
} from "@arcadiasystems/morse-sdk";
|
|
74
|
+
|
|
75
|
+
const created = await createPublication(adapter, config, {
|
|
76
|
+
name: "My Publication",
|
|
77
|
+
slug: "my-publication",
|
|
78
|
+
});
|
|
79
|
+
await createCollection(adapter, config, {
|
|
80
|
+
publicationId: created.publicationId,
|
|
81
|
+
publisherCapId: created.publisherCapId,
|
|
82
|
+
name: "blog",
|
|
83
|
+
storageMode: StorageMode.Blob,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const walrus = DefaultWalrusWriteAdapter.fromConfig(
|
|
87
|
+
{ network: "testnet", suiClient: client },
|
|
88
|
+
keypair,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const entry = await addEntryFromBytes(adapter, config, {
|
|
92
|
+
walrus,
|
|
93
|
+
publicationId: created.publicationId,
|
|
94
|
+
publisherCapId: created.publisherCapId,
|
|
95
|
+
collectionName: "blog",
|
|
96
|
+
name: "first-post",
|
|
97
|
+
bytes: new TextEncoder().encode("hello world"),
|
|
98
|
+
contentType: "text/plain",
|
|
99
|
+
upload: { epochs: 3, deletable: true },
|
|
100
|
+
});
|
|
101
|
+
const fetched = await reader.getEntry(created.publicationId, "blog", entry.entryId);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`addEntryFromBytes` runs in **2 wallet popups** (one for `register_blob`, one for the combined `certify_blob + add_entry_to_collection` PTB) instead of the 3 popups a separate `uploadBlob` + `addEntry` would emit. See "Choosing the right entry path" below for when to prefer the lower-level split form.
|
|
105
|
+
|
|
106
|
+
The compile-checked end-to-end version is in [`examples/quickstart.ts`](./examples/quickstart.ts).
|
|
107
|
+
|
|
108
|
+
## Walrus access patterns
|
|
109
|
+
|
|
110
|
+
morse-sdk ships two pairs of Walrus adapters. They implement the same interfaces (`WalrusReadAdapter`, `WalrusWriteAdapter`) and the rest of the SDK is unchanged whichever pair you pick.
|
|
111
|
+
|
|
112
|
+
| Pair | Trust model | Browser CORS | Popup count for upload + addEntry | Storage cost paid by |
|
|
113
|
+
| ----------------------------------------------------------------- | ---------------- | --------------- | --------------------------------- | -------------------- |
|
|
114
|
+
| `DefaultWalrusReadAdapter` + `DefaultWalrusWriteAdapter` | Trustless (direct fanout to ~30 storage nodes) | Spotty on testnet | 2 (with `addEntryFromBytes`) or 3 (split) | Consumer wallet (WAL + gas) |
|
|
115
|
+
| `HttpAggregatorReadAdapter` + `HttpPublisherWriteAdapter` | Operator-trusted | Reliable | 1 (`uploadBlob` is a publisher HTTP call, only `addEntry` signs) | Publisher operator (WAL); consumer (Sui gas only) |
|
|
116
|
+
|
|
117
|
+
**When to pick which:**
|
|
118
|
+
- **Default direct-protocol pair**: trustless reads, full control. Best for CLI smokes, server-side dapps, or browser dapps that don't hit CORS gaps. The flow-aware optimization (`addEntryFromBytes`) cuts popups from 3 to 2.
|
|
119
|
+
- **HTTP pair**: reliable browser reads (one CORS-friendly endpoint instead of ~30), and a "publisher pays storage" UX where the user signs only the on-chain `addEntry`. Trade trustless reads for operator trust; use `verifyBlobIntegrity` on the read adapter for a trust-but-verify path.
|
|
120
|
+
|
|
121
|
+
The HTTP adapters are NOT compatible with `addEntryFromBytes` / `addEncryptedEntryFromBytes`. Those functions require `WalrusFlowCapable` for the 2-popup combined PTB; the publisher-paid path is naturally 1-popup through standard `uploadBlob` + `addEntry`.
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
// Default (direct, trustless, 2-3 popups)
|
|
125
|
+
const reader = DefaultWalrusReadAdapter.fromConfig({ network: "testnet", suiClient });
|
|
126
|
+
const writer = DefaultWalrusWriteAdapter.fromConfig({ network: "testnet", suiClient }, signer);
|
|
127
|
+
|
|
128
|
+
// HTTP (operator-trusted, 1 popup for upload+addEntry)
|
|
129
|
+
const reader = HttpAggregatorReadAdapter.fromMorseConfig(config, suiClient);
|
|
130
|
+
const writer = HttpPublisherWriteAdapter.fromConfig({
|
|
131
|
+
publisherUrl: "https://walrus-testnet-publisher.nami.cloud",
|
|
132
|
+
ownerAddress: account.address,
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The aggregator URL for testnet is baked into `morseConfig.walrusEndpoints.aggregator` (Mysten's canonical service). The publisher URL is intentionally not baked in — publishers are operator-specific and consumers pick one explicitly.
|
|
137
|
+
|
|
138
|
+
`HttpPublisherWriteAdapter` parses Mysten's published publisher binary (camelCase JSON) and the documented OpenAPI schema (snake_case fallback). For non-standard publisher forks that serve a different shape, pass `parseResponse` to `HttpPublisherWriteAdapter.fromConfig({ ..., parseResponse })` — it receives the raw decoded JSON and returns an `UploadBlobResult`, replacing the built-in parser. Throws from the callback propagate verbatim.
|
|
139
|
+
|
|
140
|
+
## Choosing the right entry path
|
|
141
|
+
|
|
142
|
+
The SDK ships two ways to publish content. The high-level `addEntryFromBytes` (and its encrypted twin `addEncryptedEntryFromBytes`) is the recommended default; the split form (`uploadBlob` + `addEntry`) is for cases the high-level shape doesn't cover.
|
|
143
|
+
|
|
144
|
+
| Use | Function | Wallet popups |
|
|
145
|
+
| ---------------------------------------------------- | --------------------------------------------------- | ------------- |
|
|
146
|
+
| Publish raw bytes as a new entry (typical case) | `addEntryFromBytes` | 2 |
|
|
147
|
+
| Publish encrypted bytes as a new entry | `addEncryptedEntryFromBytes` | 2 |
|
|
148
|
+
| Reuse one blob across many entries (deduplication) | `uploadBlob` once, then `addEntry` N times | 2 + N |
|
|
149
|
+
| Decouple upload and add-entry (e.g. draft-then-attach UX) | `uploadBlob` (upload time), `addEntry` (publish time) | 2 + 1 |
|
|
150
|
+
| Server pre-uploads, browser only adds entries | `uploadBlob` (server), `addEntry` (browser) | 0 server + 1 browser |
|
|
151
|
+
|
|
152
|
+
**`addEntryFromBytes` requires a `WalrusWriteAdapter` that also implements `WalrusFlowCapable`** (the optimization uses its flow-aware `startBlobUpload` API). The default `DefaultWalrusWriteAdapter` implements both; custom adapters that don't implement the capability are rejected with `TransportError` before any IO and should use the split form.
|
|
153
|
+
|
|
154
|
+
If `addEntryFromBytes` succeeds in popup 1 (register + upload) but fails in popup 2 (the combined certify + add_entry tx — user rejected, contract aborted, network blip), it throws `UncertifiedBlobError` carrying the `blobObjectId` and `blobId` of the orphaned blob. The blob is on storage nodes and you've paid for it but it's uncertified; storage releases on registration expiry. Surface the error to your user or log the IDs for support.
|
|
155
|
+
|
|
156
|
+
## Examples
|
|
157
|
+
|
|
158
|
+
Per-concern, compile-checked illustrative code. Each file is short, focused, and intended to be read alongside the JSDoc on the public exports.
|
|
159
|
+
|
|
160
|
+
| Concern | File | Covers |
|
|
161
|
+
| ------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
|
|
162
|
+
| Setup | [`examples/setup.ts`](./examples/setup.ts) | morseConfig, gRPC client, KeypairAdapter, reader |
|
|
163
|
+
| Quick start | [`examples/quickstart.ts`](./examples/quickstart.ts) | End-to-end happy path |
|
|
164
|
+
| Publication lifecycle | [`examples/publication-lifecycle.ts`](./examples/publication-lifecycle.ts) | createPublication, transferOwnership, deletePublication |
|
|
165
|
+
| Publisher cap roles | [`examples/publisher-caps.ts`](./examples/publisher-caps.ts) | issuePublisherCap, transferPublisherCap, revokePublisherCap, destroyPublisherCap |
|
|
166
|
+
| Collections | [`examples/collections.ts`](./examples/collections.ts) | createCollection (Blob and Quilt modes), deleteCollection |
|
|
167
|
+
| Entries (revisions, draft → publish) | [`examples/entries.ts`](./examples/entries.ts) | addEntry, appendDraftRevision, publishFromDraft, publishDirect, deleteEntry |
|
|
168
|
+
| Encrypted entries | [`examples/encrypted-entries.ts`](./examples/encrypted-entries.ts) | buildPublisherSealId, encrypt, addEncryptedEntry, appendEncryptedDraftRevision, decrypt |
|
|
169
|
+
| Reading | [`examples/reading.ts`](./examples/reading.ts) | getPublication, getEntry, getRevision, listEntries, scanEntries |
|
|
170
|
+
| Browser wallet integration | [`examples/wallet-standard.ts`](./examples/wallet-standard.ts) | WalletAdapter impl against `@mysten/dapp-kit` hooks (or any wallet-standard signer) |
|
|
171
|
+
| React + dapp-kit + Suiet | [`examples/wallet-standard-react.md`](./examples/wallet-standard-react.md) | Worked walkthrough: providers, connect button, hook, adapter wiring, Seal SessionKey |
|
|
172
|
+
| Walrus HTTP adapters | [`examples/walrus-http-adapters.ts`](./examples/walrus-http-adapters.ts) | HttpAggregatorReadAdapter + HttpPublisherWriteAdapter (browser-friendly, operator-paid storage) |
|
|
173
|
+
|
|
174
|
+
## API reference
|
|
175
|
+
|
|
176
|
+
The full public surface, grouped by concern. Every export carries a JSDoc on its definition; this table is the index, not the documentation.
|
|
177
|
+
|
|
178
|
+
### Configuration
|
|
179
|
+
|
|
180
|
+
| Export | Purpose |
|
|
181
|
+
| --- | --- |
|
|
182
|
+
| `morseConfig({ network })` | Build a `NetworkConfig` for testnet (canonical addresses baked in) or supply override fields for forks / local nodes. |
|
|
183
|
+
| `Network` | Const enum-like: `"mainnet" \| "testnet" \| "localnet"`. Mainnet currently throws `ConfigurationError` (gates v1.0.0). |
|
|
184
|
+
| `DEFAULT_RPC_URLS` | Public Sui fullnode URLs per network. Read-only. |
|
|
185
|
+
| `TESTED_SUBSTRATE` | Mysten substrate versions verified end-to-end. Diagnostic constant. |
|
|
186
|
+
|
|
187
|
+
### Domain ops (write paths)
|
|
188
|
+
|
|
189
|
+
| Export | Purpose |
|
|
190
|
+
| --- | --- |
|
|
191
|
+
| `createPublication(adapter, config, args)` | Create + share publication; returns `{ publicationId, ownerCapId, publisherCapId }`. |
|
|
192
|
+
| `transferOwnership(adapter, config, args)` | Transfer the OwnerCap to a new address. |
|
|
193
|
+
| `deletePublication(reader, adapter, config, args)` | Delete an empty publication; pre-flight checks for collections. |
|
|
194
|
+
| `issuePublisherCap` / `revokePublisherCap` / `destroyPublisherCap` / `transferPublisherCap` | PublisherCap lifecycle. Issue + transfer-to-holder is atomic. |
|
|
195
|
+
| `createCollection` / `deleteCollection` | Collection lifecycle in blob or quilt mode. |
|
|
196
|
+
| `addEntryFromBytes(adapter, config, args)` | **Recommended.** Upload + add entry in 2 wallet popups. |
|
|
197
|
+
| `addEncryptedEntryFromBytes(adapter, config, args)` | Encrypt + upload + add encrypted entry in 2 wallet popups. |
|
|
198
|
+
| `addEntry` / `addEncryptedEntry` | Lower-level: add entry against a pre-uploaded `blobObjectId`. |
|
|
199
|
+
| `appendDraftRevision` / `appendEncryptedDraftRevision` / `publishFromDraft` / `publishDirect` | Revision lifecycle on existing entries. |
|
|
200
|
+
| `deleteEntry` | Remove an entry and its revisions. |
|
|
201
|
+
|
|
202
|
+
### Reader (RPC-backed)
|
|
203
|
+
|
|
204
|
+
| Export | Purpose |
|
|
205
|
+
| --- | --- |
|
|
206
|
+
| `RpcPublicationReader.fromMorseConfig(config, client)` | Construct a reader bound to the canonical `originalPackageId` for type filters. |
|
|
207
|
+
| `reader.getPublication` / `getEntry` / `getRevision` / `getPublisherCap` | Single-object reads. |
|
|
208
|
+
| `reader.listPublicationsOwnedBy` / `listPublisherCapsOwnedBy` / `listEntries` | Paginated lists. |
|
|
209
|
+
| `reader.scanEntries` | Async-iterator over every entry in a collection. |
|
|
210
|
+
|
|
211
|
+
### Adapters
|
|
212
|
+
|
|
213
|
+
| Export | Purpose |
|
|
214
|
+
| --- | --- |
|
|
215
|
+
| `KeypairAdapter` | Server / CLI `WalletAdapter` wrapping a raw `Ed25519Keypair`. |
|
|
216
|
+
| `WalletStandardSigner.fromAccount(account, callbacks)` | Browser-side `Signer` for `@mysten/walrus` and `@mysten/seal`; wraps wallet-standard wallets without ever holding the user's key. |
|
|
217
|
+
| `DefaultWalrusWriteAdapter.fromConfig(config, signer)` | Walrus uploads (blob + quilt). Implements `WalrusFlowCapable` (the 2-popup optimization). |
|
|
218
|
+
| `DefaultWalrusReadAdapter.fromConfig(config)` | Walrus reads (`readBlob`, `readBlobByObjectId`, `readQuiltPatch`, `readBlobRef`). |
|
|
219
|
+
| `HttpPublisherWriteAdapter.fromConfig({ publisherUrl, ownerAddress })` | Walrus uploads via a publisher HTTP service (operator pays storage; 1 popup for upload + addEntry). |
|
|
220
|
+
| `HttpAggregatorReadAdapter.fromMorseConfig(config, suiClient)` / `.fromConfig({ aggregatorUrl, suiClient })` | Walrus reads via a single CORS-friendly aggregator endpoint instead of fanout to ~30 storage nodes. |
|
|
221
|
+
| `DefaultSealAdapter.fromMorseConfig(config, options, suiClient)` | Threshold encryption / decryption. Defaults canonical testnet key servers. |
|
|
222
|
+
| `WalletAdapter` / `WalrusWriteAdapter` / `WalrusReadAdapter` / `SealAdapter` | Interfaces for substituting custom implementations. |
|
|
223
|
+
| `WalrusFlowCapable` / `isWalrusFlowCapable` | Optional capability for the 2-popup `addEntryFromBytes` path. |
|
|
224
|
+
|
|
225
|
+
### Seal identity
|
|
226
|
+
|
|
227
|
+
| Export | Purpose |
|
|
228
|
+
| --- | --- |
|
|
229
|
+
| `buildPublisherSealId(publicationId, nonce)` | Build a publisher-policy Seal identity (`pubId(32) \|\| tag(1) \|\| nonce`). |
|
|
230
|
+
| `decodePublisherSealId(sealId)` | Inspect an existing identity. Throws `ValidationError` on tampered tags. |
|
|
231
|
+
|
|
232
|
+
### Codecs (branded ID constructors)
|
|
233
|
+
|
|
234
|
+
| Export | Purpose |
|
|
235
|
+
| --- | --- |
|
|
236
|
+
| `toPackageId` / `toRegistryId` / `toPublicationId` / `toOwnerCapId` / `toPublisherCapId` / `toBlobObjectId` / `toSuiAddress` / `toSuiObjectId` | Validate and normalize Sui object IDs to canonical 64-char hex. |
|
|
237
|
+
| `toWalrusBlobId` | Validate Walrus content-addressed blob ID (43-char URL-safe-base64). |
|
|
238
|
+
| `toQuiltPatchId` | Validate 37-byte quilt patch ID. |
|
|
239
|
+
| `accessPolicyToU8` / `accessPolicyFromU8` / `storageModeToU8` / `storageModeFromU8` | Move enum ↔ TypeScript enum conversion. |
|
|
240
|
+
| `encodeQuiltPatchId` / `decodeQuiltPatchId` / `quiltPatchIdToString` / `quiltPatchIdFromString` | Quilt patch ID structural codec (`{quiltBlobId, version, startIndex, endIndex}`). |
|
|
241
|
+
|
|
242
|
+
### Errors
|
|
243
|
+
|
|
244
|
+
| Export | Purpose |
|
|
245
|
+
| --- | --- |
|
|
246
|
+
| `MorseError` | Abstract base. Every SDK throw extends it. |
|
|
247
|
+
| `ValidationError` (`field`) | Client-side input rejection. |
|
|
248
|
+
| `NotFoundError` (`resource`, `identifier`) | Object missing on-chain or on Walrus. |
|
|
249
|
+
| `UnauthorizedError` | Client-side auth check failed. |
|
|
250
|
+
| `ContractAbortError` (`module`, `abortCode`, `reason`) | Move VM aborted; `ABORT_CODES` table maps codes to names. |
|
|
251
|
+
| `SealError` (`code`) | Seal authorization or decryption failure (`no-access` / `decrypt-failed` / `session-expired` / `rate-limited`). |
|
|
252
|
+
| `TransportError` | RPC, network, or response-parsing failure. |
|
|
253
|
+
| `ConfigurationError` | SDK config gap (e.g. unsupported network, raw-byte sign on `WalletStandardSigner`). |
|
|
254
|
+
| `UncertifiedBlobError` (`blobObjectId`, `blobId`) | `addEntryFromBytes` upload succeeded but second popup failed. |
|
|
255
|
+
|
|
256
|
+
### Types
|
|
257
|
+
|
|
258
|
+
`Publication`, `Collection`, `Entry`, `Revision`, `PublisherCap`, `OwnerCap`, `BlobRef`, `AccessPolicy`, `StorageMode`, `SealPolicyTag`, branded ID types (`PublicationId`, `BlobObjectId`, `WalrusBlobId`, `QuiltPatchId`, etc.).
|
|
259
|
+
|
|
260
|
+
## Conceptual model
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
PublicationRegistry (one shared object, name-uniqueness index)
|
|
264
|
+
Publication (one shared object per publication)
|
|
265
|
+
Collection × N (inline VecMap; storage mode fixed at create)
|
|
266
|
+
Entry × N (dynamic-field table; monotonic u64 ids)
|
|
267
|
+
Revision × N (append-only vector; never mutated in place)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
- **Publication**: top-level container with a globally-unique slug. Holds collections inline. Owned via `OwnerCap`; write access delegated via `PublisherCap`.
|
|
271
|
+
- **Collection**: named bucket for entries. `storageMode` (`Blob` or `Quilt`) is immutable after creation.
|
|
272
|
+
- **Entry**: identified by a stable monotonic `u64`. Carries a `name`, append-only `revisions`, and `draftHead` / `publicHead` pointers.
|
|
273
|
+
- **Revision**: immutable. Carries a `BlobRef` (Walrus blob object or quilt patch id), `contentType`, `encrypted` flag, `accessPolicy`, `sealId`, and `author`.
|
|
274
|
+
|
|
275
|
+
## Adapter pattern
|
|
276
|
+
|
|
277
|
+
Three abstractions; the SDK ships default impls and accepts substitutions:
|
|
278
|
+
|
|
279
|
+
- **`WalletAdapter`** signs and submits Sui transactions. Default: `KeypairAdapter`. Browser apps implement against a wallet-standard signer.
|
|
280
|
+
- **`WalrusWriteAdapter`** uploads bytes to Walrus, returns the resulting blob's Sui object id. Default: `DefaultWalrusWriteAdapter` wrapping `@mysten/walrus`.
|
|
281
|
+
- **`SealAdapter`** encrypts and decrypts under a publisher Seal identity. Default: `DefaultSealAdapter` wrapping `@mysten/seal`.
|
|
282
|
+
|
|
283
|
+
Reader pattern is parallel: `PublicationReader` is the interface, `RpcPublicationReader` is the gRPC-backed default. An indexer-backed reader could implement the same shape.
|
|
284
|
+
|
|
285
|
+
Always construct readers and seal adapters via `fromMorseConfig` (e.g. `RpcPublicationReader.fromMorseConfig(config, client)`); the raw constructors take `originalPackageId` directly and passing the wrong value silently empties type-filtered list results.
|
|
286
|
+
|
|
287
|
+
## Wallet scheme support
|
|
288
|
+
|
|
289
|
+
`WalletStandardSigner.fromAccount(account, callbacks)` takes a wallet-standard `WalletAccount` and produces a Sui `Signer` for `@mysten/walrus` and `@mysten/seal`. It tries every plausible interpretation of `account.publicKey` (raw bytes and Sui's canonical with-flag encoding) and picks the one whose derived address matches `account.address`.
|
|
290
|
+
|
|
291
|
+
| Scheme | Status | Verified against | Notes |
|
|
292
|
+
| --------- | ----------------------------- | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
293
|
+
| ED25519 | Supported (verified) | Slush + imported keypair, `@mysten/seal@1.1.3`, `@mysten/walrus@1.1.6`, 2026-05-10 | Accepts raw 32-byte key (Suiet) and Sui canonical `0x00 \|\| 32 raw` (Slush). Most common configuration. |
|
|
294
|
+
| Secp256k1 | Supported (decoder) | - | Accepts raw 33-byte key and `0x01 \|\| 33 raw`. End-to-end behavior on Walrus + Seal not yet verified against a wallet that exposes Secp256k1 accounts. |
|
|
295
|
+
| Secp256r1 | Supported (decoder) | - | Accepts raw 33-byte key and `0x02 \|\| 33 raw`. Disambiguated from Secp256k1 / Passkey by address derivation. |
|
|
296
|
+
| Passkey | Supported (decoder) | - | Accepts raw 33-byte key and `0x06 \|\| 33 raw`. WebAuthn signing inside the wallet; `Signer` surface unchanged. |
|
|
297
|
+
| ZkLogin | Decoder ships, E2E unverified | - | Variable-length `[1 iss-len][iss][32 addressSeed]` identifier (auto-detects modern vs legacy address derivation). Walrus and Seal `SessionKey` flows have not been smoke-tested with zkLogin signatures; fall back to a keypair account if you see errors. |
|
|
298
|
+
| MultiSig | Refused | - | Variable-length aggregation of multiple keys; signing semantics differ from `Signer` and have not been wired up. Implement a custom `Signer` subclass if you need it. |
|
|
299
|
+
|
|
300
|
+
Refused schemes throw `ConfigurationError` at construction time. Surface the message to your user as "this wallet account isn't supported yet" rather than letting the page crash inside Walrus or Seal later.
|
|
301
|
+
|
|
302
|
+
## Error taxonomy
|
|
303
|
+
|
|
304
|
+
All errors extend `MorseError`. Narrow by class:
|
|
305
|
+
|
|
306
|
+
| Class | Carries | Thrown when |
|
|
307
|
+
| -------------------- | -------------------------------- | -------------------------------------------------------------------- |
|
|
308
|
+
| `ValidationError` | `field` | Client-side input failed a precondition. |
|
|
309
|
+
| `NotFoundError` | `resource`, `identifier` | Object doesn't exist on-chain. |
|
|
310
|
+
| `UnauthorizedError` | - | Client-side auth check failed before submit. |
|
|
311
|
+
| `ContractAbortError` | `module`, `abortCode`, `reason` | Move VM aborted (e.g. `ESlugAlreadyExists`). |
|
|
312
|
+
| `SealError` | `code` (`no-access` / `decrypt-failed` / `session-expired` / `rate-limited`) | Seal authorization or decryption failed. |
|
|
313
|
+
| `TransportError` | - | RPC, network, or response-parsing failure. |
|
|
314
|
+
| `ConfigurationError` | - | SDK config gap (e.g. unsupported network). |
|
|
315
|
+
| `UncertifiedBlobError` | `blobObjectId`, `blobId` | `addEntryFromBytes` upload succeeded but the combined certify+add_entry tx failed; the blob is uploaded but uncertified. |
|
|
316
|
+
|
|
317
|
+
```ts
|
|
318
|
+
try {
|
|
319
|
+
await addEntry(adapter, config, args);
|
|
320
|
+
} catch (err) {
|
|
321
|
+
if (err instanceof ContractAbortError && err.reason === "EPublisherCapRevoked") {
|
|
322
|
+
// your cap was revoked - issue a new one
|
|
323
|
+
} else if (err instanceof SealError && err.code === "no-access") {
|
|
324
|
+
// identity rejected by key servers
|
|
325
|
+
} else if (err instanceof NotFoundError && err.resource === "entry") {
|
|
326
|
+
// entry was deleted between read and write
|
|
327
|
+
} else if (err instanceof TransportError) {
|
|
328
|
+
// network blip - retry
|
|
329
|
+
} else {
|
|
330
|
+
throw err;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Network configuration
|
|
336
|
+
|
|
337
|
+
```ts
|
|
338
|
+
const config = morseConfig({ network: "testnet" });
|
|
339
|
+
// {
|
|
340
|
+
// network, rpcUrl, packageId, originalPackageId, registryId,
|
|
341
|
+
// sealKeyServers: [{ objectId, weight }, ...] // canonical testnet allowlist
|
|
342
|
+
// }
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Override individual fields for forks or local nodes:
|
|
346
|
+
|
|
347
|
+
```ts
|
|
348
|
+
const config = morseConfig({
|
|
349
|
+
network: "localnet",
|
|
350
|
+
packageId: "0x...", // required: no canonical localnet deployment
|
|
351
|
+
registryId: "0x...", // required
|
|
352
|
+
rpcUrl: "http://127.0.0.1:9000",
|
|
353
|
+
});
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
`packageId` is the published-at address (used for Move calls). `originalPackageId` is the genesis publish address (used for Sui type filters and Seal package binding). Always thread both through `morseConfig` and let the SDK pick the right one per call site.
|
|
357
|
+
|
|
358
|
+
## Known limitations
|
|
359
|
+
|
|
360
|
+
- **Testnet only at v0.x**. Mainnet config lands once the contracts are frozen.
|
|
361
|
+
- **No encrypted publish path**. The Move contract hardcodes `encrypted=false` on `publish_from_draft` and `publish_direct`. Encrypted content stays as drafts.
|
|
362
|
+
- **`Subscription` access policy is reserved**, not enforced.
|
|
363
|
+
- **`listEntries` ordering is dynamic-field object-store order**, not chronological. Sort by `entry.id` for insertion order.
|
|
364
|
+
- **Walrus testnet flakiness**. `NotEnoughBlobConfirmationsError` from the underlying client is environmental; rerun. The SDK preserves the original error as the `cause` (use `instanceof` for narrowing — Walrus error classes don't set `.name`). Browser consumers may additionally see `NoBlobMetadataReceivedError` on reads from testnet due to CORS gaps on a subset of Walrus storage nodes; the CLI smoke scripts hit the full node pool and are more reliable for verification.
|
|
365
|
+
- **Walrus uploads need WAL, not just SUI**. Fund the address from the [Walrus testnet faucet](https://docs.walrus.site/usage/web-tool.html#testnet-tokens) in addition to the [Sui faucet](https://faucet.sui.io/). Uploads error with `Insufficient balance of ::wal::WAL` if you skip this.
|
|
366
|
+
- **gRPC client only at v0.1.0**. The reader and adapter interfaces are typed against `Pick<SuiGrpcClient, ...>` from `@mysten/sui/grpc`. `SuiJsonRpcClient` from `@mysten/sui/jsonRpc` has differently-named methods (`getDynamicFields` vs `listDynamicFields`, etc.) and is not yet a drop-in alternative. JSON-RPC fallback is planned for v0.2.0; for now, environments that block gRPC need to proxy or use a gRPC-compatible RPC endpoint.
|
|
367
|
+
|
|
368
|
+
## Smoke scripts
|
|
369
|
+
|
|
370
|
+
The `scripts/` directory has end-to-end testnet smokes that cost real WAL and SUI. They're the canonical "this works against the live deployment" checks:
|
|
371
|
+
|
|
372
|
+
| Script | Exercises |
|
|
373
|
+
| ------------------------- | -------------------------------------------------- |
|
|
374
|
+
| `phase-2-publication.ts` | Publication CRUD |
|
|
375
|
+
| `phase-3-cap.ts` | Cap issue / revoke / destroy |
|
|
376
|
+
| `phase-4-collection.ts` | Blob and quilt-mode collection lifecycle |
|
|
377
|
+
| `phase-5-walrus.ts` | Walrus blob and quilt upload |
|
|
378
|
+
| `phase-6-blob.ts` | Entry lifecycle in a Blob collection |
|
|
379
|
+
| `phase-6-quilt.ts` | Entry lifecycle in a Quilt collection |
|
|
380
|
+
| `phase-7-encrypted.ts` | Seal encrypt + addEncryptedEntry + decrypt |
|
|
381
|
+
| `phase-6-blob-http.ts` | HTTP publisher upload + aggregator read; skips when `WALRUS_PUBLISHER_URL` unset |
|
|
382
|
+
| `phase-7-encrypted-http.ts` | HTTP variant of phase-7; skips when `WALRUS_PUBLISHER_URL` unset |
|
|
383
|
+
|
|
384
|
+
Each requires `PRIVATE_KEY` (Bech32 `suiprivkey1...`) on an address with testnet SUI; phase-5 onward also needs WAL on the same address. Phase-7 picks up Seal key servers from `morseConfig.sealKeyServers` (canonical testnet allowlist baked in) by default — pass `SEAL_KEY_SERVERS` only if you want to override with a custom set.
|
|
385
|
+
|
|
386
|
+
## Development
|
|
387
|
+
|
|
388
|
+
```sh
|
|
389
|
+
# from the repo root
|
|
390
|
+
bun install
|
|
391
|
+
|
|
392
|
+
# from morse-sdk/
|
|
393
|
+
bun run lint
|
|
394
|
+
bun run typecheck
|
|
395
|
+
bun run test
|
|
396
|
+
bun run test:coverage # 265 tests, ~97% line / ~96% function coverage at v0.1.0
|
|
397
|
+
bun run build
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
`bun test` is the unit test runner; `bun run test:coverage` adds a per-file coverage report. CI gates require all four (lint, typecheck, test, build) to pass; coverage is informational. End-to-end testnet smokes live in `scripts/` (above).
|
|
401
|
+
|
|
402
|
+
## License
|
|
403
|
+
|
|
404
|
+
MIT. See [LICENSE](./LICENSE).
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Narrow `Pick<SuiGrpcClient, ...>` interfaces used by the SDK.
|
|
3
|
+
* A full `SuiGrpcClient` satisfies all of these structurally.
|
|
4
|
+
*/
|
|
5
|
+
import type { SuiGrpcClient } from "@mysten/sui/grpc";
|
|
6
|
+
/** RPC methods for single-object reads, ownership lookup, and dynamic fields. */
|
|
7
|
+
export type ObjectReader = Pick<SuiGrpcClient, "getObject" | "listOwnedObjects" | "listDynamicFields" | "getDynamicField">;
|
|
8
|
+
/** Batch `getObjects`, used when resolving many IDs at once. */
|
|
9
|
+
export type BatchObjectReader = Pick<SuiGrpcClient, "getObjects">;
|
|
10
|
+
/** RPC methods needed to sign, submit, and await a transaction. */
|
|
11
|
+
export type TransactionExecutor = Pick<SuiGrpcClient, "signAndExecuteTransaction" | "waitForTransaction" | "simulateTransaction">;
|
|
12
|
+
//# sourceMappingURL=clients.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clients.d.ts","sourceRoot":"","sources":["../src/clients.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAItD,iFAAiF;AACjF,MAAM,MAAM,YAAY,GAAG,IAAI,CAC9B,aAAa,EACb,WAAW,GAAG,kBAAkB,GAAG,mBAAmB,GAAG,iBAAiB,CAC1E,CAAC;AAEF,gEAAgE;AAChE,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;AAIlE,mEAAmE;AACnE,MAAM,MAAM,mBAAmB,GAAG,IAAI,CACrC,aAAa,EACb,2BAA2B,GAAG,oBAAoB,GAAG,qBAAqB,CAC1E,CAAC"}
|
package/dist/clients.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clients.js","sourceRoot":"","sources":["../src/clients.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
package/dist/codecs.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime codecs: branded-ID constructors and enum converters between
|
|
3
|
+
* TypeScript values and on-chain Move representations.
|
|
4
|
+
*/
|
|
5
|
+
import { AccessPolicy, type BlobObjectId, type OwnerCapId, type PackageId, type PublicationId, type PublisherCapId, type QuiltPatchId, type RegistryId, StorageMode, type SuiAddress, type SuiObjectId, type WalrusBlobId } from "./types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Construct a `PackageId`. Input may be `0x` + 1-64 lowercase hex chars;
|
|
8
|
+
* the result is always zero-padded to 64 hex chars (Sui canonical form).
|
|
9
|
+
* @throws {ValidationError} On invalid shape.
|
|
10
|
+
*/
|
|
11
|
+
export declare function toPackageId(value: string): PackageId;
|
|
12
|
+
/** Construct a `RegistryId`. Input is normalized to canonical 64-char form. @throws {ValidationError} On invalid shape. */
|
|
13
|
+
export declare function toRegistryId(value: string): RegistryId;
|
|
14
|
+
/** Construct a `PublicationId`. Input is normalized to canonical 64-char form. @throws {ValidationError} On invalid shape. */
|
|
15
|
+
export declare function toPublicationId(value: string): PublicationId;
|
|
16
|
+
/** Construct an `OwnerCapId`. Input is normalized to canonical 64-char form. @throws {ValidationError} On invalid shape. */
|
|
17
|
+
export declare function toOwnerCapId(value: string): OwnerCapId;
|
|
18
|
+
/** Construct a `PublisherCapId`. Input is normalized to canonical 64-char form. @throws {ValidationError} On invalid shape. */
|
|
19
|
+
export declare function toPublisherCapId(value: string): PublisherCapId;
|
|
20
|
+
/** Construct a `BlobObjectId`. Input is normalized to canonical 64-char form. @throws {ValidationError} On invalid shape. */
|
|
21
|
+
export declare function toBlobObjectId(value: string): BlobObjectId;
|
|
22
|
+
/** Construct a `SuiAddress`. Input is normalized to canonical 64-char form. @throws {ValidationError} On invalid shape. */
|
|
23
|
+
export declare function toSuiAddress(value: string): SuiAddress;
|
|
24
|
+
/** Construct a `SuiObjectId`. Input is normalized to canonical 64-char form. @throws {ValidationError} On invalid shape. */
|
|
25
|
+
export declare function toSuiObjectId(value: string): SuiObjectId;
|
|
26
|
+
/**
|
|
27
|
+
* Construct a `WalrusBlobId` from a URL-safe base64 string (43 chars,
|
|
28
|
+
* unpadded). @throws {ValidationError} On invalid shape.
|
|
29
|
+
*/
|
|
30
|
+
export declare function toWalrusBlobId(value: string): WalrusBlobId;
|
|
31
|
+
/**
|
|
32
|
+
* Construct a `QuiltPatchId` by validating length only. Use the structural
|
|
33
|
+
* codec in `walrus/quilt-patch-id.ts` for `{quiltId, version, startIndex,
|
|
34
|
+
* endIndex}` round-trips. @throws {ValidationError} On wrong length.
|
|
35
|
+
*/
|
|
36
|
+
export declare function toQuiltPatchId(bytes: Uint8Array): QuiltPatchId;
|
|
37
|
+
/** Convert `StorageMode` to Move `u8`. */
|
|
38
|
+
export declare function storageModeToU8(mode: StorageMode): number;
|
|
39
|
+
/** Convert Move `u8` to `StorageMode`. @throws {ValidationError} On unknown values. */
|
|
40
|
+
export declare function storageModeFromU8(value: number): StorageMode;
|
|
41
|
+
/** Convert `AccessPolicy` to Move `u8`. */
|
|
42
|
+
export declare function accessPolicyToU8(policy: AccessPolicy): number;
|
|
43
|
+
/** Convert Move `u8` to `AccessPolicy`. @throws {ValidationError} On unknown values. */
|
|
44
|
+
export declare function accessPolicyFromU8(value: number): AccessPolicy;
|
|
45
|
+
//# sourceMappingURL=codecs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codecs.d.ts","sourceRoot":"","sources":["../src/codecs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACN,YAAY,EACZ,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,cAAc,EAEnB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,WAAW,EACX,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,MAAM,YAAY,CAAC;AA8BpB;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAEpD;AAED,2HAA2H;AAC3H,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAEtD;AAED,8HAA8H;AAC9H,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAE5D;AAED,4HAA4H;AAC5H,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAEtD;AAED,+HAA+H;AAC/H,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAE9D;AAED,6HAA6H;AAC7H,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAE1D;AAED,2HAA2H;AAC3H,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAEtD;AAED,4HAA4H;AAC5H,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAExD;AAOD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAQ1D;AAID;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,YAAY,CAQ9D;AAID,0CAA0C;AAC1C,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAKzD;AAED,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAW5D;AAID,2CAA2C;AAC3C,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAQ7D;AAED,wFAAwF;AACxF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAc9D"}
|