@misofm/platform 0.0.0 → 0.18.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.
Files changed (168) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +686 -3
  3. package/dist/auth.d.ts +73 -0
  4. package/dist/auth.d.ts.map +1 -0
  5. package/dist/auth.js +193 -0
  6. package/dist/auth.js.map +1 -0
  7. package/dist/catalog.d.ts +48 -0
  8. package/dist/catalog.d.ts.map +1 -0
  9. package/dist/catalog.js +92 -0
  10. package/dist/catalog.js.map +1 -0
  11. package/dist/client.d.ts +462 -0
  12. package/dist/client.d.ts.map +1 -0
  13. package/dist/client.js +788 -0
  14. package/dist/client.js.map +1 -0
  15. package/dist/contracts.d.ts +34 -0
  16. package/dist/contracts.d.ts.map +1 -0
  17. package/dist/contracts.js +49 -0
  18. package/dist/contracts.js.map +1 -0
  19. package/dist/cover.d.ts +69 -0
  20. package/dist/cover.d.ts.map +1 -0
  21. package/dist/cover.js +132 -0
  22. package/dist/cover.js.map +1 -0
  23. package/dist/credits.d.ts +200 -0
  24. package/dist/credits.d.ts.map +1 -0
  25. package/dist/credits.js +465 -0
  26. package/dist/credits.js.map +1 -0
  27. package/dist/deployments.d.ts +224 -0
  28. package/dist/deployments.d.ts.map +1 -0
  29. package/dist/deployments.js +177 -0
  30. package/dist/deployments.js.map +1 -0
  31. package/dist/execute.d.ts +30 -0
  32. package/dist/execute.d.ts.map +1 -0
  33. package/dist/execute.js +31 -0
  34. package/dist/execute.js.map +1 -0
  35. package/dist/index.d.ts +21 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +52 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/internal.d.ts +13 -0
  40. package/dist/internal.d.ts.map +1 -0
  41. package/dist/internal.js +41 -0
  42. package/dist/internal.js.map +1 -0
  43. package/dist/mix.d.ts +144 -0
  44. package/dist/mix.d.ts.map +1 -0
  45. package/dist/mix.js +656 -0
  46. package/dist/mix.js.map +1 -0
  47. package/dist/pressing.d.ts +137 -0
  48. package/dist/pressing.d.ts.map +1 -0
  49. package/dist/pressing.js +390 -0
  50. package/dist/pressing.js.map +1 -0
  51. package/dist/publication.d.ts +216 -0
  52. package/dist/publication.d.ts.map +1 -0
  53. package/dist/publication.js +872 -0
  54. package/dist/publication.js.map +1 -0
  55. package/dist/queries.d.ts +2 -0
  56. package/dist/queries.d.ts.map +1 -0
  57. package/dist/queries.js +8 -0
  58. package/dist/queries.js.map +1 -0
  59. package/dist/read/artist.d.ts +18 -0
  60. package/dist/read/artist.d.ts.map +1 -0
  61. package/dist/read/artist.js +86 -0
  62. package/dist/read/artist.js.map +1 -0
  63. package/dist/read/catalog.d.ts +93 -0
  64. package/dist/read/catalog.d.ts.map +1 -0
  65. package/dist/read/catalog.js +441 -0
  66. package/dist/read/catalog.js.map +1 -0
  67. package/dist/read/client.d.ts +37 -0
  68. package/dist/read/client.d.ts.map +1 -0
  69. package/dist/read/client.js +50 -0
  70. package/dist/read/client.js.map +1 -0
  71. package/dist/read/config.d.ts +68 -0
  72. package/dist/read/config.d.ts.map +1 -0
  73. package/dist/read/config.js +63 -0
  74. package/dist/read/config.js.map +1 -0
  75. package/dist/read/genres.d.ts +7 -0
  76. package/dist/read/genres.d.ts.map +1 -0
  77. package/dist/read/genres.js +38 -0
  78. package/dist/read/genres.js.map +1 -0
  79. package/dist/read/index.d.ts +14 -0
  80. package/dist/read/index.d.ts.map +1 -0
  81. package/dist/read/index.js +23 -0
  82. package/dist/read/index.js.map +1 -0
  83. package/dist/read/internal/scalars.d.ts +23 -0
  84. package/dist/read/internal/scalars.d.ts.map +1 -0
  85. package/dist/read/internal/scalars.js +57 -0
  86. package/dist/read/internal/scalars.js.map +1 -0
  87. package/dist/read/internal/walrus.d.ts +6 -0
  88. package/dist/read/internal/walrus.d.ts.map +1 -0
  89. package/dist/read/internal/walrus.js +29 -0
  90. package/dist/read/internal/walrus.js.map +1 -0
  91. package/dist/read/receipts.d.ts +41 -0
  92. package/dist/read/receipts.d.ts.map +1 -0
  93. package/dist/read/receipts.js +398 -0
  94. package/dist/read/receipts.js.map +1 -0
  95. package/dist/read/types.d.ts +328 -0
  96. package/dist/read/types.d.ts.map +1 -0
  97. package/dist/read/types.js +21 -0
  98. package/dist/read/types.js.map +1 -0
  99. package/dist/read/wallet.d.ts +74 -0
  100. package/dist/read/wallet.d.ts.map +1 -0
  101. package/dist/read/wallet.js +578 -0
  102. package/dist/read/wallet.js.map +1 -0
  103. package/dist/read/works.d.ts +29 -0
  104. package/dist/read/works.d.ts.map +1 -0
  105. package/dist/read/works.js +191 -0
  106. package/dist/read/works.js.map +1 -0
  107. package/dist/recording-extensions.d.ts +62 -0
  108. package/dist/recording-extensions.d.ts.map +1 -0
  109. package/dist/recording-extensions.js +159 -0
  110. package/dist/recording-extensions.js.map +1 -0
  111. package/dist/release-extensions.d.ts +93 -0
  112. package/dist/release-extensions.d.ts.map +1 -0
  113. package/dist/release-extensions.js +190 -0
  114. package/dist/release-extensions.js.map +1 -0
  115. package/dist/release-graph.d.ts +65 -0
  116. package/dist/release-graph.d.ts.map +1 -0
  117. package/dist/release-graph.js +136 -0
  118. package/dist/release-graph.js.map +1 -0
  119. package/dist/share-template.d.ts +3 -0
  120. package/dist/share-template.d.ts.map +1 -0
  121. package/dist/share-template.js +46 -0
  122. package/dist/share-template.js.map +1 -0
  123. package/dist/share.d.ts +67 -0
  124. package/dist/share.d.ts.map +1 -0
  125. package/dist/share.js +157 -0
  126. package/dist/share.js.map +1 -0
  127. package/dist/transactions.d.ts +216 -0
  128. package/dist/transactions.d.ts.map +1 -0
  129. package/dist/transactions.js +314 -0
  130. package/dist/transactions.js.map +1 -0
  131. package/dist/vault.d.ts +364 -0
  132. package/dist/vault.d.ts.map +1 -0
  133. package/dist/vault.js +507 -0
  134. package/dist/vault.js.map +1 -0
  135. package/package.json +145 -5
  136. package/src/auth.ts +275 -0
  137. package/src/catalog.ts +189 -0
  138. package/src/client.ts +1320 -0
  139. package/src/contracts.ts +54 -0
  140. package/src/cover.ts +240 -0
  141. package/src/credits.ts +906 -0
  142. package/src/deployments.ts +357 -0
  143. package/src/execute.ts +52 -0
  144. package/src/index.ts +69 -0
  145. package/src/internal.ts +46 -0
  146. package/src/mix.ts +910 -0
  147. package/src/pressing.ts +705 -0
  148. package/src/publication.ts +1214 -0
  149. package/src/queries.ts +9 -0
  150. package/src/read/artist.ts +118 -0
  151. package/src/read/catalog.ts +668 -0
  152. package/src/read/client.ts +87 -0
  153. package/src/read/config.ts +138 -0
  154. package/src/read/genres.ts +37 -0
  155. package/src/read/index.ts +85 -0
  156. package/src/read/internal/scalars.ts +59 -0
  157. package/src/read/internal/walrus.ts +42 -0
  158. package/src/read/receipts.ts +476 -0
  159. package/src/read/types.ts +415 -0
  160. package/src/read/wallet.ts +687 -0
  161. package/src/read/works.ts +297 -0
  162. package/src/recording-extensions.ts +256 -0
  163. package/src/release-extensions.ts +311 -0
  164. package/src/release-graph.ts +219 -0
  165. package/src/share-template.ts +55 -0
  166. package/src/share.ts +261 -0
  167. package/src/transactions.ts +542 -0
  168. package/src/vault.ts +1009 -0
