@misofm/musicos 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.
Files changed (103) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +75 -0
  3. package/dist/client.d.ts +309 -0
  4. package/dist/client.d.ts.map +1 -0
  5. package/dist/client.js +216 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/contracts/musicos/composition.d.ts +195 -0
  8. package/dist/contracts/musicos/composition.d.ts.map +1 -0
  9. package/dist/contracts/musicos/composition.js +195 -0
  10. package/dist/contracts/musicos/composition.js.map +1 -0
  11. package/dist/contracts/musicos/deps/bps/bps.d.ts +17 -0
  12. package/dist/contracts/musicos/deps/bps/bps.d.ts.map +1 -0
  13. package/dist/contracts/musicos/deps/bps/bps.js +19 -0
  14. package/dist/contracts/musicos/deps/bps/bps.js.map +1 -0
  15. package/dist/contracts/musicos/recording.d.ts +238 -0
  16. package/dist/contracts/musicos/recording.d.ts.map +1 -0
  17. package/dist/contracts/musicos/recording.js +232 -0
  18. package/dist/contracts/musicos/recording.js.map +1 -0
  19. package/dist/contracts/musicos/release.d.ts +298 -0
  20. package/dist/contracts/musicos/release.d.ts.map +1 -0
  21. package/dist/contracts/musicos/release.js +298 -0
  22. package/dist/contracts/musicos/release.js.map +1 -0
  23. package/dist/contracts/musicos/track.d.ts +193 -0
  24. package/dist/contracts/musicos/track.d.ts.map +1 -0
  25. package/dist/contracts/musicos/track.js +197 -0
  26. package/dist/contracts/musicos/track.js.map +1 -0
  27. package/dist/contracts/utils/index.d.ts +104 -0
  28. package/dist/contracts/utils/index.d.ts.map +1 -0
  29. package/dist/contracts/utils/index.js +272 -0
  30. package/dist/contracts/utils/index.js.map +1 -0
  31. package/dist/contracts.d.ts +9 -0
  32. package/dist/contracts.d.ts.map +1 -0
  33. package/dist/contracts.js +14 -0
  34. package/dist/contracts.js.map +1 -0
  35. package/dist/deployments.d.ts +60 -0
  36. package/dist/deployments.d.ts.map +1 -0
  37. package/dist/deployments.js +109 -0
  38. package/dist/deployments.js.map +1 -0
  39. package/dist/events.d.ts +16 -0
  40. package/dist/events.d.ts.map +1 -0
  41. package/dist/events.js +25 -0
  42. package/dist/events.js.map +1 -0
  43. package/dist/execute.d.ts +49 -0
  44. package/dist/execute.d.ts.map +1 -0
  45. package/dist/execute.js +110 -0
  46. package/dist/execute.js.map +1 -0
  47. package/dist/index.d.ts +13 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +20 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/internal.d.ts +17 -0
  52. package/dist/internal.d.ts.map +1 -0
  53. package/dist/internal.js +46 -0
  54. package/dist/internal.js.map +1 -0
  55. package/dist/numeric.d.ts +5 -0
  56. package/dist/numeric.d.ts.map +1 -0
  57. package/dist/numeric.js +21 -0
  58. package/dist/numeric.js.map +1 -0
  59. package/dist/packages.d.ts +67 -0
  60. package/dist/packages.d.ts.map +1 -0
  61. package/dist/packages.js +85 -0
  62. package/dist/packages.js.map +1 -0
  63. package/dist/parsers.d.ts +9 -0
  64. package/dist/parsers.d.ts.map +1 -0
  65. package/dist/parsers.js +40 -0
  66. package/dist/parsers.js.map +1 -0
  67. package/dist/queries.d.ts +178 -0
  68. package/dist/queries.d.ts.map +1 -0
  69. package/dist/queries.js +611 -0
  70. package/dist/queries.js.map +1 -0
  71. package/dist/transactions.d.ts +136 -0
  72. package/dist/transactions.d.ts.map +1 -0
  73. package/dist/transactions.js +145 -0
  74. package/dist/transactions.js.map +1 -0
  75. package/dist/types.d.ts +194 -0
  76. package/dist/types.d.ts.map +1 -0
  77. package/dist/types.js +4 -0
  78. package/dist/types.js.map +1 -0
  79. package/dist/view.d.ts +22 -0
  80. package/dist/view.d.ts.map +1 -0
  81. package/dist/view.js +42 -0
  82. package/dist/view.js.map +1 -0
  83. package/package.json +119 -0
  84. package/src/client.ts +341 -0
  85. package/src/contracts/musicos/composition.ts +280 -0
  86. package/src/contracts/musicos/deps/bps/bps.ts +22 -0
  87. package/src/contracts/musicos/recording.ts +308 -0
  88. package/src/contracts/musicos/release.ts +409 -0
  89. package/src/contracts/musicos/track.ts +255 -0
  90. package/src/contracts/utils/index.ts +428 -0
  91. package/src/contracts.ts +29 -0
  92. package/src/deployments.ts +166 -0
  93. package/src/events.ts +30 -0
  94. package/src/execute.ts +137 -0
  95. package/src/index.ts +21 -0
  96. package/src/internal.ts +78 -0
  97. package/src/numeric.ts +25 -0
  98. package/src/packages.ts +117 -0
  99. package/src/parsers.ts +66 -0
  100. package/src/queries.ts +928 -0
  101. package/src/transactions.ts +305 -0
  102. package/src/types.ts +222 -0
  103. package/src/view.ts +65 -0
