@misofm/sdk 0.15.10 → 0.15.11

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.
@@ -406,7 +406,7 @@ async function resolveVaultedReleaseCaps(
406
406
  const vault = vaultContract.Vault(networkContracts.release.ReleaseAdminCap).parse(
407
407
  object.content,
408
408
  );
409
- const releaseId = vault.cap.value?.release_id;
409
+ const releaseId = vault.cap?.value?.release_id;
410
410
  if (releaseId) releasesByVault.set(object.objectId, releaseId);
411
411
  } catch {
412
412
  // Batch catalog discovery is best-effort per authority, matching the
@@ -615,20 +615,54 @@ export async function getWorkByCap(client: MisoClient, capId: string): Promise<W
615
615
 
616
616
  // ── Balance ──────────────────────────────────────────────────────────────────
617
617
 
618
+ const decimalsByClient = new WeakMap<MisoClient, Map<string, Promise<number>>>();
619
+
620
+ /** Resolve once per client/type. Failed lookups are evicted so a transient
621
+ * fullnode error cannot poison a long-lived API worker. */
622
+ async function coinDecimals(client: MisoClient, coinType: string): Promise<number> {
623
+ let cache = decimalsByClient.get(client);
624
+ if (!cache) {
625
+ cache = new Map();
626
+ decimalsByClient.set(client, cache);
627
+ }
628
+ const key = normalizeStructTag(coinType);
629
+ const cached = cache.get(key);
630
+ if (cached) return cached;
631
+
632
+ const pending = client.protocol.core.getCoinMetadata({ coinType: key }).then(({ coinMetadata }) => {
633
+ const decimals = coinMetadata?.decimals;
634
+ if (!Number.isSafeInteger(decimals) || decimals! < 0 || decimals! > 18) {
635
+ throw new Error(`Coin metadata for ${key} has no supported decimal precision`);
636
+ }
637
+ return decimals!;
638
+ });
639
+ cache.set(key, pending);
640
+ try {
641
+ return await pending;
642
+ } catch (error) {
643
+ if (cache.get(key) === pending) cache.delete(key);
644
+ throw error;
645
+ }
646
+ }
647
+
618
648
  /**
619
- * A wallet's spendable balance in one currency. `core.getBalance` totals coin
620
- * OBJECTS and the address balance, so funds that arrived either way are counted —
621
- * which matters because `balance::send_funds` (how the app transfers dollars)
622
- * credits the address balance, not a coin.
649
+ * A wallet's balance in one currency. Keep the aggregate and both Sui storage
650
+ * classes: callers using a `FundsWithdrawal` must never mistake coin-object
651
+ * value for immediately withdrawable address balance.
623
652
  */
624
653
  export async function getBalance(client: MisoClient, address: string, coinType?: string): Promise<Balance> {
625
654
  const type = coinType ?? client.config.money.usdCoinType;
626
- const res = await client.protocol.core.getBalance({ owner: address, coinType: type });
655
+ const [res, decimals] = await Promise.all([
656
+ client.protocol.core.getBalance({ owner: address, coinType: type }),
657
+ coinDecimals(client, type),
658
+ ]);
627
659
  return {
628
660
  address: normalizeSuiAddress(address),
629
661
  coinType: type,
630
662
  balance: u64(res.balance.balance),
631
- decimals: type === client.config.money.usdCoinType ? client.config.money.usdDecimals : 9,
663
+ coinBalance: u64(res.balance.coinBalance),
664
+ addressBalance: u64(res.balance.addressBalance),
665
+ decimals,
632
666
  };
633
667
  }
634
668
 
package/src/vault.ts CHANGED
@@ -17,7 +17,7 @@ import type {
17
17
  TransactionArgument,
18
18
  TransactionObjectArgument,
19
19
  } from "@mysten/sui/transactions";
20
- import { normalizeStructTag } from "@mysten/sui/utils";
20
+ import { deriveObjectID, normalizeStructTag } from "@mysten/sui/utils";
21
21
  import * as vault from "./contracts/vault/vault.ts";
22
22
  import * as compositionRoyaltyPool from "./contracts/composition_royalty_pool/composition_royalty_pool.ts";
23
23
  import * as recordingRoyaltyPool from "./contracts/recording_royalty_pool/recording_royalty_pool.ts";
@@ -145,6 +145,8 @@ export function invokeWithAdminCap(
145
145
  export interface CustodyNewAdminCapParams {
146
146
  /** The freshly-created raw protocol cap, consumed into the Vault. */
147
147
  readonly adminCap: TransactionObjectArgument;
148
+ /** Shared singleton from which canonical Vault IDs are derived. */
149
+ readonly vaultRegistry: ObjectInput;
148
150
  readonly capType: string;
149
151
  readonly vaultPackageId: string;
150
152
  /** Recipient of the only owner-held authority: VaultAdminCap. */
@@ -162,6 +164,7 @@ export type AdminCapCustody =
162
164
  | {
163
165
  readonly kind: "vault";
164
166
  readonly owner: string | TransactionArgument;
167
+ readonly vaultRegistry: ObjectInput;
165
168
  readonly capType: string;
166
169
  readonly vaultPackageId: string;
167
170
  readonly configure?: CustodyNewAdminCapParams["configure"];
@@ -179,6 +182,7 @@ export function disposeNewAdminCap(
179
182
  }
180
183
  custodyNewAdminCap(tx, {
181
184
  adminCap,
185
+ vaultRegistry: custody.vaultRegistry,
182
186
  capType: custody.capType,
183
187
  vaultPackageId: custody.vaultPackageId,
184
188
  owner: custody.owner,
@@ -186,23 +190,60 @@ export function disposeNewAdminCap(
186
190
  });
187
191
  }
188
192
 
189
- /** Destroy a vault only while deliberately disposing of its returned raw cap. */
190
- export function destroyVault(
193
+ /** Withdraw the raw capability while leaving its canonical Vault shell intact. */
194
+ export function withdrawVaultCapability(
191
195
  tx: Transaction,
192
196
  params: {
193
197
  readonly vault: ObjectInput;
194
198
  readonly vaultAdminCap: ObjectInput;
195
199
  readonly capType: string;
196
200
  readonly vaultPackageId: string;
197
- readonly disposition: AdminCapCustody;
198
201
  },
199
- ): void {
200
- const cap = tx.add(vault.destroy({
202
+ ): TransactionObjectArgument {
203
+ return tx.add(vault.withdrawCap({
201
204
  package: params.vaultPackageId,
202
205
  typeArguments: [params.capType],
203
206
  arguments: [object(tx, params.vault), object(tx, params.vaultAdminCap)],
204
207
  }));
205
- disposeNewAdminCap(tx, cap, params.disposition);
208
+ }
209
+
210
+ /** Restore the one exact capability permanently assigned to a Vault. */
211
+ export function restoreVaultCapability(
212
+ tx: Transaction,
213
+ params: {
214
+ readonly vault: ObjectInput;
215
+ readonly vaultAdminCap: ObjectInput;
216
+ readonly adminCap: ObjectInput;
217
+ readonly capType: string;
218
+ readonly vaultPackageId: string;
219
+ },
220
+ ): void {
221
+ tx.add(vault.restoreCap({
222
+ package: params.vaultPackageId,
223
+ typeArguments: [params.capType],
224
+ arguments: [
225
+ object(tx, params.vault),
226
+ object(tx, params.vaultAdminCap),
227
+ object(tx, params.adminCap),
228
+ ],
229
+ }));
230
+ }
231
+
232
+ /** Transfer the key-only VaultAdminCap through the Vault module. */
233
+ export function transferVaultAdminCap(
234
+ tx: Transaction,
235
+ params: {
236
+ readonly vaultAdminCap: ObjectInput;
237
+ readonly owner: string | TransactionArgument;
238
+ readonly capType: string;
239
+ readonly vaultPackageId: string;
240
+ },
241
+ ): void {
242
+ tx.add(vault.transferAdminCap({
243
+ package: params.vaultPackageId,
244
+ typeArguments: [params.capType],
245
+ arguments: [object(tx, params.vaultAdminCap), params.owner],
246
+ }));
206
247
  }
207
248
 
208
249
  /**
@@ -217,7 +258,7 @@ export function custodyNewAdminCap(
217
258
  vault._new({
218
259
  package: params.vaultPackageId,
219
260
  typeArguments: [params.capType],
220
- arguments: [params.adminCap],
261
+ arguments: [object(tx, params.vaultRegistry), params.adminCap],
221
262
  }),
222
263
  );
223
264
  const vaultObject = requiredVaultResult(created, 0, "vault::_new");
@@ -230,7 +271,43 @@ export function custodyNewAdminCap(
230
271
  arguments: [vaultObject],
231
272
  }),
232
273
  );
233
- tx.transferObjects([vaultAdminCap], params.owner);
274
+ transferVaultAdminCap(tx, {
275
+ vaultAdminCap,
276
+ owner: params.owner,
277
+ capType: params.capType,
278
+ vaultPackageId: params.vaultPackageId,
279
+ });
280
+ }
281
+
282
+ export interface VaultIdParams {
283
+ readonly vaultRegistryId: string;
284
+ readonly capId: string;
285
+ readonly capType: string;
286
+ readonly vaultPackageId: string;
287
+ }
288
+
289
+ /** Derive the canonical Vault ID without an RPC lookup. */
290
+ export function deriveVaultId(params: VaultIdParams): string {
291
+ const keyType = normalizeStructTag(
292
+ `${params.vaultPackageId}::vault::VaultKey<${params.capType}>`,
293
+ );
294
+ return deriveObjectID(
295
+ params.vaultRegistryId,
296
+ keyType,
297
+ vault.VaultKey.serialize([params.capId]).toBytes(),
298
+ );
299
+ }
300
+
301
+ /** Derive the canonical VaultAdminCap ID from its Vault ID. */
302
+ export function deriveVaultAdminCapId(
303
+ vaultId: string,
304
+ vaultPackageId: string,
305
+ ): string {
306
+ return deriveObjectID(
307
+ vaultId,
308
+ `${vaultPackageId}::vault::VaultAdminCapKey`,
309
+ vault.VaultAdminCapKey.serialize([false]).toBytes(),
310
+ );
234
311
  }
235
312
 
236
313
  export interface CompositionRoyaltyPoolPluginParams {