package/README.md CHANGED
@@ -1,6 +1,689 @@
1
1
  # @misofm/platform
2
2
 
3
- Placeholder publish reserving this name so a trusted publisher can be
4
- configured before the first real release. Contains no code.
3
+ The complete client SDK for the **Miso platform layer** on Sui: composed catalog,
4
+ artist, wallet, and receipt reads; the record production line and sale of copies;
5
+ and fail-closed Vault custody, raw Actions, and safe crank plugins built on `@misofm/protocol`'s
6
+ protocol and data-extension primitives.
5
7
 
6
- Use `@misofm/platform` >= 0.12.0 (protocol) / >= 0.18.0 (platform).
8
+ ## The split
9
+
10
+ Miso ships two SDK packages, and the package name tells you which promise you are
11
+ holding:
12
+
13
+ | Package | Layer | Owns |
14
+ | -------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
15
+ | `@misofm/protocol` | **Protocol** | Composition, Recording, Release, Party identity; metadata/data extensions; utilities; generic royalty-pool and routed-stake primitives |
16
+ | `@misofm/platform` | **Platform** | Pressing, Listing, Record, and an explicitly deployed Vault/Action/plugin compatibility set |
17
+
18
+ A release is protocol. Pressing a record off that release and selling it is
19
+ platform. So is deciding _what to do_ with a freshly-minted work's share
20
+ supply — the protocol only knows how to mint one. Keeping the boundary at the
21
+ package line is what stops the open protocol from quietly growing a
22
+ storefront (or an opinion about tokenomics).
23
+
24
+ Extensions add data to a work. Raw Actions accept an admin cap and remain
25
+ composable with either direct authority or a scoped Vault borrow. Three safe,
26
+ permissionless crank plugins borrow through their own witness: a shared
27
+ `Vault<AdminCap>` custodies the raw cap, while its owner holds a
28
+ `VaultAdminCap<AdminCap>`. A plugin borrows the cap and must return the exact
29
+ object in the same PTB. The SDK supports both vault authorities and legacy
30
+ address-owned admin caps explicitly; it never silently treats a legacy cap as a
31
+ vaulted one. New Vault IDs are derived from the shared `VaultRegistry`, the raw
32
+ cap ID, and its type; each VaultAdminCap ID is then derived from its Vault.
33
+
34
+ This package depends on `@misofm/protocol` directly and imports its bare
35
+ `createComposition`/`createRecording` primitives, composing them with its own
36
+ minato-dispersal and share-currency logic in the same PTB — the
37
+ transaction-thunk composition pattern from the
38
+ [Sui SDK building guide](https://sdk.mystenlabs.com/sui/sdk-building), just
39
+ crossing a package boundary.
40
+
41
+ ```sh
42
+ bun add @misofm/platform@^0.18.0
43
+ ```
44
+
45
+ `@misofm/protocol` resolves transitively through that dependency, so
46
+ applications get exactly one protocol SDK and one compatible deployment map
47
+ without installing it themselves. Registration takes a recursively frozen
48
+ snapshot of custom deployment/config records without freezing the caller's
49
+ original objects, so later caller mutation cannot retarget an existing client.
50
+
51
+ ### Canonical Record-gated sessions
52
+
53
+ `@misofm/platform/mix` defines the complete protected-playback wire contract. A
54
+ Record resolves its immutable Release track to a Recording, then derives that
55
+ Recording's `recording_engine_session::ExtensionKey` dynamic field directly.
56
+ The field names one plaintext Walrus `miso.engine-session/1` document containing
57
+ the encrypted stem blob IDs and one Seal-wrapped 32-byte session key. No event
58
+ scan, indexer-maintained relationship, or second encrypted manifest is needed.
59
+
60
+ The same entry point exposes exact identity encoding, strict canonical-session
61
+ parsing, Seal-envelope inspection, and explicit attach/replace/unset PTB
62
+ builders. The bundled Testnet deployment pins the Record, policy, gate, and
63
+ engine-session identities together at `recordSales.recordPackageId`,
64
+ `packages.recordSealPolicy`, `objects.recordGate`, and
65
+ `packages.recordingEngineSession`.
66
+
67
+ ## The model
68
+
69
+ A release may have one `Pressing` per positive `u16` edition. Each Pressing owns its
70
+ independent `u32` Record-number sequence, current supply, optional immutable `u32`
71
+ maximum supply, and authorized distributor witness types.
72
+
73
+ Selling in a currency is a `Listing<Currency>`, one per currency, permanent, edited in
74
+ place rather than replaced. The Listing's enabled/disabled state is the sale switch;
75
+ the Pressing has no schedule state.
76
+
77
+ **Everything is address math.** The pressing's UID derives off its release's, each
78
+ listing's off the pressing's. The protocol's canonical `ReleaseRegistry` creates the
79
+ release; there is no _pressing_ registry or mutable lookup pointer to follow, so
80
+ "where is it" is answered offline. A Pressing derives from `(release, edition)`, a
81
+ Record from `(pressing, number)`, and a `Listing<Currency>` from its Pressing under
82
+ the separate immutable Record Shop package. There are no Record Registry or Settings
83
+ singletons.
84
+
85
+ ## Usage
86
+
87
+ Register the client extension on any client implementing Sui's Core API
88
+ ([SDK building guidelines](https://sdk.mystenlabs.com/sui/sdk-building)):
89
+
90
+ ```ts
91
+ import { SuiGrpcClient } from "@mysten/sui/grpc";
92
+ import { Transaction } from "@mysten/sui/transactions";
93
+ import { miso } from "@misofm/platform";
94
+
95
+ const client = new SuiGrpcClient({ network: "testnet", baseUrl }).$extend(
96
+ miso({ deployment: verifiedDeployment }),
97
+ );
98
+
99
+ // One memoized Core API read proves this endpoint is the deployment's exact
100
+ // ledger before any synchronous client-bound builder can be used.
101
+ await client.miso.ready();
102
+
103
+ // The permissionless protocol SDK is part of the same facade.
104
+ const release = await client.miso.protocol.getReleaseById(releaseId);
105
+ const party = await client.miso.party.getPartyById(partyId);
106
+
107
+ // Read: run + one currency's offer, one round trip, no registry lookup.
108
+ const { pressing, listing } = await client.miso.getSale({
109
+ releaseId,
110
+ edition,
111
+ currencyType: USD_COIN_TYPE,
112
+ });
113
+
114
+ // Write: a thunk, so it composes with protocol calls in the same PTB.
115
+ const tx = new Transaction();
116
+ tx.add(
117
+ client.miso.tx.purchaseRecord({
118
+ releaseId,
119
+ edition,
120
+ currencyType: USD_COIN_TYPE,
121
+ paymentAmount: listing.pricing.amount,
122
+ expectedPricing: listing.pricing,
123
+ recipient: buyer,
124
+ }),
125
+ );
126
+ ```
127
+
128
+ The bundled Testnet deployment includes both verified immutable package IDs.
129
+ Custom deployments can still mark sales unavailable explicitly; sales builders
130
+ and readers fail closed whenever either package is unavailable.
131
+
132
+ Holding the ids yourself? The bare APIs take `recordPackageId` and/or
133
+ `recordShopPackageId` explicitly:
134
+
135
+ ```ts
136
+ import { purchaseRecord, getSale } from "@misofm/platform/pressing";
137
+ ```
138
+
139
+ Verified package and singleton IDs are bundled in
140
+ `MISO_PLATFORM_DEPLOYMENTS.testnet`. Calling `miso()` selects that verified map
141
+ from the Sui client's network. Unbundled and custom networks still fail closed
142
+ unless the caller passes one complete deployment through `miso({ deployment })`.
143
+
144
+ ### High-level platform reads
145
+
146
+ `@misofm/platform/read` turns protocol, pressing, Party, credits, cover, and wallet
147
+ objects into the JSON-safe views a client actually renders. It works in browsers,
148
+ Workers, and servers. Miso's HTTP API is a thin validated and cached transport over
149
+ this same surface, not a separate domain implementation.
150
+
151
+ ```ts
152
+ import {
153
+ createMisoClient,
154
+ getDiscoverShelf,
155
+ getReleaseDetail,
156
+ getOwnedRecords,
157
+ } from "@misofm/platform/read";
158
+
159
+ const miso = createMisoClient({ config: verifiedReadConfig });
160
+
161
+ const discover = await getDiscoverShelf(miso);
162
+ const release = await getReleaseDetail(miso, releaseId);
163
+ const library = await getOwnedRecords(miso, walletAddress);
164
+ ```
165
+
166
+ The package root also exposes the same functions under the `read` namespace:
167
+
168
+ ```ts
169
+ import { read } from "@misofm/platform";
170
+
171
+ const miso = read.createMisoClient({ config: verifiedReadConfig });
172
+ const artist = await read.getArtistProfile(miso, partyId);
173
+ ```
174
+
175
+ ### Authenticated platform mutations
176
+
177
+ `@misofm/platform/auth` implements Miso's Enoki + Sui personal-message authorization
178
+ protocol without owning session state or private credentials. It asks the API
179
+ for a short-lived, method/path-bound challenge, validates the response, signs
180
+ the exact bytes with the caller's Sui signer, and sends the authenticated
181
+ mutation.
182
+
183
+ ```ts
184
+ import { authenticatedFetch } from "@misofm/platform/auth";
185
+
186
+ await authenticatedFetch(
187
+ "https://api.testnet.miso.fm/platform/usernames/alice",
188
+ {
189
+ method: "PUT",
190
+ body: JSON.stringify({}),
191
+ headers: { "Content-Type": "application/json" },
192
+ auth: {
193
+ token: enokiOidcToken,
194
+ address: suiAddress,
195
+ signer: await enokiFlow.getKeypair({ network: "testnet" }),
196
+ network: "testnet",
197
+ },
198
+ },
199
+ );
200
+ ```
201
+
202
+ The SDK is only a client and shared wire contract. The API remains the security
203
+ boundary: it verifies Enoki membership, challenge freshness, the recovered Sui
204
+ address, and the exact authorized route on every protected request.
205
+
206
+ ### Payment
207
+
208
+ `listing::purchase` takes a bare `Balance<Currency>`, and `purchaseRecord` sources it with
209
+ `tx.balance()` — which draws from the buyer's **address balance** first and falls back
210
+ to coin objects only if it must. When the address balance covers the price, that is a
211
+ single `balance::redeem_funds` and **no coin object is minted, touched, or destroyed**,
212
+ leaving the sale free of owned-object contention.
213
+
214
+ Never hand-pick coin objects for a payment. That road shows a buyer their $1,000 and
215
+ then refuses to spend a cent of it, because a coin listing cannot see money that lives
216
+ in the address balance.
217
+
218
+ Purchases always set `useGasCoin: false`: the gas coin may belong to a sponsor. Buyers
219
+ also pass the exact expected `Fixed` or `Floor` pricing variant and value, protecting
220
+ them from stale pricing-mode changes as well as amount changes.
221
+
222
+ ### Vault fund settlement
223
+
224
+ `settleAndDistributeReleaseRevenue` invokes the fixed release plugin with the
225
+ framework `AccumulatorRoot` (`0xacc`); the plugin redeems the whole settled
226
+ snapshot and exposes no amount argument. `settleCompositionRoyaltyPool` and
227
+ `settleRecordingRoyaltyPool` read `balance::settled_funds_value` and pass that
228
+ command result directly to their exact-value plugin calls. The lower-level raw
229
+ Action `redeemAndDistributeReleaseRevenue` and the plugin helpers
230
+ `redeemAndDepositCompositionRoyaltyPool`, and
231
+ `redeemAndDepositRecordingRoyaltyPool` remain available when an earlier PTB
232
+ command already produced the exact value.
233
+
234
+ Party-wallet monetary builders are similarly composable:
235
+ `receivePartyWalletBalance`, `redeemPartyWalletBalance`, and
236
+ `settlePartyWalletBalance` return the PTB `Balance<Currency>` result. Pass that
237
+ result directly to another Move call, or convert it with `coin::from_balance`
238
+ only when an owned Coin is required. Every returned Balance must be consumed in
239
+ the same PTB.
240
+
241
+ ## Publishing (`transactions.ts`, `share.ts`, `release-graph.ts`)
242
+
243
+ `@misofm/protocol`'s `createComposition`/`createRecording` mint a work and hand
244
+ back its by-value parts (the object, its admin cap, its freshly-minted share
245
+ `Balance`) without dispersing, sharing, or transferring anything. This package
246
+ supplies the opinionated finish on top:
247
+
248
+ ```ts
249
+ import { miso } from "@misofm/platform";
250
+
251
+ const client = new SuiGrpcClient({ network: "testnet", baseUrl }).$extend(
252
+ miso({ deployment: verifiedDeployment }),
253
+ );
254
+
255
+ // Mints the composition's share supply, disperses it to shareRecipients as
256
+ // address balances, publishes (shares) the composition, and transfers the
257
+ // CompositionAdminCap to adminAddress — createComposition → finalizeComposition
258
+ // in one PTB.
259
+ const thunk = client.miso.tx.publishComposition({
260
+ title: "Song Title",
261
+ royaltyRateBps: 1000,
262
+ shareType: "0x...::share::Share",
263
+ shareCurrencyId: "0x...",
264
+ shareTreasuryCapId: "0x...",
265
+ shareRecipients: [{ address: ownerAddress, value: 10_000_000_000_000 }],
266
+ adminAddress: ownerAddress,
267
+ });
268
+ ```
269
+
270
+ `client.miso.tx.publishRecording` and `publishCompositionAndRecording`
271
+ follow the same shape (the latter atomically, borrow-before-share, in one PTB —
272
+ see `@misofm/protocol`'s README for why the ordering is load-bearing).
273
+ The protocol, immutable Record and Record Shop packages, minato, and core
274
+ `ReleaseRegistry` address all come from the deployment selected by the Sui
275
+ client's network. Record sales have no Record Registry or Settings singleton.
276
+ The deprecated `misoPlatform()` constructor still accepts those values manually
277
+ for compatibility with existing integrations. It must also receive the exact
278
+ `network` and `chainIdentifier`; call `await client.misoPlatform.ready()` before
279
+ accessing its client-bound protocol or platform surfaces.
280
+
281
+ For custom PTBs, the bare primitives (`disperseShares`, `finalizeComposition`,
282
+ `finalizeRecording`) and the whole-graph orchestrator are exported standalone:
283
+
284
+ ```ts
285
+ import { publishReleaseGraph } from "@misofm/platform";
286
+
287
+ // Every composition and recording, optional royalty pools, tracks, and
288
+ // the release — with the release id derived ON-CHAIN — in one atomic PTB.
289
+ const thunk = publishReleaseGraph({
290
+ compositions: [
291
+ {
292
+ shareType,
293
+ shareCurrencyId,
294
+ shareTreasuryCapId,
295
+ title: "Song",
296
+ royaltyRateBps: 1000,
297
+ shareRecipients,
298
+ adminAddress,
299
+ },
300
+ ],
301
+ recordings: [
302
+ {
303
+ shareType,
304
+ shareCurrencyId,
305
+ shareTreasuryCapId,
306
+ compositionShareType,
307
+ parentCompositionIndex: 0,
308
+ shareRecipients,
309
+ adminAddress,
310
+ },
311
+ ],
312
+ release: {
313
+ title: "Album",
314
+ nonce: "42",
315
+ adminAddress,
316
+ releaseRegistryId: "0x...",
317
+ tracks: [{ recordingIndex: 0, splitBps: 10000 }],
318
+ },
319
+ misoPackageId: "0x...",
320
+ minatoPackageId: "0x...",
321
+ });
322
+ ```
323
+
324
+ ### Atomic catalog publication (`publication.ts`)
325
+
326
+ `publishAtomicCatalog` owns the semantic publication transaction. Given
327
+ pre-initialized share currencies, it creates every new Party, Composition,
328
+ Recording, Track, and Release; applies all declared data extensions; composes
329
+ raw-cap Actions through direct custody or a scoped Vault borrow; installs only
330
+ the permissionless royalty/revenue crank plugins; opens the Pressing and
331
+ Listings; shares the new objects; and delivers only the selected direct admin
332
+ cap or VaultAdminCap. The
333
+ entire catalog stage is one PTB, so none of it can land partially.
334
+
335
+ Share allocation is explicit at the SDK boundary. Omitting
336
+ `shareDistribution` preserves the existing `"balance"` behavior. Setting it to
337
+ `"stake"` converts the minted `Balance<Share>` into one address-owned
338
+ `Stake<Share>` per `shareRecipients` entry. When the work also declares a
339
+ `royaltyPool`, the builder creates the pool unshared, registers each fresh
340
+ stake, shares the pool, and then transfers the registered stakes. The lower
341
+ level `createShareStake`, `createShareStakes`, `registerShareStake`,
342
+ `newCompositionRoyaltyPool`, `newRecordingRoyaltyPool`, and
343
+ `shareRoyaltyPool` builders expose each step separately for custom PTBs.
344
+
345
+ For a fresh Recording whose parent Composition owns a protocol royalty cut,
346
+ `recordings[].routedStake` redeems that exact cut into a derived
347
+ `RoutedStake`, registers it with the Recording's royalty pool, and shares it
348
+ for permissionless sweeping. The Recording and parent Composition must both
349
+ declare royalty pools in the same currency, and the parent must use Vault
350
+ custody:
351
+
352
+ ```ts
353
+ recordings: [{
354
+ // ...fresh parent, share currency, custody, and royaltyPool...
355
+ routedStake: true,
356
+ }]
357
+ ```
358
+
359
+ The SDK derives the exact `composition_routed_stake::create_stake` value from
360
+ the protocol's fixed share supply and the fresh parent's `royaltyRateBps`; it
361
+ does not permit zero or partial routing. Both works must use `"stake"` share
362
+ distribution so the Recording pool has its complete supply registered and the
363
+ parent destination pool is operable from the first sweep. Atomic publication
364
+ supports rates from 1 to 9999 BPS because it always allocates a non-zero
365
+ Recording creator remainder; lower-level routed-stake builders remain
366
+ available for the Move layer's 100% composition-cut case.
367
+
368
+ ```ts
369
+ import {
370
+ assertAtomicPublicationBounds,
371
+ parseAtomicPublicationResult,
372
+ publishAtomicCatalog,
373
+ } from "@misofm/platform/publication";
374
+
375
+ const publication = {
376
+ deployment,
377
+ parties,
378
+ compositions, // includes initialized share Currency + TreasuryCap ids
379
+ recordings,
380
+ release,
381
+ pressing,
382
+ };
383
+
384
+ // Pure local assembly: fail before publishing any share package if the final
385
+ // PTB exceeds the SDK's command/input safety limits or has an invalid graph.
386
+ assertAtomicPublicationBounds(publication);
387
+
388
+ const executed = await client.miso.executeViaExecutor(
389
+ executor,
390
+ publishAtomicCatalog(publication),
391
+ );
392
+ const result = parseAtomicPublicationResult(publication, executed);
393
+ ```
394
+
395
+ Fresh raw PartyAdminCap, CompositionAdminCap, RecordingAdminCap, and
396
+ ReleaseAdminCap values never leave the PTB when Vault custody is selected.
397
+ Only the Composition royalty-pool, Recording royalty-pool, and Release revenue
398
+ plugins are installable, while each new Vault is still owned. Party-wallet and
399
+ Composition routed-stake operations remain raw Actions. Atomic publication
400
+ consumes the returned routed stake by registering and sharing it; lower-level
401
+ callers retain explicit control over those lifecycle steps. Plugin witness
402
+ construction remains inside the SDK bindings.
403
+
404
+ Share packages necessarily precede this stage: publish at most five per PTB,
405
+ then initialize their currencies, then submit the atomic catalog PTB. The two
406
+ share helpers below accept a parallel-compatible executor, allowing package
407
+ batches to queue concurrently while a hardware signer serializes approvals.
408
+
409
+ ### Share Currency Provisioning (`share.ts`)
410
+
411
+ Every composition and recording is backed by its own fixed-supply share
412
+ currency: an independently published `share` package (bytecode template
413
+ embedded as `SHARE_TEMPLATE`, initializer patched via `patchInitializer`).
414
+ Publish and initialize are necessarily two transactions:
415
+
416
+ ```ts
417
+ // Sequential (one currency, two txs):
418
+ const currency = await client.miso.createShareCurrency(signer, {
419
+ name: "Song Shares",
420
+ description: "…",
421
+ });
422
+ // → { packageId, currencyId, shareType, treasuryCapId, gasUsed }
423
+
424
+ // Batched (many currencies, via a ParallelTransactionExecutor):
425
+ import { publishShareCurrencies, initializeShareCurrencies } from "@misofm/platform";
426
+ const { packageIds } = await publishShareCurrencies(
427
+ executor,
428
+ initializerAddress,
429
+ 10,
430
+ );
431
+ const { currencies } = await initializeShareCurrencies(
432
+ executor,
433
+ signerAddress,
434
+ packageIds,
435
+ (pkg) => ({
436
+ name: "…",
437
+ description: "…",
438
+ }),
439
+ );
440
+ ```
441
+
442
+ `executeViaExecutor(executor, ...thunks)` (`execute.ts`) submits a
443
+ non-idempotent PTB through a `ParallelTransactionExecutor` exactly once (no
444
+ auto-retry) — it's what the batched provisioning above builds on, layered over
445
+ `@misofm/protocol`'s transport-agnostic `buildTx`/`toExecResult`.
446
+
447
+ ## Extensions
448
+
449
+ An extension attaches data to a protocol work through that work's cap-gated
450
+ `uid_mut` hook. The work stays a protocol object; the opinion hanging off it is
451
+ ours.
452
+
453
+ ### Credits (`credits.ts`)
454
+
455
+ Contributor credits pair a party with a display name and one or more
456
+ domain-specific roles, attached to a work as a dynamic field and gated by the
457
+ work's admin cap. Three role vocabularies:
458
+
459
+ - **Composition** (writing, 1–5 roles, no level): `Adapter`, `Arranger`, `Composer`, `Lyricist`, `Songwriter`, `Translator`, or `{ type: "Custom", name }`.
460
+ - **Recording** (production/performance, 1–10 roles): 28 leveled roles (`Producer`, `Vocalist`, `Engineer`, …) each with an optional seniority `level` (`Lead`, `Featured`, `Executive`, …), plus `{ type: "Instrumentalist", instrument, level? }`, `{ type: "Custom", name, level? }`, and the unleveled `ArtistsAndRepertoire` / `Copyist`.
461
+ - **Release** (top-line billing, exactly one role): `"Primary"` or `"Featured"`.
462
+
463
+ Writers validate client-side, mirroring the Move aborts: display name
464
+ non-empty and ≤200 UTF-8 bytes; role counts within the caps above; no
465
+ duplicate roles.
466
+
467
+ ```ts
468
+ import {
469
+ attachCompositionCredit,
470
+ attachRecordingCredit,
471
+ addReleaseCredit,
472
+ addRecordingPrimaryArtist,
473
+ addRecordingFeaturedArtist,
474
+ getCompositionCredits,
475
+ getRecordingCredits,
476
+ getReleaseCredits,
477
+ } from "@misofm/platform";
478
+
479
+ const thunk = attachRecordingCredit({
480
+ recordingId: "0x...",
481
+ recordingAdminCapId: "0x...",
482
+ partyId: "0x...",
483
+ displayName: "Jane Doe",
484
+ roles: [
485
+ { type: "Vocalist", level: "Lead" },
486
+ { type: "Instrumentalist", instrument: "Guitar" },
487
+ ],
488
+ recordingShareType: "0x...::share::Share",
489
+ compositionShareType: "0x...::share::Share",
490
+ recordingCreditsPackageId: "0x...",
491
+ misoCreditPackageId: "0x...",
492
+ });
493
+
494
+ // Designate an already-credited party (same params minus displayName/roles/misoCreditPackageId):
495
+ addRecordingPrimaryArtist({
496
+ recordingId,
497
+ recordingAdminCapId,
498
+ partyId,
499
+ recordingShareType,
500
+ compositionShareType,
501
+ recordingCreditsPackageId,
502
+ });
503
+
504
+ // Reads return null when no credits field is attached.
505
+ const credits = await getCompositionCredits(
506
+ client,
507
+ compositionId,
508
+ compositionCreditsPackageId,
509
+ );
510
+ // CreditView[]: { partyId, displayName, roles: string[] } — e.g. "Producer (Lead)", "Instrumentalist: Guitar"
511
+ const rc = await getRecordingCredits(
512
+ client,
513
+ recordingId,
514
+ recordingCreditsPackageId,
515
+ );
516
+ // { credits: CreditView[], primaryArtistIds: string[], featuredArtistIds: string[] }
517
+ ```
518
+
519
+ `attachCompositionCredit` takes `compositionId`/`compositionAdminCapId`/`compositionShareType`/`compositionCreditsPackageId`;
520
+ `addReleaseCredit` takes `releaseId`/`releaseAdminCapId` and a single `role`.
521
+
522
+ A recording is `Recording<RecordingShare, CompositionShare>` — the recording's
523
+ OWN share type comes first, its parent composition's second. The recording
524
+ writers take both as separate named params for that reason; passing them in the
525
+ wrong order still typechecks (both are `string`) and resolves to the wrong
526
+ on-chain type.
527
+
528
+ ### Cover art (`cover.ts`)
529
+
530
+ A release's cover is a Walrus blob referenced on-chain via `ori::WalrusData`,
531
+ attached under the `release_cover_art` extension:
532
+
533
+ ```ts
534
+ import { setReleaseCover, getReleaseCover } from "@misofm/platform";
535
+
536
+ const thunk = setReleaseCover({
537
+ releaseId: "0x...",
538
+ releaseAdminCapId: "0x...",
539
+ stillBlobId: "987654321", // Walrus blob id as u256 (decimal string or bigint)
540
+ animatedBlobId: null, // optional animated cover
541
+ coverArtPackageId: "0x...",
542
+ releaseCoverArtPackageId: "0x...",
543
+ oriPackageId: "0x...",
544
+ });
545
+
546
+ const cover = await getReleaseCover(
547
+ client,
548
+ releaseId,
549
+ releaseCoverArtPackageId,
550
+ );
551
+ // ReleaseCoverView | null: { still, animated } as normalized Walrus refs
552
+ // ({ kind: "blob", blobId } | { kind: "quiltPatch", quiltId, version, startIndex, endIndex })
553
+ ```
554
+
555
+ ### Vault operations (`vault.ts`)
556
+
557
+ `vault.ts` contains composable PTB builders for custody and plugin flows:
558
+ `invokeWithAdminCap` safely sequences `borrow_as_admin → Move call → put_back`, and
559
+ `custodyNewAdminCap` shares the Vault while transferring only its owner-held
560
+ `VaultAdminCap` through the Vault module. `deriveVaultId` and
561
+ `deriveVaultAdminCapId` discover both canonical object IDs without an RPC lookup.
562
+ `withdrawVaultCapability` and `restoreVaultCapability` operate on the permanent
563
+ Vault shell; withdrawal requires every plugin to have been removed. Plugin
564
+ installers construct their witnesses inside their Move package; callers supply no
565
+ witness.
566
+
567
+ It also builds Composition/Recording royalty-pool initialization and cranks,
568
+ fixed Release settlement plus raw-admin amount composition, Party wallet
569
+ Actions, and the full Composition routed-stake Action lifecycle. Receive flows
570
+ take exact object references and construct the required
571
+ `vector<Receiving<Coin<Currency>>>` in the PTB.
572
+
573
+ The bundled Testnet deployment sets `operations.status` to `"available"` with
574
+ one canonical Vault package and registry, five distinct raw Action packages,
575
+ and three distinct suffixed plugin packages. Zero-config Testnet clients expose
576
+ that complete verified surface after `await client.miso.ready()`. Custom
577
+ deployments remain fail-closed unless they provide the same atomic identity set.
578
+ Structural validation checks canonical, pairwise-distinct IDs; callers remain
579
+ responsible for the provenance and compatibility of arbitrary custom IDs. The
580
+ bundled map is recursively frozen from one verified immutable admin export.
581
+
582
+ ### Migrating from 0.16
583
+
584
+ Version 0.17 is a breaking deployment-safety release. Replace flat Vault,
585
+ Action, and plugin package fields with the discriminated `operations` union.
586
+ After client
587
+ registration, call `await client.miso.ready()` before using synchronous
588
+ `client.miso.tx`, `ids`, `call`, `vault`, or `party` surfaces. Platform reads
589
+ and SDK execution helpers await the same memoized readiness check themselves.
590
+ Standalone builders remain pure for offline composition, so their caller is
591
+ responsible for completing this exact-chain validation lifecycle before
592
+ execution.
593
+
594
+ The Release revenue plugin crank is now fixed: call
595
+ `redeemAllAndDistribute(vault, release, accumulatorRoot)` with no amount. The
596
+ explicit-amount `redeemAndDistribute` Action remains available only for raw
597
+ admin-cap composition.
598
+
599
+ ### Extension types
600
+
601
+ ```ts
602
+ import type {
603
+ CreditView,
604
+ RecordingCreditsView,
605
+ CompositionRole,
606
+ RecordingRole,
607
+ RecordingRoleLevel,
608
+ RecordingLeveledRoleType,
609
+ ReleaseRole,
610
+ ReleaseCoverView,
611
+ CoverImageRef,
612
+ } from "@misofm/platform";
613
+ ```
614
+
615
+ `RecordingLeveledRoleType` is the union of the 28 recording role base names that
616
+ carry an optional `RecordingRoleLevel` (`Producer`, `Vocalist`, `Engineer`,
617
+ `Conductor`, …) — the leveled arm of `RecordingRole`. The other arms
618
+ (`Instrumentalist`, `Custom`, and the unleveled `ArtistsAndRepertoire` /
619
+ `Copyist`) are spelled out separately in `RecordingRole`.
620
+
621
+ ## Layout
622
+
623
+ ```
624
+ src/
625
+ deployments.ts fail-closed deployment schema and future address injection point
626
+ client.ts the full client.miso facade; protocol and Party live at client.miso.protocol / .party
627
+ pressing.ts facade: builders, readers, and the id derivations
628
+ queries.ts shared read plumbing (isNotFound, re-exported from @misofm/protocol)
629
+ transactions.ts the TxThunk contract + the opinionated publish flow (disperse/finalize/publish*)
630
+ release-graph.ts whole release graph in one PTB (publishReleaseGraph)
631
+ share.ts share-currency provisioning (createShareCurrency, batched variants)
632
+ share-template.ts embedded `share` package bytecode
633
+ credits.ts EXTENSION: contributor credits + the three role vocabularies
634
+ cover.ts EXTENSION: release cover art (Walrus blob via ori)
635
+ read/ high-level catalog, artist, wallet, and receipt views
636
+ vault.ts Vault authority, plugin, event, and receiving-coin builders
637
+ execute.ts executeViaExecutor, layered on @misofm/protocol's buildTx/toExecResult
638
+ internal.ts private helpers (the 0x1::option moveCall targets) — NOT exported
639
+ contracts.ts barrel re-exporting the generated bindings as `contracts`
640
+ contracts/ GENERATED — do not edit by hand
641
+ ```
642
+
643
+ ## Codegen
644
+
645
+ Bindings are generated from the live Move source, so the typed layer cannot drift from
646
+ the on-chain ABI:
647
+
648
+ ```sh
649
+ bun run codegen # reads sui-codegen.config.ts → src/contracts/
650
+ ```
651
+
652
+ `sui-codegen.config.ts` lists `miso_record`, `miso_record_shop`, data
653
+ extensions, generic `royalty_pool`/`routed_stake`, and the `vault` plus all
654
+ vault-plugin packages. The protocol CORE (`miso` —
655
+ composition/recording/release/track) generates into
656
+ `@misofm/protocol` instead, which this package depends on for those bindings —
657
+ adding the core here to save an import is how the split this package exists to
658
+ enforce gets undone.
659
+
660
+ Paths resolve against sibling checkouts, so regenerating requires
661
+ `~/Documents/GitHub/misofm/{sdk, record, record-shop, vault, vault-plugins}` and
662
+ `~/Documents/GitHub/misonetwork/{party-actions, protocol, protocol-actions,
663
+ protocol-extensions, royalty-pool, routed-stake, share, cover-art, genre}`.
664
+
665
+ For an isolated checkout, copy those source trees and set
666
+ `MISO_SDK_CODEGEN_SOURCE_ROOT` to their common parent. The codegen config reads
667
+ only from that copy, avoiding writes to a developer's live source tree.
668
+
669
+ ## Dependency on `@misofm/protocol`
670
+
671
+ `@misofm/protocol` is a regular runtime dependency of this package (a workspace
672
+ dependency in this monorepo, resolved to a published version range on publish),
673
+ not a peer. This package imports its primitives, deployment configuration,
674
+ protocol client, and Party client directly, then exposes them at
675
+ `client.miso.protocol` and `client.miso.party` only after
676
+ `await client.miso.ready()` validates the exact ledger. Direct protocol reads,
677
+ generated calls, package bindings, and nested Party APIs cannot be obtained
678
+ before that gate.
679
+
680
+ `@mysten/sui` itself stays a peer dependency here, so an application resolves
681
+ exactly one Sui SDK across both packages regardless of which protocol version
682
+ `@misofm/platform` pins.
683
+
684
+ ```bash
685
+ bun add @misofm/platform
686
+ ```
687
+
688
+ `@misofm/protocol` resolves automatically as a transitive dependency; there is
689
+ no separate install step and no peer version for consumers to reconcile.