@misonetwork/sdk 0.2.1 → 0.3.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 (165) hide show
  1. package/README.md +44 -98
  2. package/dist/contracts.d.ts +0 -12
  3. package/dist/contracts.d.ts.map +1 -1
  4. package/dist/contracts.js +3 -16
  5. package/dist/contracts.js.map +1 -1
  6. package/dist/index.d.ts +0 -3
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +3 -3
  9. package/dist/index.js.map +1 -1
  10. package/dist/internal.d.ts +0 -3
  11. package/dist/internal.d.ts.map +1 -1
  12. package/dist/internal.js +0 -4
  13. package/dist/internal.js.map +1 -1
  14. package/dist/queries.d.ts.map +1 -1
  15. package/dist/queries.js.map +1 -1
  16. package/package.json +1 -13
  17. package/src/contracts.ts +3 -19
  18. package/src/index.ts +3 -3
  19. package/src/internal.ts +10 -15
  20. package/src/queries.ts +5 -4
  21. package/dist/contracts/composition_credits/composition_credits.d.ts +0 -181
  22. package/dist/contracts/composition_credits/composition_credits.d.ts.map +0 -1
  23. package/dist/contracts/composition_credits/composition_credits.js +0 -109
  24. package/dist/contracts/composition_credits/composition_credits.js.map +0 -1
  25. package/dist/contracts/composition_credits/composition_party_role.d.ts +0 -119
  26. package/dist/contracts/composition_credits/composition_party_role.d.ts.map +0 -1
  27. package/dist/contracts/composition_credits/composition_party_role.js +0 -139
  28. package/dist/contracts/composition_credits/composition_party_role.js.map +0 -1
  29. package/dist/contracts/composition_credits/deps/miso_credit/credit.d.ts +0 -24
  30. package/dist/contracts/composition_credits/deps/miso_credit/credit.d.ts.map +0 -1
  31. package/dist/contracts/composition_credits/deps/miso_credit/credit.js +0 -23
  32. package/dist/contracts/composition_credits/deps/miso_credit/credit.js.map +0 -1
  33. package/dist/contracts/composition_credits/deps/sui/vec_map.d.ts +0 -37
  34. package/dist/contracts/composition_credits/deps/sui/vec_map.d.ts.map +0 -1
  35. package/dist/contracts/composition_credits/deps/sui/vec_map.js +0 -28
  36. package/dist/contracts/composition_credits/deps/sui/vec_map.js.map +0 -1
  37. package/dist/contracts/composition_royalty_pool/composition_royalty_pool.d.ts +0 -87
  38. package/dist/contracts/composition_royalty_pool/composition_royalty_pool.d.ts.map +0 -1
  39. package/dist/contracts/composition_royalty_pool/composition_royalty_pool.js +0 -80
  40. package/dist/contracts/composition_royalty_pool/composition_royalty_pool.js.map +0 -1
  41. package/dist/contracts/cover_art/cover_art.d.ts +0 -85
  42. package/dist/contracts/cover_art/cover_art.d.ts.map +0 -1
  43. package/dist/contracts/cover_art/cover_art.js +0 -68
  44. package/dist/contracts/cover_art/cover_art.js.map +0 -1
  45. package/dist/contracts/cover_art/deps/ori/walrus_data.d.ts +0 -52
  46. package/dist/contracts/cover_art/deps/ori/walrus_data.d.ts.map +0 -1
  47. package/dist/contracts/cover_art/deps/ori/walrus_data.js +0 -37
  48. package/dist/contracts/cover_art/deps/ori/walrus_data.js.map +0 -1
  49. package/dist/contracts/recording_credits/deps/miso_credit/credit.d.ts +0 -24
  50. package/dist/contracts/recording_credits/deps/miso_credit/credit.d.ts.map +0 -1
  51. package/dist/contracts/recording_credits/deps/miso_credit/credit.js +0 -23
  52. package/dist/contracts/recording_credits/deps/miso_credit/credit.js.map +0 -1
  53. package/dist/contracts/recording_credits/deps/sui/vec_map.d.ts +0 -37
  54. package/dist/contracts/recording_credits/deps/sui/vec_map.d.ts.map +0 -1
  55. package/dist/contracts/recording_credits/deps/sui/vec_map.js +0 -28
  56. package/dist/contracts/recording_credits/deps/sui/vec_map.js.map +0 -1
  57. package/dist/contracts/recording_credits/deps/sui/vec_set.d.ts +0 -21
  58. package/dist/contracts/recording_credits/deps/sui/vec_set.d.ts.map +0 -1
  59. package/dist/contracts/recording_credits/deps/sui/vec_set.js +0 -20
  60. package/dist/contracts/recording_credits/deps/sui/vec_set.js.map +0 -1
  61. package/dist/contracts/recording_credits/recording_credits.d.ts +0 -2313
  62. package/dist/contracts/recording_credits/recording_credits.d.ts.map +0 -1
  63. package/dist/contracts/recording_credits/recording_credits.js +0 -270
  64. package/dist/contracts/recording_credits/recording_credits.js.map +0 -1
  65. package/dist/contracts/recording_credits/recording_party_role.d.ts +0 -1174
  66. package/dist/contracts/recording_credits/recording_party_role.d.ts.map +0 -1
  67. package/dist/contracts/recording_credits/recording_party_role.js +0 -692
  68. package/dist/contracts/recording_credits/recording_party_role.js.map +0 -1
  69. package/dist/contracts/recording_royalty_pool/recording_royalty_pool.d.ts +0 -90
  70. package/dist/contracts/recording_royalty_pool/recording_royalty_pool.d.ts.map +0 -1
  71. package/dist/contracts/recording_royalty_pool/recording_royalty_pool.js +0 -80
  72. package/dist/contracts/recording_royalty_pool/recording_royalty_pool.js.map +0 -1
  73. package/dist/contracts/release_cover_art/deps/0x340057f2174fb59e4626742dd2b46c662237837b6187450cb59e4976ce7eac78/walrus_data.d.ts +0 -52
  74. package/dist/contracts/release_cover_art/deps/0x340057f2174fb59e4626742dd2b46c662237837b6187450cb59e4976ce7eac78/walrus_data.d.ts.map +0 -1
  75. package/dist/contracts/release_cover_art/deps/0x340057f2174fb59e4626742dd2b46c662237837b6187450cb59e4976ce7eac78/walrus_data.js +0 -37
  76. package/dist/contracts/release_cover_art/deps/0x340057f2174fb59e4626742dd2b46c662237837b6187450cb59e4976ce7eac78/walrus_data.js.map +0 -1
  77. package/dist/contracts/release_cover_art/deps/cover_art/cover_art.d.ts +0 -46
  78. package/dist/contracts/release_cover_art/deps/cover_art/cover_art.d.ts.map +0 -1
  79. package/dist/contracts/release_cover_art/deps/cover_art/cover_art.js +0 -21
  80. package/dist/contracts/release_cover_art/deps/cover_art/cover_art.js.map +0 -1
  81. package/dist/contracts/release_cover_art/deps/per_track/per_track.d.ts +0 -33
  82. package/dist/contracts/release_cover_art/deps/per_track/per_track.d.ts.map +0 -1
  83. package/dist/contracts/release_cover_art/deps/per_track/per_track.js +0 -32
  84. package/dist/contracts/release_cover_art/deps/per_track/per_track.js.map +0 -1
  85. package/dist/contracts/release_cover_art/release_cover_art.d.ts +0 -300
  86. package/dist/contracts/release_cover_art/release_cover_art.d.ts.map +0 -1
  87. package/dist/contracts/release_cover_art/release_cover_art.js +0 -168
  88. package/dist/contracts/release_cover_art/release_cover_art.js.map +0 -1
  89. package/dist/contracts/release_credits/deps/miso_credit/credit.d.ts +0 -24
  90. package/dist/contracts/release_credits/deps/miso_credit/credit.d.ts.map +0 -1
  91. package/dist/contracts/release_credits/deps/miso_credit/credit.js +0 -23
  92. package/dist/contracts/release_credits/deps/miso_credit/credit.js.map +0 -1
  93. package/dist/contracts/release_credits/deps/sui/vec_map.d.ts +0 -37
  94. package/dist/contracts/release_credits/deps/sui/vec_map.d.ts.map +0 -1
  95. package/dist/contracts/release_credits/deps/sui/vec_map.js +0 -28
  96. package/dist/contracts/release_credits/deps/sui/vec_map.js.map +0 -1
  97. package/dist/contracts/release_credits/release_credits.d.ts +0 -142
  98. package/dist/contracts/release_credits/release_credits.d.ts.map +0 -1
  99. package/dist/contracts/release_credits/release_credits.js +0 -108
  100. package/dist/contracts/release_credits/release_credits.js.map +0 -1
  101. package/dist/contracts/release_credits/release_party_role.d.ts +0 -58
  102. package/dist/contracts/release_credits/release_party_role.d.ts.map +0 -1
  103. package/dist/contracts/release_credits/release_party_role.js +0 -66
  104. package/dist/contracts/release_credits/release_party_role.js.map +0 -1
  105. package/dist/contracts/royalty_pool/deps/std/type_name.d.ts +0 -18
  106. package/dist/contracts/royalty_pool/deps/std/type_name.d.ts.map +0 -1
  107. package/dist/contracts/royalty_pool/deps/std/type_name.js +0 -20
  108. package/dist/contracts/royalty_pool/deps/std/type_name.js.map +0 -1
  109. package/dist/contracts/royalty_pool/deps/sui/balance.d.ts +0 -13
  110. package/dist/contracts/royalty_pool/deps/sui/balance.d.ts.map +0 -1
  111. package/dist/contracts/royalty_pool/deps/sui/balance.js +0 -15
  112. package/dist/contracts/royalty_pool/deps/sui/balance.js.map +0 -1
  113. package/dist/contracts/royalty_pool/deps/sui/vec_map.d.ts +0 -37
  114. package/dist/contracts/royalty_pool/deps/sui/vec_map.d.ts.map +0 -1
  115. package/dist/contracts/royalty_pool/deps/sui/vec_map.js +0 -28
  116. package/dist/contracts/royalty_pool/deps/sui/vec_map.js.map +0 -1
  117. package/dist/contracts/royalty_pool/pool.d.ts +0 -358
  118. package/dist/contracts/royalty_pool/pool.d.ts.map +0 -1
  119. package/dist/contracts/royalty_pool/pool.js +0 -352
  120. package/dist/contracts/royalty_pool/pool.js.map +0 -1
  121. package/dist/contracts/royalty_pool/stake.d.ts +0 -202
  122. package/dist/contracts/royalty_pool/stake.d.ts.map +0 -1
  123. package/dist/contracts/royalty_pool/stake.js +0 -197
  124. package/dist/contracts/royalty_pool/stake.js.map +0 -1
  125. package/dist/cover.d.ts +0 -48
  126. package/dist/cover.d.ts.map +0 -1
  127. package/dist/cover.js +0 -68
  128. package/dist/cover.js.map +0 -1
  129. package/dist/credits.d.ts +0 -171
  130. package/dist/credits.d.ts.map +0 -1
  131. package/dist/credits.js +0 -360
  132. package/dist/credits.js.map +0 -1
  133. package/dist/extensions/royalty-pool.d.ts +0 -32
  134. package/dist/extensions/royalty-pool.d.ts.map +0 -1
  135. package/dist/extensions/royalty-pool.js +0 -33
  136. package/dist/extensions/royalty-pool.js.map +0 -1
  137. package/src/contracts/composition_credits/composition_credits.ts +0 -169
  138. package/src/contracts/composition_credits/composition_party_role.ts +0 -189
  139. package/src/contracts/composition_credits/deps/miso_credit/credit.ts +0 -27
  140. package/src/contracts/composition_credits/deps/sui/vec_map.ts +0 -33
  141. package/src/contracts/composition_royalty_pool/composition_royalty_pool.ts +0 -135
  142. package/src/contracts/cover_art/cover_art.ts +0 -99
  143. package/src/contracts/cover_art/deps/ori/walrus_data.ts +0 -36
  144. package/src/contracts/recording_credits/deps/miso_credit/credit.ts +0 -27
  145. package/src/contracts/recording_credits/deps/sui/vec_map.ts +0 -33
  146. package/src/contracts/recording_credits/deps/sui/vec_set.ts +0 -21
  147. package/src/contracts/recording_credits/recording_credits.ts +0 -458
  148. package/src/contracts/recording_credits/recording_party_role.ts +0 -1041
  149. package/src/contracts/recording_royalty_pool/recording_royalty_pool.ts +0 -138
  150. package/src/contracts/release_cover_art/deps/0x340057f2174fb59e4626742dd2b46c662237837b6187450cb59e4976ce7eac78/walrus_data.ts +0 -36
  151. package/src/contracts/release_cover_art/deps/cover_art/cover_art.ts +0 -23
  152. package/src/contracts/release_cover_art/deps/per_track/per_track.ts +0 -36
  153. package/src/contracts/release_cover_art/release_cover_art.ts +0 -251
  154. package/src/contracts/release_credits/deps/miso_credit/credit.ts +0 -27
  155. package/src/contracts/release_credits/deps/sui/vec_map.ts +0 -33
  156. package/src/contracts/release_credits/release_credits.ts +0 -156
  157. package/src/contracts/release_credits/release_party_role.ts +0 -87
  158. package/src/contracts/royalty_pool/deps/std/type_name.ts +0 -22
  159. package/src/contracts/royalty_pool/deps/sui/balance.ts +0 -17
  160. package/src/contracts/royalty_pool/deps/sui/vec_map.ts +0 -33
  161. package/src/contracts/royalty_pool/pool.ts +0 -577
  162. package/src/contracts/royalty_pool/stake.ts +0 -317
  163. package/src/cover.ts +0 -136
  164. package/src/credits.ts +0 -645
  165. package/src/extensions/royalty-pool.ts +0 -85
