@misofm/sdk 0.5.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -9
- package/dist/client.d.ts +17 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +22 -3
- package/dist/client.js.map +1 -1
- package/dist/contracts/release_registry/release_registry.d.ts +114 -0
- package/dist/contracts/release_registry/release_registry.d.ts.map +1 -0
- package/dist/contracts/release_registry/release_registry.js +119 -0
- package/dist/contracts/release_registry/release_registry.js.map +1 -0
- package/dist/contracts/royalty_pool/pool.d.ts +28 -2
- package/dist/contracts/royalty_pool/pool.d.ts.map +1 -1
- package/dist/contracts/royalty_pool/pool.js +28 -2
- package/dist/contracts/royalty_pool/pool.js.map +1 -1
- package/dist/contracts/utils/index.d.ts +44 -0
- package/dist/contracts/utils/index.d.ts.map +1 -1
- package/dist/contracts/utils/index.js +104 -2
- package/dist/contracts/utils/index.js.map +1 -1
- package/dist/contracts.d.ts +1 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +4 -1
- package/dist/contracts.js.map +1 -1
- package/dist/cover.d.ts +3 -6
- package/dist/cover.d.ts.map +1 -1
- package/dist/cover.js +7 -16
- package/dist/cover.js.map +1 -1
- package/dist/credits.d.ts.map +1 -1
- package/dist/credits.js +17 -43
- package/dist/credits.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/read/catalog.d.ts +2 -12
- package/dist/read/catalog.d.ts.map +1 -1
- package/dist/read/catalog.js +9 -24
- package/dist/read/catalog.js.map +1 -1
- package/dist/read/config.d.ts +3 -8
- package/dist/read/config.d.ts.map +1 -1
- package/dist/read/config.js +15 -50
- package/dist/read/config.js.map +1 -1
- package/dist/read/index.d.ts +1 -1
- package/dist/read/index.d.ts.map +1 -1
- package/dist/read/index.js +1 -1
- package/dist/read/index.js.map +1 -1
- package/dist/read/types.d.ts +10 -1
- package/dist/read/types.d.ts.map +1 -1
- package/dist/read/wallet.d.ts +13 -8
- package/dist/read/wallet.d.ts.map +1 -1
- package/dist/read/wallet.js +39 -38
- package/dist/read/wallet.js.map +1 -1
- package/dist/read/works.d.ts +2 -5
- package/dist/read/works.d.ts.map +1 -1
- package/dist/read/works.js +7 -37
- package/dist/read/works.js.map +1 -1
- package/dist/release-graph.d.ts +6 -19
- package/dist/release-graph.d.ts.map +1 -1
- package/dist/release-graph.js +29 -43
- package/dist/release-graph.js.map +1 -1
- package/dist/transactions.d.ts +12 -28
- package/dist/transactions.d.ts.map +1 -1
- package/dist/transactions.js +23 -28
- package/dist/transactions.js.map +1 -1
- package/package.json +4 -4
- package/src/client.ts +33 -2
- package/src/contracts/release_registry/release_registry.ts +160 -0
- package/src/contracts/royalty_pool/pool.ts +28 -2
- package/src/contracts/utils/index.ts +156 -4
- package/src/contracts.ts +5 -1
- package/src/cover.ts +8 -19
- package/src/credits.ts +23 -63
- package/src/index.ts +3 -1
- package/src/read/catalog.ts +9 -29
- package/src/read/config.ts +18 -58
- package/src/read/index.ts +1 -1
- package/src/read/types.ts +11 -1
- package/src/read/wallet.ts +56 -38
- package/src/read/works.ts +5 -42
- package/src/release-graph.ts +40 -75
- package/src/transactions.ts +33 -62
package/src/read/wallet.ts
CHANGED
|
@@ -21,12 +21,17 @@ import {
|
|
|
21
21
|
} from "@misonetwork/sdk";
|
|
22
22
|
import type { MisoClient } from "./client.ts";
|
|
23
23
|
import { int, u64 } from "./internal/scalars.ts";
|
|
24
|
-
import type {
|
|
24
|
+
import type {
|
|
25
|
+
Balance,
|
|
26
|
+
OwnedParty,
|
|
27
|
+
OwnedRecord,
|
|
28
|
+
OwnedWork,
|
|
29
|
+
Ownership,
|
|
30
|
+
PendingMembership,
|
|
31
|
+
WorkDetail,
|
|
32
|
+
} from "./types.ts";
|
|
25
33
|
import { getRecordingTitles, getWorkAddressesByShareTypes, getWorksByIds } from "./works.ts";
|
|
26
34
|
|
|
27
|
-
/** The on-chain type suffix every record object shares, across both live packages. */
|
|
28
|
-
export const RECORD_TYPE_SUFFIX = "::record::Record";
|
|
29
|
-
|
|
30
35
|
/**
|
|
31
36
|
* Page cap on the owned-objects scan, so a wallet holding a huge unrelated object
|
|
32
37
|
* set can't spin forever. 50/page × 20 = 1000 objects — far beyond any realistic
|
|
@@ -44,53 +49,29 @@ function coerceNumber(v: unknown): number | null {
|
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
/**
|
|
47
|
-
* This copy's number in its run
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* The gRPC JSON enum shape varies by transport, so this probes defensively.
|
|
51
|
-
*
|
|
52
|
-
* Precedence is MOST SPECIFIC FIRST — a top-level `number`, then the variant's
|
|
53
|
-
* `number`, and only then `edition`. miso-app's original
|
|
54
|
-
* (`lib/records.ts:readRecordNumber`) checked `edition` before descending into
|
|
55
|
-
* the variant, so an SDK-view record reported which EDITION it came from (2)
|
|
56
|
-
* where the field means which COPY it is (12). `edition` survives as the last
|
|
57
|
-
* resort for a layout that carries nothing better.
|
|
52
|
+
* This copy's number in its run. Fresh records expose the canonical
|
|
53
|
+
* `{ release_id, number }` shape; pre-publish layouts are intentionally not
|
|
54
|
+
* accepted by the pre-launch SDK.
|
|
58
55
|
*/
|
|
59
56
|
function readRecordNumber(json: Record<string, unknown>): number | null {
|
|
60
|
-
|
|
61
|
-
if (direct != null) return direct;
|
|
62
|
-
|
|
63
|
-
const variant = json.variant;
|
|
64
|
-
if (variant && typeof variant === "object") {
|
|
65
|
-
const v = variant as Record<string, unknown>;
|
|
66
|
-
const top = coerceNumber(v.number);
|
|
67
|
-
if (top != null) return top;
|
|
68
|
-
for (const inner of Object.values(v)) {
|
|
69
|
-
if (inner && typeof inner === "object") {
|
|
70
|
-
const n = coerceNumber((inner as Record<string, unknown>).number);
|
|
71
|
-
if (n != null) return n;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
return coerceNumber(json.edition);
|
|
57
|
+
return coerceNumber(json.number);
|
|
76
58
|
}
|
|
77
59
|
|
|
78
60
|
function readReleaseId(json: Record<string, unknown>): string | null {
|
|
79
|
-
const r = json.release_id
|
|
61
|
+
const r = json.release_id;
|
|
80
62
|
return typeof r === "string" && r ? r : null;
|
|
81
63
|
}
|
|
82
64
|
|
|
83
65
|
/**
|
|
84
66
|
* The records `owner` holds. Ownership is DIRECT — a record is an address-owned
|
|
85
|
-
* `<
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* live, so we match the `::record::Record` SUFFIX client-side and catch both with
|
|
89
|
-
* no package-id config. Objects that fail to parse are skipped, never thrown.
|
|
67
|
+
* `<miso_record>::record::Record` with no pressing/license/receipt intermediary.
|
|
68
|
+
* The server-side type filter and exact local check deliberately exclude records
|
|
69
|
+
* from any retired package namespace.
|
|
90
70
|
*/
|
|
91
71
|
export async function getOwnedRecords(client: MisoClient, owner: string): Promise<OwnedRecord[]> {
|
|
92
72
|
const out: OwnedRecord[] = [];
|
|
93
73
|
let cursor: string | null = null;
|
|
74
|
+
const recordType = `${client.config.protocol.record}::record::Record`;
|
|
94
75
|
|
|
95
76
|
for (let page = 0; page < MAX_PAGES; page++) {
|
|
96
77
|
// Annotated: `cursor` is both an input and assigned from the result, which
|
|
@@ -98,12 +79,13 @@ export async function getOwnedRecords(client: MisoClient, owner: string): Promis
|
|
|
98
79
|
const res: Awaited<ReturnType<typeof client.protocol.core.listOwnedObjects>> =
|
|
99
80
|
await client.protocol.core.listOwnedObjects({
|
|
100
81
|
owner,
|
|
82
|
+
type: recordType,
|
|
101
83
|
cursor,
|
|
102
84
|
limit: 50,
|
|
103
85
|
include: { json: true },
|
|
104
86
|
});
|
|
105
87
|
for (const obj of res.objects) {
|
|
106
|
-
if (
|
|
88
|
+
if (obj.type !== recordType) continue;
|
|
107
89
|
const json = (obj.json ?? {}) as Record<string, unknown>;
|
|
108
90
|
out.push({
|
|
109
91
|
id: obj.objectId,
|
|
@@ -156,6 +138,42 @@ export async function getOwnedParties(client: MisoClient, owner: string): Promis
|
|
|
156
138
|
});
|
|
157
139
|
}
|
|
158
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Pending group invitations for every individual party the wallet administers.
|
|
143
|
+
*
|
|
144
|
+
* The Party module maintains a member-side pending-membership index, so this
|
|
145
|
+
* reads only the wallet's controlled parties — never a global event scan. Group
|
|
146
|
+
* names are resolved in one batch so the API can render an inbox without extra
|
|
147
|
+
* browser reads.
|
|
148
|
+
*/
|
|
149
|
+
export async function getPendingMemberships(
|
|
150
|
+
client: MisoClient,
|
|
151
|
+
owner: string,
|
|
152
|
+
): Promise<PendingMembership[]> {
|
|
153
|
+
const controlled = await getOwnedParties(client, owner);
|
|
154
|
+
const individuals = controlled.filter((party) => party.kind === "individual");
|
|
155
|
+
if (individuals.length === 0) return [];
|
|
156
|
+
|
|
157
|
+
const invitations = await Promise.all(
|
|
158
|
+
individuals.map(async (member) => ({
|
|
159
|
+
member,
|
|
160
|
+
groupIds: await client.sui.party.getPendingMemberships(member.partyId),
|
|
161
|
+
})),
|
|
162
|
+
);
|
|
163
|
+
const groupIds = [...new Set(invitations.flatMap(({ groupIds }) => groupIds))];
|
|
164
|
+
if (groupIds.length === 0) return [];
|
|
165
|
+
|
|
166
|
+
const groups = await client.sui.party.getPartiesByIds(groupIds);
|
|
167
|
+
return invitations.flatMap(({ member, groupIds }) =>
|
|
168
|
+
groupIds.flatMap((groupId): PendingMembership[] => {
|
|
169
|
+
const group = groups[groupId];
|
|
170
|
+
return group?.kind === "group"
|
|
171
|
+
? [{ memberPartyId: member.partyId, memberCapId: member.capId, groupId, groupName: group.name }]
|
|
172
|
+
: [];
|
|
173
|
+
}),
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
159
177
|
// ── Works (studio catalog) ───────────────────────────────────────────────────
|
|
160
178
|
|
|
161
179
|
/**
|
package/src/read/works.ts
CHANGED
|
@@ -179,31 +179,7 @@ function parseRecording(id: string, content: Uint8Array): Recording {
|
|
|
179
179
|
export function parseReleaseObject(
|
|
180
180
|
id: string,
|
|
181
181
|
content: Uint8Array,
|
|
182
|
-
json: unknown,
|
|
183
182
|
): Release {
|
|
184
|
-
const deployed = json as {
|
|
185
|
-
discs?: Parsed[];
|
|
186
|
-
state?: Parsed;
|
|
187
|
-
title?: unknown;
|
|
188
|
-
} | null;
|
|
189
|
-
if (deployed?.discs) {
|
|
190
|
-
const tracks = deployed.discs.flatMap((disc) => disc.tracks ?? []);
|
|
191
|
-
const publishedAt =
|
|
192
|
-
deployed.state?.["@variant"] === "Published" ? deployed.state.pos0 : null;
|
|
193
|
-
return {
|
|
194
|
-
id,
|
|
195
|
-
state:
|
|
196
|
-
publishedAt == null
|
|
197
|
-
? { type: "Initialized" }
|
|
198
|
-
: { type: "Published", timestampMs: Number(publishedAt) },
|
|
199
|
-
title: String(deployed.title ?? ""),
|
|
200
|
-
tracks: tracks.map((track) => ({
|
|
201
|
-
state: (track.state?.["@variant"] ?? "Unassigned") as TrackState,
|
|
202
|
-
recordingId: String(track.recording_id),
|
|
203
|
-
splitBps: { value: Number(track.split_bps?.pos0 ?? track.split_bps) },
|
|
204
|
-
})),
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
183
|
const value = contracts.release.Release.parse(content) as Parsed;
|
|
208
184
|
return {
|
|
209
185
|
id,
|
|
@@ -245,7 +221,7 @@ export async function getWorksByIds(
|
|
|
245
221
|
if (kinds.size === 0) return out;
|
|
246
222
|
const { objects } = await client.core.getObjects({
|
|
247
223
|
objectIds: [...kinds.keys()],
|
|
248
|
-
include: { content: true
|
|
224
|
+
include: { content: true },
|
|
249
225
|
});
|
|
250
226
|
for (const object of objects) {
|
|
251
227
|
if (object instanceof Error || !object.content) continue;
|
|
@@ -264,16 +240,12 @@ export async function getWorksByIds(
|
|
|
264
240
|
out.releases[object.objectId] = parseReleaseObject(
|
|
265
241
|
object.objectId,
|
|
266
242
|
object.content,
|
|
267
|
-
object.json,
|
|
268
243
|
);
|
|
269
244
|
}
|
|
270
245
|
return out;
|
|
271
246
|
}
|
|
272
247
|
|
|
273
|
-
/**
|
|
274
|
-
* Resolve display titles for both deployed recording layouts. Older recordings
|
|
275
|
-
* carry `title` in JSON; current recordings inherit it from their composition.
|
|
276
|
-
*/
|
|
248
|
+
/** Resolve recording titles through each recording's canonical composition type. */
|
|
277
249
|
export async function getRecordingTitles(
|
|
278
250
|
client: ClientWithCoreApi,
|
|
279
251
|
graphql: SuiGraphQLClient,
|
|
@@ -285,24 +257,15 @@ export async function getRecordingTitles(
|
|
|
285
257
|
|
|
286
258
|
const { objects } = await client.core.getObjects({
|
|
287
259
|
objectIds: ids,
|
|
288
|
-
include: {
|
|
260
|
+
include: {},
|
|
289
261
|
});
|
|
290
262
|
const titles: Record<string, string> = {};
|
|
291
263
|
const compositionShareByRecording: Record<string, string> = {};
|
|
292
264
|
|
|
293
265
|
for (const object of objects) {
|
|
294
266
|
if (object instanceof Error) continue;
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
titles[object.objectId] = json.title;
|
|
298
|
-
continue;
|
|
299
|
-
}
|
|
300
|
-
try {
|
|
301
|
-
const [, compositionShareType] = extractTypeParams2(object.type);
|
|
302
|
-
compositionShareByRecording[object.objectId] = compositionShareType;
|
|
303
|
-
} catch {
|
|
304
|
-
// A deployed layout with no composition type and no JSON title remains Untitled.
|
|
305
|
-
}
|
|
267
|
+
const [, compositionShareType] = extractTypeParams2(object.type);
|
|
268
|
+
compositionShareByRecording[object.objectId] = compositionShareType;
|
|
306
269
|
}
|
|
307
270
|
|
|
308
271
|
const compositionShareTypes = Object.values(compositionShareByRecording);
|
package/src/release-graph.ts
CHANGED
|
@@ -3,35 +3,33 @@
|
|
|
3
3
|
|
|
4
4
|
// Single-PTB publisher for a whole release graph: creates every composition and
|
|
5
5
|
// recording, optionally attaches royalty pools, derives the release id ON-CHAIN
|
|
6
|
-
// (so recordings need not be shared first), builds the
|
|
6
|
+
// (so recordings need not be shared first), builds the tracks/release,
|
|
7
7
|
// then shares everything and transfers the admin caps — all in ONE transaction.
|
|
8
8
|
//
|
|
9
9
|
// Share currencies must already be published + initialized (their Currency objects
|
|
10
10
|
// shared on-chain); their ids/types are passed in. Ordering is load-bearing:
|
|
11
11
|
// 1. composition::new → recording::new(&comp) (borrow-before-share) + pool attach
|
|
12
|
-
// 2. release::
|
|
13
|
-
// derivedId, split) →
|
|
12
|
+
// 2. release::derive_target_release_id(<recording::id results>) →
|
|
13
|
+
// track::new(cap, &rec, derivedId, split) → release_registry::new_release
|
|
14
14
|
// 3. publish (share) comps + recs + release, disperse supply, transfer caps
|
|
15
15
|
// Steps 1–2 borrow the works; step 3 consumes them into share_object.
|
|
16
16
|
//
|
|
17
|
-
// Release tracks come in
|
|
18
|
-
//
|
|
19
|
-
// inline), and existing recordings authorized by a pre-made Deal in hand.
|
|
20
|
-
// Pre-made deals pin the release id, so they cannot coexist with fresh
|
|
21
|
-
// recordings — the builder rejects that mix up front.
|
|
17
|
+
// Release tracks come in two shapes (see TrackNode): recordings created in THIS
|
|
18
|
+
// PTB, and existing recordings authorized by their admin cap.
|
|
22
19
|
//
|
|
23
20
|
// This is the whole-graph orchestration half of the opinionated publish flow —
|
|
24
|
-
// it composes `@misonetwork/sdk`'s bare `composition`/`recording`/`
|
|
25
|
-
// `release` call bindings and
|
|
21
|
+
// it composes `@misonetwork/sdk`'s bare `composition`/`recording`/`track`/
|
|
22
|
+
// `release` call bindings and this package's `release_registry` binding with its
|
|
26
23
|
// own `disperseShares`/`finalizeRelease` and royalty-pool extension helpers, into
|
|
27
24
|
// one PTB.
|
|
28
25
|
|
|
29
26
|
import { Transaction, type TransactionObjectArgument } from "@mysten/sui/transactions";
|
|
30
|
-
import { contracts,
|
|
27
|
+
import { contracts, PROTOCOL_MAX_ROYALTY_RATE_BPS } from "@misonetwork/sdk";
|
|
31
28
|
import { attachCompositionRoyaltyPool, attachRecordingRoyaltyPool } from "./extensions/royalty-pool.ts";
|
|
32
29
|
import { disperseShares, finalizeRelease, type ShareRecipient } from "./transactions.ts";
|
|
30
|
+
import * as releaseRegistry from "./contracts/release_registry/release_registry.ts";
|
|
33
31
|
|
|
34
|
-
const { composition, recording,
|
|
32
|
+
const { composition, recording, track, release } = contracts;
|
|
35
33
|
|
|
36
34
|
/** A royalty pool to attach to a work, in the same PTB. */
|
|
37
35
|
export interface RoyaltyPoolNode {
|
|
@@ -75,7 +73,7 @@ export interface RecordingNode {
|
|
|
75
73
|
royaltyPool?: RoyaltyPoolNode;
|
|
76
74
|
}
|
|
77
75
|
|
|
78
|
-
/** A track whose recording is created in THIS PTB
|
|
76
|
+
/** A track whose recording is created in THIS PTB. */
|
|
79
77
|
export interface FreshTrackNode {
|
|
80
78
|
/** Index into `recordings[]`. */
|
|
81
79
|
recordingIndex: number;
|
|
@@ -83,9 +81,8 @@ export interface FreshTrackNode {
|
|
|
83
81
|
}
|
|
84
82
|
|
|
85
83
|
/**
|
|
86
|
-
* A track over an EXISTING recording, authorized by its admin cap
|
|
87
|
-
* sender
|
|
88
|
-
* This is the only way to mix existing recordings with ones created in this PTB.
|
|
84
|
+
* A track over an EXISTING recording, authorized by its admin cap held by the
|
|
85
|
+
* sender. This can coexist with recordings created in this PTB.
|
|
89
86
|
*/
|
|
90
87
|
export interface CapTrackNode {
|
|
91
88
|
recordingId: string;
|
|
@@ -95,25 +92,9 @@ export interface CapTrackNode {
|
|
|
95
92
|
splitBps: number;
|
|
96
93
|
}
|
|
97
94
|
|
|
98
|
-
|
|
99
|
-
* A track over an EXISTING recording, authorized by a pre-made `Deal` held by
|
|
100
|
-
* the sender. A deal pins the exact release id — a digest over ALL track
|
|
101
|
-
* recording ids — so deal-backed tracks cannot coexist with `FreshTrackNode`s
|
|
102
|
-
* (a fresh recording's id is unknowable when the deal was created).
|
|
103
|
-
* `splitBps` must be the value stored in the deal (read it from the object).
|
|
104
|
-
*/
|
|
105
|
-
export interface DealTrackNode {
|
|
106
|
-
dealId: string;
|
|
107
|
-
recordingId: string;
|
|
108
|
-
recordingShareType: string;
|
|
109
|
-
compositionShareType: string;
|
|
110
|
-
splitBps: number;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export type TrackNode = FreshTrackNode | CapTrackNode | DealTrackNode;
|
|
95
|
+
export type TrackNode = FreshTrackNode | CapTrackNode;
|
|
114
96
|
|
|
115
97
|
const isFresh = (t: TrackNode): t is FreshTrackNode => "recordingIndex" in t;
|
|
116
|
-
const isDealBacked = (t: TrackNode): t is DealTrackNode => "dealId" in t;
|
|
117
98
|
|
|
118
99
|
export interface ReleaseNode {
|
|
119
100
|
title: string;
|
|
@@ -131,6 +112,8 @@ export interface PublishReleaseGraphParams {
|
|
|
131
112
|
release?: ReleaseNode;
|
|
132
113
|
misoPackageId: string;
|
|
133
114
|
minatoPackageId: string;
|
|
115
|
+
/** `release_registry` extension package, distinct from its shared object id. */
|
|
116
|
+
releaseRegistryPackageId: string;
|
|
134
117
|
}
|
|
135
118
|
|
|
136
119
|
interface Parts {
|
|
@@ -141,7 +124,7 @@ interface Parts {
|
|
|
141
124
|
|
|
142
125
|
/** Builds the entire graph in a single transaction (see file header for ordering). */
|
|
143
126
|
export function publishReleaseGraph(params: PublishReleaseGraphParams): (tx: Transaction) => void {
|
|
144
|
-
const { misoPackageId: pkg, minatoPackageId } = params;
|
|
127
|
+
const { misoPackageId: pkg, minatoPackageId, releaseRegistryPackageId } = params;
|
|
145
128
|
return (tx) => {
|
|
146
129
|
// ── 1) Create every composition, then every recording (borrow-before-share) ──
|
|
147
130
|
const comps: Parts[] = params.compositions.map((c) => {
|
|
@@ -199,61 +182,43 @@ export function publishReleaseGraph(params: PublishReleaseGraphParams): (tx: Tra
|
|
|
199
182
|
return parts;
|
|
200
183
|
});
|
|
201
184
|
|
|
202
|
-
// ── 2) Release: derive the id on-chain, then
|
|
185
|
+
// ── 2) Release: derive the id on-chain, then tracks → release ──────────
|
|
203
186
|
if (params.release) {
|
|
204
187
|
const rel = params.release;
|
|
205
188
|
const ordered = rel.tracks;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
)
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
//
|
|
216
|
-
//
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
type: "0x2::object::ID",
|
|
221
|
-
elements: ordered.map((t) =>
|
|
222
|
-
isFresh(t)
|
|
223
|
-
? tx.add(recording.id({ package: pkg, typeArguments: recTypeArgs(t.recordingIndex), arguments: [recs[t.recordingIndex]!.work] }))
|
|
224
|
-
: tx.pure.id(t.recordingId),
|
|
225
|
-
),
|
|
226
|
-
});
|
|
227
|
-
const splitVec = tx.makeMoveVec({ type: "u64", elements: ordered.map((t) => tx.pure.u64(t.splitBps)) });
|
|
228
|
-
releaseId = tx.add(
|
|
229
|
-
release.deriveReleaseId({ package: pkg, arguments: [idVec, splitVec, tx.pure.u256(BigInt(rel.nonce)), tx.object(rel.releaseRegistryId)] }),
|
|
230
|
-
);
|
|
231
|
-
}
|
|
189
|
+
const idVec = tx.makeMoveVec({
|
|
190
|
+
type: "0x2::object::ID",
|
|
191
|
+
elements: ordered.map((t) =>
|
|
192
|
+
isFresh(t)
|
|
193
|
+
? tx.add(recording.id({ package: pkg, typeArguments: recTypeArgs(t.recordingIndex), arguments: [recs[t.recordingIndex]!.work] }))
|
|
194
|
+
: tx.pure.id(t.recordingId),
|
|
195
|
+
),
|
|
196
|
+
});
|
|
197
|
+
const splitVec = tx.makeMoveVec({ type: "u64", elements: ordered.map((t) => tx.pure.u64(t.splitBps)) });
|
|
198
|
+
// This returns an ID value (not an object); pass the result straight to
|
|
199
|
+
// `track::new` so every track consents to this exact release target.
|
|
200
|
+
const releaseId = tx.add(
|
|
201
|
+
release.deriveTargetReleaseId({ package: pkg, arguments: [idVec, splitVec, tx.pure.u256(BigInt(rel.nonce)), tx.pure.id(rel.releaseRegistryId)] }),
|
|
202
|
+
);
|
|
232
203
|
|
|
233
204
|
const trackArgs = ordered.map((t) => {
|
|
234
|
-
if (isDealBacked(t)) {
|
|
235
|
-
const typeArgs: [string, string] = [t.recordingShareType, t.compositionShareType];
|
|
236
|
-
return tx.add(track._new({ package: pkg, typeArguments: typeArgs, arguments: [tx.object(t.dealId), tx.object(t.recordingId)] }));
|
|
237
|
-
}
|
|
238
205
|
if (isFresh(t)) {
|
|
239
206
|
const typeArgs = recTypeArgs(t.recordingIndex);
|
|
240
207
|
const parts = recs[t.recordingIndex]!;
|
|
241
|
-
|
|
242
|
-
return tx.add(track._new({ package: pkg, typeArguments: typeArgs, arguments: [dealArg, parts.work] }));
|
|
208
|
+
return tx.add(track._new({ package: pkg, typeArguments: typeArgs, arguments: [parts.adminCap, parts.work, releaseId, tx.pure.u16(t.splitBps)] }));
|
|
243
209
|
}
|
|
244
210
|
const typeArgs: [string, string] = [t.recordingShareType, t.compositionShareType];
|
|
245
211
|
const rec = tx.object(t.recordingId);
|
|
246
|
-
|
|
247
|
-
deal._new({ package: pkg, typeArguments: typeArgs, arguments: [tx.object(t.recordingAdminCapId), rec, releaseId!, tx.pure.u16(t.splitBps)] }),
|
|
248
|
-
);
|
|
249
|
-
return tx.add(track._new({ package: pkg, typeArguments: typeArgs, arguments: [dealArg, rec] }));
|
|
212
|
+
return tx.add(track._new({ package: pkg, typeArguments: typeArgs, arguments: [tx.object(t.recordingAdminCapId), rec, releaseId, tx.pure.u16(t.splitBps)] }));
|
|
250
213
|
});
|
|
251
214
|
const trackVec = tx.makeMoveVec({ type: `${pkg}::track::Track`, elements: trackArgs });
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
215
|
+
const created = tx.add(
|
|
216
|
+
releaseRegistry.newRelease({
|
|
217
|
+
package: releaseRegistryPackageId,
|
|
218
|
+
arguments: [tx.object(rel.releaseRegistryId), tx.pure.string(rel.title), trackVec, tx.pure.u256(BigInt(rel.nonce))],
|
|
219
|
+
}),
|
|
256
220
|
);
|
|
221
|
+
const releaseParts = { release: created[0]!, adminCap: created[1]! };
|
|
257
222
|
finalizeRelease(tx, { ...releaseParts, adminAddress: rel.adminAddress, misoPackageId: pkg });
|
|
258
223
|
}
|
|
259
224
|
|
package/src/transactions.ts
CHANGED
|
@@ -15,10 +15,11 @@
|
|
|
15
15
|
// (`createComposition`/`createRecording`) — dispersing it to recipients via
|
|
16
16
|
// minato, publishing (sharing) the work, and transferring its admin cap is
|
|
17
17
|
// an opinion about economics;
|
|
18
|
-
// - minting a release is
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
18
|
+
// - minting a release is a platform primitive: core's `release::new` takes an
|
|
19
|
+
// unconstructible PTB `&mut UID`, so `release_registry::new_release` is the
|
|
20
|
+
// canonical client path. Choosing a track-assembly strategy, publishing
|
|
21
|
+
// (sharing) it, and picking a recipient for its `ReleaseAdminCap` is the
|
|
22
|
+
// opinion, so `finalizeRelease` / `publishRelease` live here.
|
|
22
23
|
//
|
|
23
24
|
// Every `finalize*` here MUST run in the same PTB as the `create*` that produced
|
|
24
25
|
// its parts — `Composition`, `Recording`, and `Release` are all `key`-only with
|
|
@@ -34,17 +35,21 @@ import {
|
|
|
34
35
|
contracts,
|
|
35
36
|
createComposition,
|
|
36
37
|
createRecording,
|
|
37
|
-
createRelease,
|
|
38
38
|
type CompositionParts,
|
|
39
39
|
type CreateCompositionParams,
|
|
40
40
|
type CreateRecordingParams,
|
|
41
41
|
type RecordingParts,
|
|
42
|
-
type ReleaseParts,
|
|
43
42
|
type ShareCurrencyBinding,
|
|
44
43
|
type TxThunk,
|
|
45
44
|
} from "@misonetwork/sdk";
|
|
45
|
+
import * as releaseRegistry from "./contracts/release_registry/release_registry.ts";
|
|
46
46
|
|
|
47
|
-
const {
|
|
47
|
+
const { track, release } = contracts;
|
|
48
|
+
|
|
49
|
+
interface ReleaseParts {
|
|
50
|
+
release: TransactionObjectArgument;
|
|
51
|
+
adminCap: TransactionObjectArgument;
|
|
52
|
+
}
|
|
48
53
|
|
|
49
54
|
// `TxThunk` is the protocol SDK's type, re-exported so platform consumers get
|
|
50
55
|
// the same nominal shape rather than a structurally-identical twin.
|
|
@@ -329,7 +334,8 @@ export interface FinalizeReleaseParams extends ReleaseParts {
|
|
|
329
334
|
* The opinionated finish for a release: publish (share) it and transfer its
|
|
330
335
|
* admin cap to `adminAddress`.
|
|
331
336
|
*
|
|
332
|
-
* MUST run in the same PTB as the `
|
|
337
|
+
* MUST run in the same PTB as the `release_registry::new_release` that produced
|
|
338
|
+
* these parts —
|
|
333
339
|
* `Release` is `key`-only with no `drop`, so an unpublished release cannot
|
|
334
340
|
* outlive its transaction. Splitting create from finalize is what lets a caller
|
|
335
341
|
* do something else with the cap (route it to a vault, hand it to another
|
|
@@ -343,9 +349,9 @@ export function finalizeRelease(tx: Transaction, params: FinalizeReleaseParams):
|
|
|
343
349
|
export interface TrackInput {
|
|
344
350
|
recordingId: string;
|
|
345
351
|
recordingAdminCapId: string;
|
|
346
|
-
/** Share type of the recording (the
|
|
352
|
+
/** Share type of the recording (the track `RecordingShare` phantom). */
|
|
347
353
|
recordingShareType: string;
|
|
348
|
-
/** Share type of the parent composition (the
|
|
354
|
+
/** Share type of the parent composition (the track `CompositionShare` phantom). */
|
|
349
355
|
compositionShareType: string;
|
|
350
356
|
splitBps: number;
|
|
351
357
|
}
|
|
@@ -354,6 +360,8 @@ export interface PublishReleaseParams {
|
|
|
354
360
|
title: string;
|
|
355
361
|
/** The ordered tracklist. Display grouping (discs/sides) is extension data. */
|
|
356
362
|
tracks: TrackInput[];
|
|
363
|
+
/** `release_registry` extension package, distinct from its shared object id. */
|
|
364
|
+
releaseRegistryPackageId: string;
|
|
357
365
|
releaseRegistryId: string;
|
|
358
366
|
releaseId: string;
|
|
359
367
|
releaseNonce: string;
|
|
@@ -371,70 +379,33 @@ function buildTrackVec(
|
|
|
371
379
|
|
|
372
380
|
/**
|
|
373
381
|
* Convenience: publish a release end-to-end, assembling its tracklist from
|
|
374
|
-
* recording admin caps held by the sender
|
|
382
|
+
* recording admin caps held by the sender.
|
|
375
383
|
*/
|
|
376
384
|
export function publishRelease(params: PublishReleaseParams): TxThunk {
|
|
377
385
|
return (tx) => {
|
|
378
|
-
const { misoPackageId } = params;
|
|
386
|
+
const { misoPackageId, releaseRegistryPackageId } = params;
|
|
379
387
|
const trackArgs = params.tracks.map((t) => {
|
|
380
388
|
const typeArguments: [string, string] = [t.recordingShareType, t.compositionShareType];
|
|
381
|
-
|
|
382
|
-
|
|
389
|
+
return tx.add(
|
|
390
|
+
track._new({
|
|
383
391
|
package: misoPackageId,
|
|
384
392
|
typeArguments,
|
|
385
393
|
arguments: [tx.object(t.recordingAdminCapId), tx.object(t.recordingId), tx.pure.id(params.releaseId), tx.pure.u16(t.splitBps)],
|
|
386
394
|
}),
|
|
387
395
|
);
|
|
388
|
-
return tx.add(track._new({ package: misoPackageId, typeArguments, arguments: [dealArg, tx.object(t.recordingId)] }));
|
|
389
396
|
});
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
dealId: string;
|
|
401
|
-
/** The recording the deal is for — `track::new` takes `&Recording` alongside the deal. */
|
|
402
|
-
recordingId: string;
|
|
403
|
-
/** Share type of the recording (the deal/track `RecordingShare` phantom). */
|
|
404
|
-
recordingShareType: string;
|
|
405
|
-
/** Share type of the parent composition (the deal/track `CompositionShare` phantom). */
|
|
406
|
-
compositionShareType: string;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
export interface PublishReleaseFromDealsParams {
|
|
410
|
-
title: string;
|
|
411
|
-
/** The ordered tracklist, one pre-made deal per track. */
|
|
412
|
-
deals: DealInput[];
|
|
413
|
-
releaseRegistryId: string;
|
|
414
|
-
releaseNonce: string;
|
|
415
|
-
misoPackageId: string;
|
|
416
|
-
adminAddress: string;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
/**
|
|
420
|
-
* Convenience: publish a release end-to-end from pre-made `Deal`s held by the
|
|
421
|
-
* sender (each deal already pins this release's id).
|
|
422
|
-
*/
|
|
423
|
-
export function publishReleaseFromDeals(params: PublishReleaseFromDealsParams): TxThunk {
|
|
424
|
-
return (tx) => {
|
|
425
|
-
const { misoPackageId } = params;
|
|
426
|
-
const trackArgs = params.deals.map((dl) =>
|
|
427
|
-
tx.add(track._new({
|
|
428
|
-
package: misoPackageId,
|
|
429
|
-
typeArguments: [dl.recordingShareType, dl.compositionShareType],
|
|
430
|
-
arguments: [tx.object(dl.dealId), tx.object(dl.recordingId)],
|
|
431
|
-
})),
|
|
432
|
-
);
|
|
433
|
-
const parts = createRelease(
|
|
434
|
-
tx,
|
|
435
|
-
{ title: params.title, nonce: params.releaseNonce, releaseRegistryId: params.releaseRegistryId, misoPackageId },
|
|
436
|
-
buildTrackVec(tx, misoPackageId, trackArgs),
|
|
397
|
+
const created = tx.add(
|
|
398
|
+
releaseRegistry.newRelease({
|
|
399
|
+
package: releaseRegistryPackageId,
|
|
400
|
+
arguments: [
|
|
401
|
+
tx.object(params.releaseRegistryId),
|
|
402
|
+
tx.pure.string(params.title),
|
|
403
|
+
buildTrackVec(tx, misoPackageId, trackArgs),
|
|
404
|
+
tx.pure.u256(BigInt(params.releaseNonce)),
|
|
405
|
+
],
|
|
406
|
+
}),
|
|
437
407
|
);
|
|
408
|
+
const parts: ReleaseParts = { release: created[0]!, adminCap: created[1]! };
|
|
438
409
|
finalizeRelease(tx, { ...parts, adminAddress: params.adminAddress, misoPackageId });
|
|
439
410
|
};
|
|
440
411
|
}
|