@@ -0,0 +1,305 @@
1
+ // Copyright (c) Miso Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // Transaction builders. Every builder adds commands to a caller-owned
5
+ // `Transaction`, so flows compose in a single PTB.
6
+ // The `create*` primitives take the `Transaction` as their FIRST argument and
7
+ // return their by-value results, so those results can be threaded into later
8
+ // commands. Miso calls go through the codegen-generated, type-safe call
9
+ // functions.
10
+ //
11
+ // This module keeps the bare protocol PRIMITIVES only. The rule is: do the
12
+ // minimum the Move semantics FORCE, and return anything the caller could
13
+ // legitimately route elsewhere.
14
+ //
15
+ // `createComposition` and `createRecording` each append a
16
+ // single `::new` and hand back its by-value results — never dispersing a share
17
+ // supply, publishing (sharing) the object, or routing an admin cap.
18
+ //
19
+ // Each created object MUST still be consumed by its `publish` in the same PTB
20
+ // (`Composition`, `Recording`, and `Release` are all `key`-only with no `drop`),
21
+ // but that is a same-TRANSACTION requirement, not a same-FUNCTION one: a later
22
+ // `finalize*` command on the same `tx` satisfies it. Keeping them apart is what
23
+ // lets the intermediate value be used at all — borrowing a still-unshared
24
+ // composition into `recording::new`, attaching a royalty pool via `uid_mut`, or
25
+ // sending an admin cap somewhere other than a plain address (every admin cap is
26
+ // `key, store`, so it is freely routable).
27
+ //
28
+ // This package exposes the complete low-level current ABI. The platform SDK
29
+ // should own product-specific orchestration (cap disposition, currency
30
+ // lifecycle, authority-package workflows) on top of these composable calls.
31
+
32
+ import {
33
+ Transaction,
34
+ type TransactionObjectArgument,
35
+ } from "@mysten/sui/transactions";
36
+
37
+ import * as composition from "./contracts/musicos/composition.ts";
38
+ import * as recording from "./contracts/musicos/recording.ts";
39
+ import * as release from "./contracts/musicos/release.ts";
40
+ import { asU256, type UnsignedInput } from "./numeric.ts";
41
+ import * as track from "./contracts/musicos/track.ts";
42
+
43
+ /** A thunk that adds commands to a transaction. May be async (resolves at build time). */
44
+ export type TxThunk = (tx: Transaction) => void | Promise<void>;
45
+
46
+ // ============================================================================
47
+ // Shared inputs
48
+ // ============================================================================
49
+
50
+ /**
51
+ * Share-currency binding for a work: the fully-qualified `share::Share` type, the
52
+ * `Currency<Share>` object, and the `TreasuryCap<Share>`. All three are known once
53
+ * the currency has been published + initialized, so builders take them explicitly
54
+ * rather than reading them from chain — keeping the thunks synchronous and
55
+ * composable (no RPC inside the transaction build).
56
+ */
57
+ export interface ShareCurrencyBinding {
58
+ /** The `${packageId}::share::Share` type. */
59
+ shareType: string;
60
+ /** The `Currency<Share>` object id. */
61
+ shareCurrencyId: string;
62
+ /** The `TreasuryCap<Share>` object id (held by the caller, consumed by `new`). */
63
+ shareTreasuryCapId: string;
64
+ }
65
+
66
+ // ============================================================================
67
+ // Composition
68
+ // ============================================================================
69
+
70
+ /** The three by-value results of `composition::new`, for threading onward in a PTB. */
71
+ export interface CompositionParts {
72
+ composition: TransactionObjectArgument;
73
+ adminCap: TransactionObjectArgument;
74
+ /** The creator's freshly-minted share supply (a `Balance<Share>`). */
75
+ balance: TransactionObjectArgument;
76
+ }
77
+
78
+ export interface CreateCompositionParams extends ShareCurrencyBinding {
79
+ title: string;
80
+ royaltyRateBps: number;
81
+ misoPackageId: string;
82
+ }
83
+
84
+ /**
85
+ * PRIMITIVE. Appends `composition::new` and returns its by-value results without
86
+ * dispersing, sharing, or transferring anything — the caller decides what happens
87
+ * next (share it, keep it unshared to bundle with a recording, attach a dynamic
88
+ * field via `uid_mut`, …). Composes with anything in the same PTB.
89
+ */
90
+ export function createComposition(tx: Transaction, params: CreateCompositionParams): CompositionParts {
91
+ const result = tx.add(
92
+ composition._new({
93
+ package: params.misoPackageId,
94
+ typeArguments: [params.shareType],
95
+ arguments: [tx.pure.string(params.title), tx.pure.u16(params.royaltyRateBps), tx.object(params.shareCurrencyId), tx.object(params.shareTreasuryCapId)],
96
+ }),
97
+ );
98
+ return { composition: result[0]!, adminCap: result[1]!, balance: result[2]! };
99
+ }
100
+
101
+ // ============================================================================
102
+ // Recording
103
+ // ============================================================================
104
+
105
+ /** The three by-value results of `recording::new`, for threading onward in a PTB. */
106
+ export interface RecordingParts {
107
+ recording: TransactionObjectArgument;
108
+ adminCap: TransactionObjectArgument;
109
+ /** The creator's remaining share supply after the composition's cut is split off. */
110
+ balance: TransactionObjectArgument;
111
+ }
112
+
113
+ export interface CreateRecordingParams extends ShareCurrencyBinding {
114
+ /** Share type of the parent composition (the recording's `CompositionShare` phantom). */
115
+ compositionShareType: string;
116
+ /**
117
+ * Parent `Composition`, passed by immutable reference (`recording::new` reads only
118
+ * its id + royalty rate). May be an on-chain object (`tx.object(id)`) or a still-
119
+ * unshared, transaction-local `createComposition(...).composition` result.
120
+ */
121
+ composition: TransactionObjectArgument;
122
+ misoPackageId: string;
123
+ }
124
+
125
+ /**
126
+ * PRIMITIVE. Appends `recording::new` and returns its by-value results without
127
+ * publishing/dispersing/transferring. Because it borrows the composition by
128
+ * reference, it can run against a composition that is still an unshared PTB-local
129
+ * value — the borrow-before-share pattern that lets a composition + recording share
130
+ * one PTB.
131
+ */
132
+ export function createRecording(tx: Transaction, params: CreateRecordingParams): RecordingParts {
133
+ const result = tx.add(
134
+ recording._new({
135
+ package: params.misoPackageId,
136
+ typeArguments: [params.shareType, params.compositionShareType],
137
+ arguments: [params.composition, tx.object(params.shareCurrencyId), tx.object(params.shareTreasuryCapId)],
138
+ }),
139
+ );
140
+ return { recording: result[0]!, adminCap: result[1]!, balance: result[2]! };
141
+ }
142
+
143
+ // ============================================================================
144
+ // Track
145
+ // ============================================================================
146
+
147
+ export interface CreateTrackParams {
148
+ /** Shared recording object ID, unless `recording` is PTB-local. */
149
+ recordingId?: string;
150
+ /** A recording returned by `createRecording` earlier in this same PTB. */
151
+ recording?: TransactionObjectArgument;
152
+ recordingAdminCapId?: string;
153
+ recordingAdminCap?: TransactionObjectArgument;
154
+ /** Share type of the recording (the track's `RecordingShare` phantom). */
155
+ recordingShareType: string;
156
+ /** Share type of the parent composition (the track's `CompositionShare` phantom). */
157
+ compositionShareType: string;
158
+ targetReleaseId: string;
159
+ trackSplitBps: number;
160
+ misoPackageId: string;
161
+ }
162
+
163
+ /**
164
+ * PRIMITIVE. Appends `track::new` and returns its by-value `Track`. A track has
165
+ * `drop, store`, so callers may leave it unused; to assemble a release, pass
166
+ * returned tracks to `tx.makeMoveVec({ type: `${misoPackageId}::track::Track`,
167
+ * elements })` and then pass that vector to `createRelease` with its shared
168
+ * `ReleaseRegistry` object.
169
+ *
170
+ * The `recordingAdminCap` may be passed as an on-chain object id
171
+ * (`recordingAdminCapId`) or as a PTB-local argument (`recordingAdminCap`) —
172
+ * the latter lets a track be created against a recording created earlier in the
173
+ * same transaction, before its cap has been transferred anywhere.
174
+ */
175
+ export function createTrack(tx: Transaction, params: CreateTrackParams): TransactionObjectArgument {
176
+ if (!params.recordingAdminCap && !params.recordingAdminCapId) {
177
+ throw new Error("createTrack: recordingAdminCapId or recordingAdminCap required");
178
+ }
179
+ if (!params.recording && !params.recordingId) {
180
+ throw new Error("createTrack: recordingId or recording required");
181
+ }
182
+ const adminCapArg = params.recordingAdminCap ?? tx.object(params.recordingAdminCapId!);
183
+ const recordingArg = params.recording ?? tx.object(params.recordingId!);
184
+ return tx.add(
185
+ track._new({
186
+ package: params.misoPackageId,
187
+ typeArguments: [params.recordingShareType, params.compositionShareType],
188
+ arguments: [
189
+ adminCapArg,
190
+ recordingArg,
191
+ tx.pure.id(params.targetReleaseId),
192
+ tx.pure.u16(params.trackSplitBps),
193
+ ],
194
+ }),
195
+ );
196
+ }
197
+
198
+ // ============================================================================
199
+ // Publish finalizers
200
+ // ============================================================================
201
+
202
+ /** Consume and share an initialized composition. The generated call supplies Clock. */
203
+ export function publishComposition(
204
+ tx: Transaction,
205
+ params: {
206
+ composition: TransactionObjectArgument;
207
+ adminCap: TransactionObjectArgument;
208
+ shareType: string;
209
+ misoPackageId: string;
210
+ },
211
+ ): void {
212
+ tx.add(
213
+ composition.publish({
214
+ package: params.misoPackageId,
215
+ typeArguments: [params.shareType],
216
+ arguments: [params.composition, params.adminCap],
217
+ }),
218
+ );
219
+ }
220
+
221
+ /** Consume and share an initialized recording. The generated call supplies Clock. */
222
+ export function publishRecording(
223
+ tx: Transaction,
224
+ params: {
225
+ recording: TransactionObjectArgument;
226
+ adminCap: TransactionObjectArgument;
227
+ recordingShareType: string;
228
+ compositionShareType: string;
229
+ misoPackageId: string;
230
+ },
231
+ ): void {
232
+ tx.add(
233
+ recording.publish({
234
+ package: params.misoPackageId,
235
+ typeArguments: [params.recordingShareType, params.compositionShareType],
236
+ arguments: [params.recording, params.adminCap],
237
+ }),
238
+ );
239
+ }
240
+
241
+ /** Consume and share an initialized release. The generated call supplies Clock. */
242
+ export function publishRelease(
243
+ tx: Transaction,
244
+ params: {
245
+ release: TransactionObjectArgument;
246
+ adminCap: TransactionObjectArgument;
247
+ misoPackageId: string;
248
+ },
249
+ ): void {
250
+ tx.add(
251
+ release.publish({
252
+ package: params.misoPackageId,
253
+ arguments: [params.release, params.adminCap],
254
+ }),
255
+ );
256
+ }
257
+
258
+ // ============================================================================
259
+ // Core release registry
260
+ // ============================================================================
261
+
262
+ /** The by-value result of core `release::new`. Publish it in this PTB. */
263
+ export interface ReleaseParts {
264
+ release: TransactionObjectArgument;
265
+ adminCap: TransactionObjectArgument;
266
+ }
267
+
268
+ export interface CreateReleaseParams {
269
+ /** Shared, canonical `miso::release::ReleaseRegistry` object ID. */
270
+ releaseRegistryId: string;
271
+ title: string;
272
+ /** `Track` values returned by {@link createTrack}; all are consumed. */
273
+ tracks: readonly TransactionObjectArgument[];
274
+ nonce: UnsignedInput;
275
+ /** Freshly published core `miso` package ID. */
276
+ misoPackageId: string;
277
+ }
278
+
279
+ /**
280
+ * Assemble a release through core `miso::release::new`. The registry is the
281
+ * first object argument; there is no arbitrary-parent or utility-package path.
282
+ * Both returned values are non-drop and must be published or otherwise consumed
283
+ * in this PTB.
284
+ */
285
+ export function createRelease(
286
+ tx: Transaction,
287
+ params: CreateReleaseParams,
288
+ ): ReleaseParts {
289
+ const tracks = tx.makeMoveVec({
290
+ type: `${params.misoPackageId}::track::Track`,
291
+ elements: [...params.tracks],
292
+ });
293
+ const result = tx.add(
294
+ release._new({
295
+ package: params.misoPackageId,
296
+ arguments: [
297
+ tx.object(params.releaseRegistryId),
298
+ tx.pure.string(params.title),
299
+ tracks,
300
+ tx.pure.u256(asU256("release nonce", params.nonce)),
301
+ ],
302
+ }),
303
+ );
304
+ return { release: result[0]!, adminCap: result[1]! };
305
+ }
package/src/types.ts ADDED
@@ -0,0 +1,222 @@
1
+ // Copyright (c) Miso Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // ============================================================================
5
+ // Common
6
+ // ============================================================================
7
+
8
+ /** Basis points value (0-10000, where 10000 = 100%). */
9
+ export interface BPS {
10
+ value: number;
11
+ }
12
+
13
+ // ============================================================================
14
+ // Composition
15
+ // ============================================================================
16
+
17
+ /** Lifecycle state of a composition. */
18
+ export type CompositionState =
19
+ | { type: "Initialized" }
20
+ | { type: "Published"; timestampMs: number };
21
+
22
+ /**
23
+ * A musical composition representing the underlying written work.
24
+ *
25
+ * Compositions are the written musical works (songs, instrumentals) that
26
+ * recordings are based on. Each composition has its own share token for
27
+ * ownership distribution.
28
+ *
29
+ * State machine: Initialized -> Published (immutable after publish)
30
+ */
31
+ export interface Composition {
32
+ /** Unique identifier for this composition. */
33
+ id: string;
34
+ /** Current lifecycle state. */
35
+ state: CompositionState;
36
+ /** Primary title of the composition. */
37
+ title: string;
38
+ /**
39
+ * Royalty rate this composition earns from each recording's revenue (basis
40
+ * points, 0-10000). Immutable for the composition's lifetime.
41
+ */
42
+ royaltyRate: BPS;
43
+ }
44
+
45
+ /**
46
+ * Emitted once when a composition is published. A pure pointer carrying only the
47
+ * composition's identity — an indexer fetches the full immutable object by
48
+ * `compositionId`.
49
+ */
50
+ export interface CompositionPublishedEvent {
51
+ compositionId: string;
52
+ }
53
+
54
+ /**
55
+ * Admin cap for a Composition, derived deterministically from the Composition object ID.
56
+ *
57
+ * The share type parameter T is extracted from the on-chain type
58
+ * `CompositionAdminCap<T>` where T is the composition's share token type.
59
+ */
60
+ export interface CompositionAdminCap {
61
+ /** The object ID of the admin cap. */
62
+ id: string;
63
+ /** The share type parameter T from CompositionAdminCap<T>. */
64
+ shareType: string;
65
+ }
66
+
67
+ // ============================================================================
68
+ // Recording
69
+ // ============================================================================
70
+
71
+ /** Lifecycle state of a recording. */
72
+ export type RecordingState =
73
+ | { type: "Initialized" }
74
+ | { type: "Published"; timestampMs: number };
75
+
76
+ /**
77
+ * An audio recording of a composition.
78
+ *
79
+ * Recordings are the audio performances that are distributed and played.
80
+ * Each recording has its own share token for ownership distribution.
81
+ *
82
+ * A recording carries no name of its own: its display title is its
83
+ * composition's title, resolved through the recording's `CompositionShare`
84
+ * type parameter. Richer naming ("(Live)", localized titles) lives in the
85
+ * metadata extension.
86
+ *
87
+ * State machine: Initialized -> Published (immutable after publish)
88
+ */
89
+ export interface Recording {
90
+ /** Unique identifier for this recording. */
91
+ id: string;
92
+ /** Current lifecycle state. */
93
+ state: RecordingState;
94
+ /**
95
+ * Object ID of the parent composition. An identity/membership handle — not a
96
+ * revenue routing target: the composition is paid via its recording-share
97
+ * ownership, settled at recording creation. Immutable.
98
+ */
99
+ compositionId: string;
100
+ }
101
+
102
+ /**
103
+ * Emitted once when a recording is published. A pure pointer carrying only the
104
+ * recording's identity — an indexer fetches the full immutable object by
105
+ * `recordingId`.
106
+ */
107
+ export interface RecordingPublishedEvent {
108
+ recordingId: string;
109
+ }
110
+
111
+ /**
112
+ * Emitted when recording creation grants the composition its immutable royalty
113
+ * rate's share of the new recording currency.
114
+ */
115
+ export interface CompositionSharesGrantedEvent {
116
+ recordingId: string;
117
+ compositionId: string;
118
+ /** Recording-share base units sent to the composition address. */
119
+ value: string;
120
+ /** The immutable composition royalty rate applied at recording creation. */
121
+ rateBps: number;
122
+ grantedBy: string;
123
+ }
124
+
125
+ /**
126
+ * Admin cap for a Recording, derived deterministically from the Recording object ID.
127
+ *
128
+ * The share type parameter T is extracted from the on-chain type
129
+ * `RecordingAdminCap<T>` where T is the recording's share token type.
130
+ */
131
+ export interface RecordingAdminCap {
132
+ /** The object ID of the admin cap. */
133
+ id: string;
134
+ /** The share type parameter T from RecordingAdminCap<T>. */
135
+ shareType: string;
136
+ }
137
+
138
+ // Track
139
+ // ============================================================================
140
+
141
+ /** Lifecycle state of a track on a release. */
142
+ export type TrackState = "Unassigned" | "Assigned";
143
+
144
+ /**
145
+ * A track on a release, linking a recording to its position in the tracklist.
146
+ * The recording is the handle through which all other metadata (share types,
147
+ * composition lineage, and — via the composition — the display title) is
148
+ * reached.
149
+ */
150
+ export interface Track {
151
+ /** Current state of the track (Unassigned until the release claims it, then Assigned). */
152
+ state: TrackState;
153
+ /**
154
+ * ID of the composition underlying this track's recording. An identity/
155
+ * membership handle — not a revenue routing target: the composition is paid
156
+ * via its recording-share ownership, and a track routes its full split to the
157
+ * recording.
158
+ */
159
+ compositionId: string;
160
+ /** ID of the recording on this track. */
161
+ recordingId: string;
162
+ /** Revenue split for this track within the release (in basis points). */
163
+ splitBps: BPS;
164
+ }
165
+
166
+ // ============================================================================
167
+ // Release
168
+ // ============================================================================
169
+
170
+ /** Lifecycle state of a release. */
171
+ export type ReleaseState =
172
+ | { type: "Initialized" }
173
+ | { type: "Published"; timestampMs: number };
174
+
175
+ /**
176
+ * A music release (album, EP, or single).
177
+ *
178
+ * A release is a flat, ordered tracklist with per-track revenue distribution
179
+ * configuration. Display grouping (discs, vinyl sides), cover art, and edition
180
+ * naming live in extensions — the stored tracklist has the same shape as the
181
+ * digest pre-image every track consented to.
182
+ *
183
+ * State machine: Initialized -> Published (immutable after publish)
184
+ */
185
+ export interface Release {
186
+ /** Unique identifier for this release. */
187
+ id: string;
188
+ /** Current lifecycle state. */
189
+ state: ReleaseState;
190
+ /** Title of the release. */
191
+ title: string;
192
+ /** The ordered tracklist. */
193
+ tracks: Track[];
194
+ }
195
+
196
+ /**
197
+ * Emitted once when a release is published. A pure pointer carrying only the
198
+ * release's identity — an indexer fetches the full immutable object by
199
+ * `releaseId`.
200
+ */
201
+ export interface ReleasePublishedEvent {
202
+ releaseId: string;
203
+ }
204
+
205
+ /** Emitted when package initialization shares the canonical release registry. */
206
+ export interface ReleaseRegistryCreatedEvent {
207
+ registryId: string;
208
+ createdBy: string;
209
+ }
210
+
211
+ /**
212
+ * Admin cap for a Release, derived deterministically from the Release object ID.
213
+ *
214
+ * Unlike Composition and Recording admin caps, ReleaseAdminCap is not generic
215
+ * (Release has no share type parameter) and stores a reference to its Release.
216
+ */
217
+ export interface ReleaseAdminCap {
218
+ /** The object ID of the admin cap. */
219
+ id: string;
220
+ /** The object ID of the Release this cap administers. */
221
+ releaseId: string;
222
+ }
package/src/view.ts ADDED
@@ -0,0 +1,65 @@
1
+ // Copyright (c) Miso Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // Simulate-based reads (the `view` surface). These build a transaction and run it
5
+ // through the Core `simulateTransaction` API to read a computed value without
6
+ // changing state — used where the value is a pure function of inputs the chain
7
+ // derives (e.g. the deterministic release id).
8
+
9
+ import { Transaction } from "@mysten/sui/transactions";
10
+ import { bcs } from "@mysten/sui/bcs";
11
+ import type { ClientWithCoreApi } from "@mysten/sui/client";
12
+ import * as release from "./contracts/musicos/release.ts";
13
+ import { asU256, asU64, type UnsignedInput } from "./numeric.ts";
14
+
15
+ export interface DeriveTargetReleaseIdParams {
16
+ /** Sender for the simulation (any address; not charged). */
17
+ sender: string;
18
+ /** Recording object ids, in track order. */
19
+ recordingIds: string[];
20
+ /** Per-track split basis points, aligned to `recordingIds`. */
21
+ splitBps: UnsignedInput[];
22
+ /** The release nonce (u256 as a decimal string). */
23
+ nonce: UnsignedInput;
24
+ /** Shared canonical core `miso::release::ReleaseRegistry` object ID. */
25
+ releaseRegistryId: string;
26
+ }
27
+
28
+ /**
29
+ * Derives the release id the on-chain `release::new` will produce for these
30
+ * inputs, via a `simulateTransaction` call to `release::derive_target_release_id`.
31
+ * Tracks embedded in a release must reference this exact ID, so it is computed
32
+ * up front and threaded into the core release builder.
33
+ */
34
+ export async function deriveTargetReleaseId(
35
+ client: ClientWithCoreApi,
36
+ misoPackageId: string,
37
+ params: DeriveTargetReleaseIdParams,
38
+ ): Promise<string> {
39
+ if (params.recordingIds.length !== params.splitBps.length) {
40
+ throw new Error(`deriveTargetReleaseId: recordingIds (${params.recordingIds.length}) and splitBps (${params.splitBps.length}) length mismatch.`);
41
+ }
42
+
43
+ const tx = new Transaction();
44
+ tx.setSender(params.sender);
45
+ tx.add(
46
+ release.deriveTargetReleaseId({
47
+ package: misoPackageId,
48
+ arguments: [
49
+ params.releaseRegistryId,
50
+ params.recordingIds,
51
+ params.splitBps.map((v) => asU64("track split bps", v)),
52
+ asU256("release nonce", params.nonce),
53
+ ],
54
+ }),
55
+ );
56
+
57
+ // gRPC/Core equivalent of devInspect: simulate with per-command return values.
58
+ const res = await client.core.simulateTransaction({ transaction: tx, include: { commandResults: true } });
59
+ if (res.$kind !== "Transaction") {
60
+ throw new Error(`derive_target_release_id simulation failed: ${JSON.stringify(res.FailedTransaction.status)}`);
61
+ }
62
+ const returned = res.commandResults?.[0]?.returnValues?.[0]?.bcs;
63
+ if (!returned) throw new Error("derive_target_release_id returned no value.");
64
+ return bcs.Address.parse(returned);
65
+ }