package/src/credits.ts DELETED
@@ -1,645 +0,0 @@
1
- // Copyright (c) Miso Labs, Inc.
2
- // SPDX-License-Identifier: Apache-2.0
3
-
4
- // Contributor credits. A credit pairs a party with a display name and one or more
5
- // domain-specific roles (`miso_credit::credit::Credit<Role>`), attached to a work
6
- // via a dynamic field on the work's UID and gated by the work's admin cap. Three
7
- // first-party extensions carry the domain role vocabularies:
8
- //
9
- // - composition_credits — writing credits (Composer, Lyricist, …), no level.
10
- // - recording_credits — production/performance credits (Producer, Vocalist,
11
- // Instrumentalist, …), each with an optional seniority
12
- // level; plus primary/featured artist designation.
13
- // - release_credits — top-line billing, exactly one role (Primary|Featured).
14
- //
15
- // A credit is built in-PTB from raw role constructor move-calls (the role enums
16
- // are closed — only the extension's `new_*_role` functions can mint them), wrapped
17
- // in `credit::new(display_name, roles)`, then attached with the extension's
18
- // `add_credit` (which borrows the work `&mut` via its cap-gated `uid_mut`).
19
- //
20
- // Writers mirror `cover.ts`: they return a `TxThunk` and take explicit on-chain
21
- // package ids. Generic works (Composition, Recording) additionally require the
22
- // share coin type argument(s) so the `&mut Work<Share>` / `&AdminCap<Share>` calls
23
- // resolve.
24
-
25
- import type { ClientWithCoreApi } from "@mysten/sui/client";
26
- import { bcs } from "@mysten/sui/bcs";
27
- import { deriveDynamicFieldID } from "@mysten/sui/utils";
28
- import type { Transaction, TransactionArgument, TransactionObjectArgument } from "@mysten/sui/transactions";
29
- import type { TxThunk } from "./transactions.ts";
30
- import { OPTION_NONE, OPTION_SOME } from "./internal.ts";
31
- import { isNotFound } from "./queries.ts";
32
- import * as compositionCredits from "./contracts/composition_credits/composition_credits.ts";
33
- import * as compositionPartyRole from "./contracts/composition_credits/composition_party_role.ts";
34
- import * as recordingCredits from "./contracts/recording_credits/recording_credits.ts";
35
- import * as recordingPartyRole from "./contracts/recording_credits/recording_party_role.ts";
36
- import * as releaseCredits from "./contracts/release_credits/release_credits.ts";
37
- import * as releasePartyRole from "./contracts/release_credits/release_party_role.ts";
38
-
39
- // ── Role model ────────────────────────────────────────────────────────────────
40
-
41
- /** A composition writing role. `Custom` carries a free-form (validated) name. */
42
- export type CompositionRole =
43
- | { type: "Adapter" | "Arranger" | "Composer" | "Lyricist" | "Songwriter" | "Translator" }
44
- | { type: "Custom"; name: string };
45
-
46
- /** Seniority/prominence level for a recording role. */
47
- export type RecordingRoleLevel =
48
- | "Additional"
49
- | "Assistant"
50
- | "Associate"
51
- | "Backing"
52
- | "Executive"
53
- | "Featured"
54
- | "Lead"
55
- | "Primary"
56
- | "Principal";
57
-
58
- /** Recording role base names that carry an optional level. */
59
- export type RecordingLeveledRoleType =
60
- | "Actor"
61
- | "Arranger"
62
- | "BandLeader"
63
- | "Choir"
64
- | "ChoirMaster"
65
- | "ConcertMaster"
66
- | "Conductor"
67
- | "Contractor"
68
- | "DJ"
69
- | "Editor"
70
- | "Engineer"
71
- | "Ensemble"
72
- | "MasteringEngineer"
73
- | "MixingEngineer"
74
- | "MusicDirector"
75
- | "MusicSupervisor"
76
- | "Narrator"
77
- | "Orchestra"
78
- | "Orchestrator"
79
- | "Performer"
80
- | "Producer"
81
- | "Programmer"
82
- | "RecordingEngineer"
83
- | "RemixingEngineer"
84
- | "Soloist"
85
- | "SoundDesigner"
86
- | "Speaker"
87
- | "Vocalist";
88
-
89
- /**
90
- * A recording production/performance role. Most roles take an optional `level`;
91
- * `Instrumentalist` also carries the instrument name; `Custom` carries a free-form
92
- * (validated) name; `ArtistsAndRepertoire` and `Copyist` are clerical and take no
93
- * level.
94
- */
95
- export type RecordingRole =
96
- | { type: RecordingLeveledRoleType; level?: RecordingRoleLevel }
97
- | { type: "Instrumentalist"; instrument: string; level?: RecordingRoleLevel }
98
- | { type: "Custom"; name: string; level?: RecordingRoleLevel }
99
- | { type: "ArtistsAndRepertoire" }
100
- | { type: "Copyist" };
101
-
102
- /** A release billing role — a release credit carries exactly one of these. */
103
- export type ReleaseRole = "Primary" | "Featured";
104
-
105
- // Role constructor lookups. Each generated `new_*_role` returns a thunk that adds
106
- // the constructor move-call; `tx.add` runs it and yields the role value.
107
- type RoleThunk = (tx: Transaction) => TransactionObjectArgument;
108
- type LevelCtor = (options: { package?: string }) => RoleThunk;
109
- type LeveledRoleCtor = (options: { package?: string; arguments: [TransactionArgument] }) => RoleThunk;
110
-
111
- const COMPOSITION_ROLE_CTOR: Record<Exclude<CompositionRole["type"], "Custom">, LevelCtor> = {
112
- Adapter: compositionPartyRole.newAdapterRole,
113
- Arranger: compositionPartyRole.newArrangerRole,
114
- Composer: compositionPartyRole.newComposerRole,
115
- Lyricist: compositionPartyRole.newLyricistRole,
116
- Songwriter: compositionPartyRole.newSongwriterRole,
117
- Translator: compositionPartyRole.newTranslatorRole,
118
- };
119
-
120
- const RECORDING_LEVEL_CTOR: Record<RecordingRoleLevel, LevelCtor> = {
121
- Additional: recordingPartyRole.newAdditionalRoleLevel,
122
- Assistant: recordingPartyRole.newAssistantRoleLevel,
123
- Associate: recordingPartyRole.newAssociateRoleLevel,
124
- Backing: recordingPartyRole.newBackingRoleLevel,
125
- Executive: recordingPartyRole.newExecutiveRoleLevel,
126
- Featured: recordingPartyRole.newFeaturedRoleLevel,
127
- Lead: recordingPartyRole.newLeadRoleLevel,
128
- Primary: recordingPartyRole.newPrimaryRoleLevel,
129
- Principal: recordingPartyRole.newPrincipalRoleLevel,
130
- };
131
-
132
- const RECORDING_LEVELED_ROLE_CTOR: Record<RecordingLeveledRoleType, LeveledRoleCtor> = {
133
- Actor: recordingPartyRole.newActorRole,
134
- Arranger: recordingPartyRole.newArrangerRole,
135
- BandLeader: recordingPartyRole.newBandLeaderRole,
136
- Choir: recordingPartyRole.newChoirRole,
137
- ChoirMaster: recordingPartyRole.newChoirMasterRole,
138
- ConcertMaster: recordingPartyRole.newConcertMasterRole,
139
- Conductor: recordingPartyRole.newConductorRole,
140
- Contractor: recordingPartyRole.newContractorRole,
141
- DJ: recordingPartyRole.newDjRole,
142
- Editor: recordingPartyRole.newEditorRole,
143
- Engineer: recordingPartyRole.newEngineerRole,
144
- Ensemble: recordingPartyRole.newEnsembleRole,
145
- MasteringEngineer: recordingPartyRole.newMasteringEngineerRole,
146
- MixingEngineer: recordingPartyRole.newMixingEngineerRole,
147
- MusicDirector: recordingPartyRole.newMusicDirectorRole,
148
- MusicSupervisor: recordingPartyRole.newMusicSupervisorRole,
149
- Narrator: recordingPartyRole.newNarratorRole,
150
- Orchestra: recordingPartyRole.newOrchestraRole,
151
- Orchestrator: recordingPartyRole.newOrchestratorRole,
152
- Performer: recordingPartyRole.newPerformerRole,
153
- Producer: recordingPartyRole.newProducerRole,
154
- Programmer: recordingPartyRole.newProgrammerRole,
155
- RecordingEngineer: recordingPartyRole.newRecordingEngineerRole,
156
- RemixingEngineer: recordingPartyRole.newRemixingEngineerRole,
157
- Soloist: recordingPartyRole.newSoloistRole,
158
- SoundDesigner: recordingPartyRole.newSoundDesignerRole,
159
- Speaker: recordingPartyRole.newSpeakerRole,
160
- Vocalist: recordingPartyRole.newVocalistRole,
161
- };
162
-
163
- /** Builds an `Option<RecordingPartyRoleLevel>` argument for a role constructor. */
164
- function levelOption(
165
- tx: Transaction,
166
- pkg: string,
167
- level: RecordingRoleLevel | undefined,
168
- ): TransactionArgument {
169
- const levelType = `${pkg}::recording_party_role::RecordingPartyRoleLevel`;
170
- if (!level) return tx.moveCall({ target: OPTION_NONE, typeArguments: [levelType] });
171
- const lvl = tx.add(RECORDING_LEVEL_CTOR[level]({ package: pkg }));
172
- return tx.moveCall({ target: OPTION_SOME, typeArguments: [levelType], arguments: [lvl] });
173
- }
174
-
175
- /** Builds one `CompositionPartyRole` value in the PTB. */
176
- function buildCompositionRole(tx: Transaction, pkg: string, role: CompositionRole): TransactionObjectArgument {
177
- if (role.type === "Custom") {
178
- return tx.add(compositionPartyRole.newCustomRole({ package: pkg, arguments: [role.name] }));
179
- }
180
- return tx.add(COMPOSITION_ROLE_CTOR[role.type]({ package: pkg }));
181
- }
182
-
183
- /** Builds one `RecordingPartyRole` value in the PTB. */
184
- function buildRecordingRole(tx: Transaction, pkg: string, role: RecordingRole): TransactionObjectArgument {
185
- switch (role.type) {
186
- case "ArtistsAndRepertoire":
187
- return tx.add(recordingPartyRole.newArtistsAndRepertoireRole({ package: pkg }));
188
- case "Copyist":
189
- return tx.add(recordingPartyRole.newCopyistRole({ package: pkg }));
190
- case "Instrumentalist":
191
- return tx.add(
192
- recordingPartyRole.newInstrumentalistRole({
193
- package: pkg,
194
- arguments: [role.instrument, levelOption(tx, pkg, role.level)],
195
- }),
196
- );
197
- case "Custom":
198
- return tx.add(
199
- recordingPartyRole.newCustomRole({
200
- package: pkg,
201
- arguments: [role.name, levelOption(tx, pkg, role.level)],
202
- }),
203
- );
204
- default:
205
- return tx.add(
206
- RECORDING_LEVELED_ROLE_CTOR[role.type]({
207
- package: pkg,
208
- arguments: [levelOption(tx, pkg, role.level)],
209
- }),
210
- );
211
- }
212
- }
213
-
214
- /** Builds one `ReleasePartyRole` value in the PTB. */
215
- function buildReleaseRole(tx: Transaction, pkg: string, role: ReleaseRole): TransactionObjectArgument {
216
- const ctor = role === "Primary" ? releasePartyRole.newPrimaryRole : releasePartyRole.newFeaturedRole;
217
- return tx.add(ctor({ package: pkg }));
218
- }
219
-
220
- // ── Client-side validation ────────────────────────────────────────────────────
221
- //
222
- // Mirrors the Move aborts so bad params fail fast (and readably) at the writer
223
- // call site instead of as an on-chain abort code:
224
- // - miso_credit::credit::new — EEmptyString / EMaxDisplayNameLengthExceeded
225
- // (display name non-empty, ≤200 BYTES of UTF-8) and ENoRoles / EDuplicateRoles.
226
- // - composition_credits::add_credit — EMinRolesNotMet / EExceedsMaxRoles (1–5).
227
- // - recording_credits::add_credit — EMinRolesNotMet / EExceedsMaxRoles (1–10).
228
-
229
- const MAX_DISPLAY_NAME_BYTES = 200;
230
- const MAX_COMPOSITION_ROLES = 5;
231
- const MAX_RECORDING_ROLES = 10;
232
-
233
- function assertDisplayName(fn: string, displayName: string): void {
234
- if (displayName.length === 0) throw new Error(`${fn}: displayName must not be empty`);
235
- const byteLength = new TextEncoder().encode(displayName).length;
236
- if (byteLength > MAX_DISPLAY_NAME_BYTES) {
237
- throw new Error(`${fn}: displayName must be at most ${MAX_DISPLAY_NAME_BYTES} bytes of UTF-8 (got ${byteLength})`);
238
- }
239
- }
240
-
241
- // Move rejects duplicates by full struct equality, so the duplicate key is the
242
- // role's complete identity: type + instrument + custom name + level.
243
- function roleKey(role: CompositionRole | RecordingRole): string {
244
- return JSON.stringify([
245
- role.type,
246
- "instrument" in role ? role.instrument : null,
247
- "name" in role ? role.name : null,
248
- "level" in role && role.level !== undefined ? role.level : null,
249
- ]);
250
- }
251
-
252
- function assertRoles(fn: string, roles: (CompositionRole | RecordingRole)[], max: number): void {
253
- if (roles.length < 1) throw new Error(`${fn}: at least one role is required`);
254
- if (roles.length > max) throw new Error(`${fn}: at most ${max} roles are allowed (got ${roles.length})`);
255
- const seen = new Set<string>();
256
- for (const role of roles) {
257
- const key = roleKey(role);
258
- if (seen.has(key)) throw new Error(`${fn}: duplicate role ${key}`);
259
- seen.add(key);
260
- }
261
- }
262
-
263
- /** Wraps built role values in a `miso_credit::credit::Credit<Role>`. */
264
- function buildCredit(
265
- tx: Transaction,
266
- misoCreditPackageId: string,
267
- roleType: string,
268
- displayName: string,
269
- roleArgs: TransactionObjectArgument[],
270
- ): TransactionObjectArgument {
271
- const roles = tx.makeMoveVec({ type: roleType, elements: roleArgs });
272
- return tx.moveCall({
273
- target: `${misoCreditPackageId}::credit::new`,
274
- typeArguments: [roleType],
275
- arguments: [tx.pure.string(displayName), roles],
276
- });
277
- }
278
-
279
- // ── Writers ───────────────────────────────────────────────────────────────────
280
-
281
- export interface AttachCompositionCreditParams {
282
- /** The `Composition` object to credit on. */
283
- compositionId: string;
284
- /** The composition's `CompositionAdminCap`. */
285
- compositionAdminCapId: string;
286
- /** The `Party` being credited. */
287
- partyId: string;
288
- /** Human-readable name for the credit (≤200 bytes, non-empty). */
289
- displayName: string;
290
- /** 1–5 writing roles; duplicates are rejected on-chain. */
291
- roles: CompositionRole[];
292
- /** The composition's share coin type (the `CompositionShare` phantom). */
293
- compositionShareType: string;
294
- /** `composition_credits` package id (roles + `add_credit`). */
295
- compositionCreditsPackageId: string;
296
- /** `miso_credit` package id (home of `credit::new`). */
297
- misoCreditPackageId: string;
298
- }
299
-
300
- /**
301
- * Adds a writing credit for a party on a composition. Throws (client-side,
302
- * mirroring the Move aborts) when `displayName` is empty or over 200 UTF-8
303
- * bytes, or `roles` is empty, has more than 5 entries, or contains duplicates.
304
- */
305
- export function attachCompositionCredit(p: AttachCompositionCreditParams): TxThunk {
306
- assertDisplayName("attachCompositionCredit", p.displayName);
307
- assertRoles("attachCompositionCredit", p.roles, MAX_COMPOSITION_ROLES);
308
- return (tx) => {
309
- const roleType = `${p.compositionCreditsPackageId}::composition_party_role::CompositionPartyRole`;
310
- const roleArgs = p.roles.map((r) => buildCompositionRole(tx, p.compositionCreditsPackageId, r));
311
- const credit = buildCredit(tx, p.misoCreditPackageId, roleType, p.displayName, roleArgs);
312
- tx.add(
313
- compositionCredits.addCredit({
314
- package: p.compositionCreditsPackageId,
315
- typeArguments: [p.compositionShareType],
316
- arguments: [p.compositionId, p.compositionAdminCapId, p.partyId, credit],
317
- }),
318
- );
319
- };
320
- }
321
-
322
- export interface AttachRecordingCreditParams {
323
- /** The `Recording` object to credit on. */
324
- recordingId: string;
325
- /** The recording's `RecordingAdminCap`. */
326
- recordingAdminCapId: string;
327
- /** The `Party` being credited. */
328
- partyId: string;
329
- /** Human-readable name for the credit (≤200 bytes, non-empty). */
330
- displayName: string;
331
- /** 1–10 production/performance roles; duplicates are rejected on-chain. */
332
- roles: RecordingRole[];
333
- /** The recording's own share coin type (the `RecordingShare` phantom). */
334
- recordingShareType: string;
335
- /** The parent composition's share coin type (the `CompositionShare` phantom). */
336
- compositionShareType: string;
337
- /** `recording_credits` package id (roles + `add_credit`). */
338
- recordingCreditsPackageId: string;
339
- /** `miso_credit` package id (home of `credit::new`). */
340
- misoCreditPackageId: string;
341
- }
342
-
343
- /**
344
- * Adds a production/performance credit for a party on a recording. Throws
345
- * (client-side, mirroring the Move aborts) when `displayName` is empty or over
346
- * 200 UTF-8 bytes, or `roles` is empty, has more than 10 entries, or contains
347
- * duplicates (same type + instrument + custom name + level).
348
- */
349
- export function attachRecordingCredit(p: AttachRecordingCreditParams): TxThunk {
350
- assertDisplayName("attachRecordingCredit", p.displayName);
351
- assertRoles("attachRecordingCredit", p.roles, MAX_RECORDING_ROLES);
352
- return (tx) => {
353
- const roleType = `${p.recordingCreditsPackageId}::recording_party_role::RecordingPartyRole`;
354
- const roleArgs = p.roles.map((r) => buildRecordingRole(tx, p.recordingCreditsPackageId, r));
355
- const credit = buildCredit(tx, p.misoCreditPackageId, roleType, p.displayName, roleArgs);
356
- tx.add(
357
- recordingCredits.addCredit({
358
- package: p.recordingCreditsPackageId,
359
- typeArguments: [p.recordingShareType, p.compositionShareType],
360
- arguments: [p.recordingId, p.recordingAdminCapId, p.partyId, credit],
361
- }),
362
- );
363
- };
364
- }
365
-
366
- export interface AddRecordingArtistParams {
367
- /** The `Recording` object. */
368
- recordingId: string;
369
- /** The recording's `RecordingAdminCap`. */
370
- recordingAdminCapId: string;
371
- /** The `Party` to designate. Must already be credited on the recording. */
372
- partyId: string;
373
- /** The recording's own share coin type (the `RecordingShare` phantom). */
374
- recordingShareType: string;
375
- /** The parent composition's share coin type (the `CompositionShare` phantom). */
376
- compositionShareType: string;
377
- /** `recording_credits` package id. */
378
- recordingCreditsPackageId: string;
379
- }
380
-
381
- /**
382
- * Designates an already-credited party as a primary artist on a recording. The
383
- * party must be credited first (via `attachRecordingCredit`) and not already a
384
- * primary or featured artist.
385
- */
386
- export function addRecordingPrimaryArtist(p: AddRecordingArtistParams): TxThunk {
387
- return (tx) => {
388
- tx.add(
389
- recordingCredits.addPrimaryArtist({
390
- package: p.recordingCreditsPackageId,
391
- typeArguments: [p.recordingShareType, p.compositionShareType],
392
- arguments: [p.recordingId, p.recordingAdminCapId, p.partyId],
393
- }),
394
- );
395
- };
396
- }
397
-
398
- /**
399
- * Designates an already-credited party as a featured artist on a recording. The
400
- * party must be credited first (via `attachRecordingCredit`) and not already a
401
- * primary or featured artist.
402
- */
403
- export function addRecordingFeaturedArtist(p: AddRecordingArtistParams): TxThunk {
404
- return (tx) => {
405
- tx.add(
406
- recordingCredits.addFeaturedArtist({
407
- package: p.recordingCreditsPackageId,
408
- typeArguments: [p.recordingShareType, p.compositionShareType],
409
- arguments: [p.recordingId, p.recordingAdminCapId, p.partyId],
410
- }),
411
- );
412
- };
413
- }
414
-
415
- export interface AddReleaseCreditParams {
416
- /** The `Release` object to credit on. */
417
- releaseId: string;
418
- /** The release's `ReleaseAdminCap`. */
419
- releaseAdminCapId: string;
420
- /** The `Party` being credited. */
421
- partyId: string;
422
- /** Human-readable name for the credit (≤200 bytes, non-empty). */
423
- displayName: string;
424
- /** The single billing role: `"Primary"` or `"Featured"`. */
425
- role: ReleaseRole;
426
- /** `release_credits` package id (roles + `add_credit`). */
427
- releaseCreditsPackageId: string;
428
- /** `miso_credit` package id (home of `credit::new`). */
429
- misoCreditPackageId: string;
430
- }
431
-
432
- /**
433
- * Adds a top-line billing credit (a single role) for a party on a release.
434
- * Throws (client-side, mirroring the Move aborts) when `displayName` is empty
435
- * or over 200 UTF-8 bytes. The single role is structural, so no role checks.
436
- */
437
- export function addReleaseCredit(p: AddReleaseCreditParams): TxThunk {
438
- assertDisplayName("addReleaseCredit", p.displayName);
439
- return (tx) => {
440
- const roleType = `${p.releaseCreditsPackageId}::release_party_role::ReleasePartyRole`;
441
- const roleArg = buildReleaseRole(tx, p.releaseCreditsPackageId, p.role);
442
- const credit = buildCredit(tx, p.misoCreditPackageId, roleType, p.displayName, [roleArg]);
443
- tx.add(
444
- releaseCredits.addCredit({
445
- package: p.releaseCreditsPackageId,
446
- arguments: [p.releaseId, p.releaseAdminCapId, p.partyId, credit],
447
- }),
448
- );
449
- };
450
- }
451
-
452
- // ── Reads ─────────────────────────────────────────────────────────────────────
453
-
454
- /** A normalized credit: the party, its display name, and its role labels. */
455
- export interface CreditView {
456
- /** Object id of the credited `Party`. */
457
- partyId: string;
458
- /** Human-readable name for the credit. */
459
- displayName: string;
460
- /**
461
- * Role labels (the `name()` semantics). For a recording `Instrumentalist` the
462
- * instrument is included (`"Instrumentalist: Guitar"`); a recording role's level
463
- * is appended in parentheses (`"Producer (Lead)"`).
464
- */
465
- roles: string[];
466
- }
467
-
468
- /** A recording's credits, plus its primary/featured artist party ids. */
469
- export interface RecordingCreditsView {
470
- credits: CreditView[];
471
- primaryArtistIds: string[];
472
- featuredArtistIds: string[];
473
- }
474
-
475
- // A parsed closed-enum value: `{ $kind, [variant]: payload }`. Unit variants carry
476
- // `true`; data variants carry their parsed payload.
477
- type ParsedEnum = { $kind: string } & Record<string, unknown>;
478
- type ParsedLevel = ParsedEnum | null;
479
-
480
- // Credits are stored as a dynamic field on the work's UID under the extension's
481
- // `ExtensionKey()` — a positional struct with no fields, so Move's implicit
482
- // `dummy_field: bool` serializes to a single `false` byte. The stored object is a
483
- // `Field { id, name: <Key>, value: <Credits> }`.
484
- const CompositionCreditsField = bcs.struct("Field", {
485
- id: bcs.Address,
486
- name: compositionCredits.ExtensionKey,
487
- value: compositionCredits.CompositionCredits,
488
- });
489
- const RecordingCreditsField = bcs.struct("Field", {
490
- id: bcs.Address,
491
- name: recordingCredits.ExtensionKey,
492
- value: recordingCredits.RecordingCredits,
493
- });
494
- const ReleaseCreditsField = bcs.struct("Field", {
495
- id: bcs.Address,
496
- name: releaseCredits.ExtensionKey,
497
- value: releaseCredits.ReleaseCredits,
498
- });
499
-
500
- const COMPOSITION_CREDITS_KEY_BYTES = compositionCredits.ExtensionKey.serialize([false]).toBytes();
501
- const RECORDING_CREDITS_KEY_BYTES = recordingCredits.ExtensionKey.serialize([false]).toBytes();
502
- const RELEASE_CREDITS_KEY_BYTES = releaseCredits.ExtensionKey.serialize([false]).toBytes();
503
-
504
- /** Fetches the BCS content of a work's credits dynamic field, or `null` if none. */
505
- async function fetchCreditsField(
506
- client: ClientWithCoreApi,
507
- workId: string,
508
- keyType: string,
509
- keyBytes: Uint8Array,
510
- ): Promise<Uint8Array | null> {
511
- const fieldId = deriveDynamicFieldID(workId, keyType, keyBytes);
512
- try {
513
- const { object } = await client.core.getObject({ objectId: fieldId, include: { content: true } });
514
- return object?.content ?? null;
515
- } catch (e) {
516
- if (isNotFound(e)) return null;
517
- throw e;
518
- }
519
- }
520
-
521
- // The credits container shape, checked structurally against the parse output of
522
- // the generated bindings (their `$inferType`): the readers below pass
523
- // `<X>CreditsField.parse(...).value.credits` in WITHOUT casting, so a codegen
524
- // field rename (`contents`/`key`/`value`/`display_name`/`roles`, or the
525
- // `credits`/`primary_artist_ids`/`featured_artist_ids` fields at the call
526
- // sites) fails typecheck here instead of silently misparsing.
527
- type ParsedCreditsMap<Role> = { contents: { key: string; value: { display_name: string; roles: Role[] } }[] };
528
-
529
- function creditViews<Role extends ParsedEnum>(
530
- credits: ParsedCreditsMap<Role>,
531
- roleLabel: (role: Role) => string,
532
- ): CreditView[] {
533
- return credits.contents.map((entry) => ({
534
- partyId: entry.key,
535
- displayName: entry.value.display_name,
536
- roles: entry.value.roles.map(roleLabel),
537
- }));
538
- }
539
-
540
- // `name()` semantics: the `$kind` is the canonical PascalCase token; `Custom`
541
- // carries the user-supplied name in its payload.
542
- function compositionRoleLabel(role: ParsedEnum): string {
543
- return role.$kind === "Custom" ? (role.Custom as string) : role.$kind;
544
- }
545
-
546
- function releaseRoleLabel(role: ParsedEnum): string {
547
- return role.$kind;
548
- }
549
-
550
- // `name()` semantics plus the recording extras: the instrument (for
551
- // `Instrumentalist`) and the optional level, both surfaced into the label.
552
- function recordingRoleLabel(role: ParsedEnum): string {
553
- let base: string;
554
- let level: ParsedLevel = null;
555
- switch (role.$kind) {
556
- case "Instrumentalist": {
557
- const [instrument, lvl] = role.Instrumentalist as [string, ParsedLevel];
558
- base = `Instrumentalist: ${instrument}`;
559
- level = lvl;
560
- break;
561
- }
562
- case "Custom": {
563
- const [name, lvl] = role.Custom as [string, ParsedLevel];
564
- base = name;
565
- level = lvl;
566
- break;
567
- }
568
- case "ArtistsAndRepertoire":
569
- case "Copyist":
570
- base = role.$kind;
571
- break;
572
- default: {
573
- // A leveled variant's payload is `Option<Level>`: `None` parses to `true`
574
- // (the unit-payload convention), `Some(level)` to the level enum object.
575
- base = role.$kind;
576
- const payload = role[role.$kind];
577
- level =
578
- payload && typeof payload === "object" && "$kind" in payload ? (payload as ParsedEnum) : null;
579
- break;
580
- }
581
- }
582
- return level ? `${base} (${level.$kind})` : base;
583
- }
584
-
585
- /**
586
- * Reads a composition's writing credits (the `composition_credits` extension), or
587
- * `null` if no credits field is attached.
588
- */
589
- export async function getCompositionCredits(
590
- client: ClientWithCoreApi,
591
- compositionId: string,
592
- compositionCreditsPackageId: string,
593
- ): Promise<CreditView[] | null> {
594
- const content = await fetchCreditsField(
595
- client,
596
- compositionId,
597
- `${compositionCreditsPackageId}::composition_credits::ExtensionKey`,
598
- COMPOSITION_CREDITS_KEY_BYTES,
599
- );
600
- if (!content) return null;
601
- return creditViews(CompositionCreditsField.parse(content).value.credits, compositionRoleLabel);
602
- }
603
-
604
- /**
605
- * Reads a recording's credits plus its primary/featured artist party ids (the
606
- * `recording_credits` extension), or `null` if no credits field is attached.
607
- */
608
- export async function getRecordingCredits(
609
- client: ClientWithCoreApi,
610
- recordingId: string,
611
- recordingCreditsPackageId: string,
612
- ): Promise<RecordingCreditsView | null> {
613
- const content = await fetchCreditsField(
614
- client,
615
- recordingId,
616
- `${recordingCreditsPackageId}::recording_credits::ExtensionKey`,
617
- RECORDING_CREDITS_KEY_BYTES,
618
- );
619
- if (!content) return null;
620
- const value = RecordingCreditsField.parse(content).value;
621
- return {
622
- credits: creditViews(value.credits, recordingRoleLabel),
623
- primaryArtistIds: value.primary_artist_ids.contents,
624
- featuredArtistIds: value.featured_artist_ids.contents,
625
- };
626
- }
627
-
628
- /**
629
- * Reads a release's top-line billing credits (the `release_credits` extension), or
630
- * `null` if no credits field is attached. Each credit has exactly one role.
631
- */
632
- export async function getReleaseCredits(
633
- client: ClientWithCoreApi,
634
- releaseId: string,
635
- releaseCreditsPackageId: string,
636
- ): Promise<CreditView[] | null> {
637
- const content = await fetchCreditsField(
638
- client,
639
- releaseId,
640
- `${releaseCreditsPackageId}::release_credits::ExtensionKey`,
641
- RELEASE_CREDITS_KEY_BYTES,
642
- );
643
- if (!content) return null;
644
- return creditViews(ReleaseCreditsField.parse(content).value.credits, releaseRoleLabel);
645
- }