@0xobelisk/sui-client 1.2.0-pre.98 → 2.0.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.
package/dist/index.mjs CHANGED
@@ -802,19 +802,26 @@ var SuiInteractor = class {
802
802
  };
803
803
 
804
804
  // src/libs/suiInteractor/defaultConfig.ts
805
- var getDefaultURL = (networkType = "testnet") => {
805
+ var TESTNET_DUBHE_FRAMEWORK_PACKAGE_ID = "0xae33be6675639d6f1a5c468ae6bbc457ae4e22e57cd7526741d6143ce219d995";
806
+ var TESTNET_DUBHE_HUB_OBJECT_ID = "0x80391929a8772aba091156fd1f099bf79240d2cf4471a14026b4aa68e3a240cc";
807
+ var MAINNET_DUBHE_FRAMEWORK_PACKAGE_ID = "0x5bc40de124588b1e3514ca50f5e7109869351c4d9544e5f0cb9d30dd47bf8de7";
808
+ var MAINNET_DUBHE_HUB_OBJECT_ID = "0x96e92d9baccfd0a2927b4f209756e1edbc18904bff34e486e4bb4a151320e1e5";
809
+ var getDefaultConfig = (networkType = "testnet") => {
806
810
  switch (networkType) {
807
- case "localnet":
811
+ case "localnet": {
812
+ const localRpc = encodeURIComponent("http://127.0.0.1:9000");
808
813
  return {
809
814
  fullNode: "http://127.0.0.1:9000",
810
815
  graphql: "http://127.0.0.1:9125",
811
816
  network: "localnet",
812
- txExplorer: "https://explorer.polymedia.app/txblock/:txHash?network=local",
813
- accountExplorer: "https://explorer.polymedia.app/address/:address?network=local",
814
- explorer: "https://explorer.polymedia.app?network=local",
817
+ txExplorer: `https://custom.suiscan.xyz/custom/tx/:txHash?network=${localRpc}`,
818
+ accountExplorer: `https://custom.suiscan.xyz/custom/account/:address?network=${localRpc}`,
819
+ explorer: `https://custom.suiscan.xyz/custom?network=${localRpc}`,
815
820
  indexerUrl: "http://127.0.0.1:3001",
816
821
  channelUrl: "http://127.0.0.1:8080"
822
+ // frameworkPackageId: undefined — set after deploying dubhe locally
817
823
  };
824
+ }
818
825
  case "devnet":
819
826
  return {
820
827
  fullNode: "https://fullnode.devnet.sui.io:443",
@@ -824,6 +831,7 @@ var getDefaultURL = (networkType = "testnet") => {
824
831
  explorer: "https://suiscan.xyz/devnet",
825
832
  indexerUrl: "http://127.0.0.1:3001",
826
833
  channelUrl: "http://127.0.0.1:8080"
834
+ // frameworkPackageId: undefined — no persistent deployment on devnet
827
835
  };
828
836
  case "testnet":
829
837
  return {
@@ -834,7 +842,9 @@ var getDefaultURL = (networkType = "testnet") => {
834
842
  accountExplorer: "https://suiscan.xyz/testnet/address/:address",
835
843
  explorer: "https://suiscan.xyz/testnet",
836
844
  indexerUrl: "http://127.0.0.1:3001",
837
- channelUrl: "http://127.0.0.1:8080"
845
+ channelUrl: "http://127.0.0.1:8080",
846
+ frameworkPackageId: TESTNET_DUBHE_FRAMEWORK_PACKAGE_ID,
847
+ dappHubId: TESTNET_DUBHE_HUB_OBJECT_ID
838
848
  };
839
849
  case "mainnet":
840
850
  return {
@@ -845,7 +855,9 @@ var getDefaultURL = (networkType = "testnet") => {
845
855
  accountExplorer: "https://suiscan.xyz/mainnet/address/:address",
846
856
  explorer: "https://suiscan.xyz/mainnet",
847
857
  indexerUrl: "http://127.0.0.1:3001",
848
- channelUrl: "http://127.0.0.1:8080"
858
+ channelUrl: "http://127.0.0.1:8080",
859
+ frameworkPackageId: MAINNET_DUBHE_FRAMEWORK_PACKAGE_ID || void 0,
860
+ dappHubId: MAINNET_DUBHE_HUB_OBJECT_ID || void 0
849
861
  };
850
862
  default:
851
863
  return {
@@ -856,10 +868,13 @@ var getDefaultURL = (networkType = "testnet") => {
856
868
  accountExplorer: "https://suiscan.xyz/testnet/address/:address",
857
869
  explorer: "https://suiscan.xyz/testnet",
858
870
  indexerUrl: "http://127.0.0.1:3001",
859
- channelUrl: "http://127.0.0.1:8080"
871
+ channelUrl: "http://127.0.0.1:8080",
872
+ frameworkPackageId: TESTNET_DUBHE_FRAMEWORK_PACKAGE_ID,
873
+ dappHubId: TESTNET_DUBHE_HUB_OBJECT_ID
860
874
  };
861
875
  }
862
876
  };
877
+ var getDefaultURL = getDefaultConfig;
863
878
 
864
879
  // src/libs/suiContractFactory/index.ts
865
880
  var SuiContractFactory = class {
@@ -1047,6 +1062,10 @@ function normalizePackageId(input) {
1047
1062
  const normalized = withoutPrefix.replace(/^0+/, "");
1048
1063
  return "0x" + normalized;
1049
1064
  }
1065
+ function normalizeDappKey(packageId) {
1066
+ const raw = packageId.replace(/^0x/, "");
1067
+ return `${raw.padStart(64, "0")}::dapp_key::DappKey`;
1068
+ }
1050
1069
 
1051
1070
  // src/errors/index.ts
1052
1071
  var ContractDataParsingError = class extends Error {
@@ -1345,6 +1364,7 @@ function createTx(meta, fn) {
1345
1364
  }
1346
1365
  );
1347
1366
  }
1367
+ var SUI_CLOCK_OBJECT_ID = "0x0000000000000000000000000000000000000000000000000000000000000006";
1348
1368
  var _query, _tx, _object, _exec, _read, _getVectorDepth, _bcs, _bcsenum, _processKeyParameter, processKeyParameter_fn;
1349
1369
  var Dubhe = class {
1350
1370
  /**
@@ -1360,6 +1380,7 @@ var Dubhe = class {
1360
1380
  * @param packageId
1361
1381
  * @param metadata
1362
1382
  * @param channelUrl, the base URL for Dubhe Channel API, optional
1383
+ * @param frameworkPackageId, the published package ID of the dubhe framework, required for proxy operations
1363
1384
  */
1364
1385
  constructor({
1365
1386
  mnemonics,
@@ -1367,8 +1388,12 @@ var Dubhe = class {
1367
1388
  networkType,
1368
1389
  fullnodeUrls,
1369
1390
  packageId,
1391
+ dappKey: dappKeyParam,
1370
1392
  metadata,
1371
- channelUrl
1393
+ channelUrl,
1394
+ frameworkPackageId,
1395
+ dappStorageId,
1396
+ dappHubId
1372
1397
  } = {}) {
1373
1398
  // async getTransactions({
1374
1399
  // first,
@@ -2069,7 +2094,7 @@ var Dubhe = class {
2069
2094
  };
2070
2095
  });
2071
2096
  networkType = networkType ?? "mainnet";
2072
- const defaultParams = getDefaultURL(networkType);
2097
+ const defaultParams = getDefaultConfig(networkType);
2073
2098
  this.accountManager = new SuiAccountManager({ mnemonics, secretKey });
2074
2099
  fullnodeUrls = fullnodeUrls || [defaultParams.fullNode];
2075
2100
  this.suiInteractor = new SuiInteractor(fullnodeUrls, networkType);
@@ -2077,6 +2102,11 @@ var Dubhe = class {
2077
2102
  baseUrl: channelUrl || defaultParams.channelUrl
2078
2103
  });
2079
2104
  this.packageId = packageId ? normalizePackageId(packageId) : void 0;
2105
+ this.dappKey = dappKeyParam;
2106
+ const networkDefault = defaultParams.frameworkPackageId;
2107
+ this.frameworkPackageId = frameworkPackageId ? normalizePackageId(frameworkPackageId) : networkDefault ? normalizePackageId(networkDefault) : void 0;
2108
+ this.dappStorageId = dappStorageId;
2109
+ this.dappHubId = dappHubId;
2080
2110
  if (metadata !== void 0) {
2081
2111
  this.metadata = metadata;
2082
2112
  const maxLoopNum = 5;
@@ -2367,6 +2397,27 @@ var Dubhe = class {
2367
2397
  getPackageId() {
2368
2398
  return this.contractFactory.packageId;
2369
2399
  }
2400
+ /**
2401
+ * Return the fully-qualified Move type string for this DApp's `DappKey`.
2402
+ *
2403
+ * The format matches what `std::type_name::get<DappKey>()` returns on-chain:
2404
+ * `<64-char-zero-padded-address>::dapp_key::DappKey`
2405
+ *
2406
+ * Use this whenever you need to pass a type argument or build a `target`
2407
+ * string that references the DApp's DappKey type.
2408
+ *
2409
+ * @example
2410
+ * ```typescript
2411
+ * tx.moveCall({
2412
+ * target: `${frameworkPackageId}::dapp_system::settle_writes`,
2413
+ * typeArguments: [contract.getDappKey()],
2414
+ * arguments: [...],
2415
+ * });
2416
+ * ```
2417
+ */
2418
+ getDappKey() {
2419
+ return this.dappKey;
2420
+ }
2370
2421
  getMetadata() {
2371
2422
  return this.contractFactory.metadata;
2372
2423
  }
@@ -2374,7 +2425,7 @@ var Dubhe = class {
2374
2425
  return this.suiInteractor.network;
2375
2426
  }
2376
2427
  getNetworkConfig() {
2377
- return getDefaultURL(this.getNetwork());
2428
+ return getDefaultConfig(this.getNetwork());
2378
2429
  }
2379
2430
  getTxExplorerUrl(txHash) {
2380
2431
  return this.getNetworkConfig().txExplorer.replace(":txHash", txHash);
@@ -2400,7 +2451,7 @@ var Dubhe = class {
2400
2451
  const fullnodeUrlsChanged = config.fullnodeUrls !== void 0;
2401
2452
  if (networkChanged || fullnodeUrlsChanged) {
2402
2453
  const newNetworkType = config.networkType ?? this.suiInteractor.network;
2403
- const defaultParams = getDefaultURL(newNetworkType);
2454
+ const defaultParams = getDefaultConfig(newNetworkType);
2404
2455
  const newFullnodeUrls = config.fullnodeUrls || [defaultParams.fullNode];
2405
2456
  this.suiInteractor = new SuiInteractor(newFullnodeUrls, newNetworkType);
2406
2457
  }
@@ -2411,6 +2462,9 @@ var Dubhe = class {
2411
2462
  }
2412
2463
  const packageIdChanged = config.packageId !== void 0 && config.packageId !== this.packageId;
2413
2464
  const metadataChanged = config.metadata !== void 0 && config.metadata !== this.metadata;
2465
+ if (config.dappKey !== void 0) {
2466
+ this.dappKey = config.dappKey;
2467
+ }
2414
2468
  if (packageIdChanged || metadataChanged) {
2415
2469
  if (config.packageId !== void 0) {
2416
2470
  this.packageId = normalizePackageId(config.packageId);
@@ -2602,11 +2656,8 @@ var Dubhe = class {
2602
2656
  if (account && account.startsWith("0x")) {
2603
2657
  account = account.slice(2);
2604
2658
  }
2605
- if (packageId && packageId.startsWith("0x")) {
2606
- packageId = packageId.slice(2);
2607
- }
2608
2659
  const payload = {
2609
- dapp_key: `${packageId}::dapp_key::DappKey`,
2660
+ dapp_key: normalizeDappKey(packageId ?? ""),
2610
2661
  account,
2611
2662
  table,
2612
2663
  key
@@ -2645,22 +2696,16 @@ var Dubhe = class {
2645
2696
  * );
2646
2697
  */
2647
2698
  async subscribeChannelTable({
2648
- packageId,
2699
+ packageId: _packageId,
2649
2700
  account,
2650
2701
  table,
2651
2702
  key
2652
2703
  } = {}, options) {
2653
- packageId = packageId || this.packageId;
2654
- if (account !== void 0) {
2655
- if (account.startsWith("0x")) {
2656
- account = account.slice(2);
2657
- }
2658
- }
2659
- if (packageId && packageId.startsWith("0x")) {
2660
- packageId = packageId.slice(2);
2704
+ if (account !== void 0 && account.startsWith("0x")) {
2705
+ account = account.slice(2);
2661
2706
  }
2662
2707
  const payload = {
2663
- dapp_key: `${packageId}::dapp_key::DappKey`
2708
+ dapp_key: this.dappKey
2664
2709
  };
2665
2710
  if (account !== void 0) {
2666
2711
  payload.account = account;
@@ -2837,6 +2882,761 @@ var Dubhe = class {
2837
2882
  async entity_key_from_u256(x) {
2838
2883
  return numberToAddressHex(x);
2839
2884
  }
2885
+ // ─── UserStorage helpers ────────────────────────────────────────────────────
2886
+ /**
2887
+ * Call the DApp's generated `user_storage_init::init_user_storage` entry function,
2888
+ * which creates and shares a `UserStorage` object for the signer.
2889
+ *
2890
+ * Each DApp exposes this entry point under its own package ID.
2891
+ * On Sui, the `UserStorage` object is shared immediately so all transactions
2892
+ * can reference it without knowing the object ID in advance.
2893
+ *
2894
+ * @param dappHubId Object ID of the DappHub shared object
2895
+ * @param dappStorageId Object ID of the DApp's DappStorage shared object
2896
+ */
2897
+ async initUserStorage({
2898
+ dappHubId,
2899
+ dappStorageId: dappStorageIdParam,
2900
+ derivePathParams,
2901
+ onSuccess,
2902
+ onError
2903
+ }) {
2904
+ const storageId = dappStorageIdParam ?? this.dappStorageId;
2905
+ if (!storageId) {
2906
+ throw new Error(
2907
+ "dappStorageId is required for initUserStorage. Pass it directly or set it in the Dubhe constructor."
2908
+ );
2909
+ }
2910
+ const pkg = this.packageId;
2911
+ if (!pkg)
2912
+ throw new Error("packageId is required for initUserStorage.");
2913
+ const tx = new Transaction2();
2914
+ tx.moveCall({
2915
+ target: `${pkg}::user_storage_init::init_user_storage`,
2916
+ arguments: [tx.object(dappHubId), tx.object(storageId)]
2917
+ });
2918
+ return this.signAndSendTxn({
2919
+ tx,
2920
+ derivePathParams,
2921
+ onSuccess,
2922
+ onError
2923
+ });
2924
+ }
2925
+ /**
2926
+ * Activate a session key for the signer's `UserStorage`.
2927
+ *
2928
+ * The signer must be the `canonical_owner` of the `UserStorage` object.
2929
+ * Calls `<frameworkPackageId>::dapp_system::activate_session<DappKey>`.
2930
+ *
2931
+ * @param userStorageId Object ID of the UserStorage to activate a session for
2932
+ * @param sessionWallet Sui address of the session wallet
2933
+ * @param durationMs Session duration in milliseconds ([60_000, 604_800_000])
2934
+ * @param clockObjectId Object ID of the Sui Clock (default: 0x6)
2935
+ */
2936
+ async activateSession({
2937
+ userStorageId,
2938
+ sessionWallet,
2939
+ durationMs,
2940
+ dappHubId: dappHubIdParam,
2941
+ clockObjectId,
2942
+ derivePathParams,
2943
+ onSuccess,
2944
+ onError
2945
+ }) {
2946
+ const fwPkg = this.frameworkPackageId;
2947
+ if (!fwPkg) {
2948
+ throw new Error(
2949
+ 'frameworkPackageId is required for activateSession. Set it in the Dubhe constructor ({ frameworkPackageId: "0x..." }).'
2950
+ );
2951
+ }
2952
+ const typeArg = this.dappKey;
2953
+ if (!typeArg) {
2954
+ throw new Error(
2955
+ 'dappKey is required for activateSession. Set it in the Dubhe constructor ({ packageId: "0x..." }).'
2956
+ );
2957
+ }
2958
+ const hubId = dappHubIdParam ?? this.dappHubId;
2959
+ if (!hubId) {
2960
+ throw new Error(
2961
+ 'dappHubId is required for activateSession. Pass it directly or set it in the Dubhe constructor ({ dappHubId: "0x..." }).'
2962
+ );
2963
+ }
2964
+ const clockId = clockObjectId ?? SUI_CLOCK_OBJECT_ID;
2965
+ const tx = new Transaction2();
2966
+ tx.moveCall({
2967
+ target: `${fwPkg}::dapp_system::activate_session`,
2968
+ typeArguments: [typeArg],
2969
+ arguments: [
2970
+ tx.object(hubId),
2971
+ tx.object(userStorageId),
2972
+ tx.pure.address(sessionWallet),
2973
+ tx.pure.u64(durationMs),
2974
+ tx.object(clockId)
2975
+ ]
2976
+ });
2977
+ return this.signAndSendTxn({
2978
+ tx,
2979
+ derivePathParams,
2980
+ onSuccess,
2981
+ onError
2982
+ });
2983
+ }
2984
+ /**
2985
+ * Deactivate the current session key on a `UserStorage`.
2986
+ *
2987
+ * Allowed callers:
2988
+ * - The `canonical_owner` (revoke at any time).
2989
+ * - The session key itself (voluntary sign-out).
2990
+ * - Anyone, once the session has expired.
2991
+ *
2992
+ * Calls `<frameworkPackageId>::dapp_system::deactivate_session<DappKey>`.
2993
+ *
2994
+ * @param userStorageId Object ID of the UserStorage to deactivate the session on
2995
+ */
2996
+ async deactivateSession({
2997
+ userStorageId,
2998
+ dappHubId: dappHubIdParam,
2999
+ derivePathParams,
3000
+ onSuccess,
3001
+ onError
3002
+ }) {
3003
+ const fwPkg = this.frameworkPackageId;
3004
+ if (!fwPkg) {
3005
+ throw new Error(
3006
+ 'frameworkPackageId is required for deactivateSession. Set it in the Dubhe constructor ({ frameworkPackageId: "0x..." }).'
3007
+ );
3008
+ }
3009
+ const typeArg = this.dappKey;
3010
+ if (!typeArg) {
3011
+ throw new Error(
3012
+ 'dappKey is required for deactivateSession. Set it in the Dubhe constructor ({ packageId: "0x..." }).'
3013
+ );
3014
+ }
3015
+ const hubId = dappHubIdParam ?? this.dappHubId;
3016
+ if (!hubId) {
3017
+ throw new Error(
3018
+ 'dappHubId is required for deactivateSession. Pass it directly or set it in the Dubhe constructor ({ dappHubId: "0x..." }).'
3019
+ );
3020
+ }
3021
+ const tx = new Transaction2();
3022
+ tx.moveCall({
3023
+ target: `${fwPkg}::dapp_system::deactivate_session`,
3024
+ typeArguments: [typeArg],
3025
+ arguments: [tx.object(hubId), tx.object(userStorageId)]
3026
+ });
3027
+ return this.signAndSendTxn({
3028
+ tx,
3029
+ derivePathParams,
3030
+ onSuccess,
3031
+ onError
3032
+ });
3033
+ }
3034
+ /**
3035
+ * Look up the `UserStorage` object ID for the given address in this DApp.
3036
+ *
3037
+ * Scans the transaction history for `user_storage_init::init_user_storage`
3038
+ * calls from the given address and extracts the created `UserStorage` object
3039
+ * ID from the transaction's object changes.
3040
+ *
3041
+ * Returns the object ID on success, or `null` when:
3042
+ * - `packageId` or `frameworkPackageId` is not set.
3043
+ * - The user has not yet called `initUserStorage`.
3044
+ *
3045
+ * @param userAddress Sui address to look up (with or without 0x prefix).
3046
+ */
3047
+ async getUserStorageId(userAddress) {
3048
+ const pkg = this.packageId;
3049
+ const fwPkg = this.frameworkPackageId;
3050
+ if (!pkg || !fwPkg)
3051
+ return null;
3052
+ const normalizedUser = userAddress.startsWith("0x") ? userAddress : `0x${userAddress}`;
3053
+ const expectedType = `${fwPkg}::dapp_service::UserStorage`;
3054
+ let cursor = null;
3055
+ let hasNextPage = true;
3056
+ while (hasNextPage) {
3057
+ const result = await this.suiInteractor.currentClient.queryTransactionBlocks({
3058
+ filter: {
3059
+ MoveFunction: {
3060
+ package: pkg,
3061
+ module: "user_storage_init",
3062
+ function: "init_user_storage"
3063
+ }
3064
+ },
3065
+ options: { showObjectChanges: true },
3066
+ ...cursor !== null && cursor !== void 0 ? { cursor } : {},
3067
+ limit: 50
3068
+ });
3069
+ for (const tx of result.data) {
3070
+ if (!tx.objectChanges)
3071
+ continue;
3072
+ const created = tx.objectChanges.find(
3073
+ (c) => c.type === "created" && typeof c.objectType === "string" && c.objectType === expectedType && c.sender === normalizedUser
3074
+ );
3075
+ if (created?.objectId)
3076
+ return created.objectId;
3077
+ }
3078
+ hasNextPage = result.hasNextPage;
3079
+ cursor = result.nextCursor ?? null;
3080
+ }
3081
+ return null;
3082
+ }
3083
+ /**
3084
+ * Fetch and return the on-chain fields of a `UserStorage` shared object.
3085
+ *
3086
+ * The returned object mirrors Move's `UserStorage` struct plus two
3087
+ * convenience fields computed from the raw counters:
3088
+ * - `unsettled_count` = write_count − settled_count
3089
+ * - `unsettled_bytes` = write_bytes − settled_bytes
3090
+ *
3091
+ * @param userStorageId Object ID of the UserStorage to inspect.
3092
+ */
3093
+ async getUserStorageFields(userStorageId) {
3094
+ const obj = await this.suiInteractor.getObject(userStorageId);
3095
+ const fields = obj?.content?.fields ?? {};
3096
+ const writeCount = BigInt(fields.write_count ?? 0);
3097
+ const settledCount = BigInt(fields.settled_count ?? 0);
3098
+ const writeBytes = BigInt(fields.write_bytes ?? 0);
3099
+ const settledBytes = BigInt(fields.settled_bytes ?? 0);
3100
+ return {
3101
+ objectId: userStorageId,
3102
+ dapp_key: fields.dapp_key ?? "",
3103
+ canonical_owner: fields.canonical_owner ?? "",
3104
+ session_key: fields.session_key ?? "",
3105
+ session_expires_at: BigInt(fields.session_expires_at ?? 0),
3106
+ write_count: writeCount,
3107
+ settled_count: settledCount,
3108
+ write_bytes: writeBytes,
3109
+ settled_bytes: settledBytes,
3110
+ unsettled_count: writeCount - settledCount,
3111
+ unsettled_bytes: writeBytes - settledBytes
3112
+ };
3113
+ }
3114
+ /**
3115
+ * Fetch and return the on-chain fields of a `DappStorage` shared object.
3116
+ *
3117
+ * Covers the full metadata + fee/credit subset of the Move struct.
3118
+ *
3119
+ * @param dappStorageId Object ID of the DappStorage to inspect.
3120
+ */
3121
+ /**
3122
+ * Read the framework-level fields from the DappHub shared object.
3123
+ *
3124
+ * Key fields:
3125
+ * - `treasury` — address that directly receives framework fee income
3126
+ * - `pending_treasury` — queued treasury address during two-step rotation
3127
+ * - `framework_admin` — address that can update framework configuration
3128
+ * - `version` — current framework version
3129
+ */
3130
+ async getDappHubFields(dappHubId) {
3131
+ const obj = await this.suiInteractor.getObject(dappHubId);
3132
+ const fields = obj?.content?.fields ?? {};
3133
+ const feeConfig = fields.fee_config?.fields ?? fields.fee_config ?? {};
3134
+ const adminConfig = fields.admin_config?.fields ?? fields.admin_config ?? {};
3135
+ return {
3136
+ objectId: dappHubId,
3137
+ version: Number(fields.version ?? 0),
3138
+ framework_admin: adminConfig.admin ?? fields.framework_admin ?? "",
3139
+ treasury: feeConfig.treasury ?? fields.treasury ?? "",
3140
+ pending_treasury: feeConfig.pending_treasury ?? fields.pending_treasury ?? ""
3141
+ };
3142
+ }
3143
+ async getDappStorageFields(dappStorageId) {
3144
+ const obj = await this.suiInteractor.getObject(dappStorageId);
3145
+ const fields = obj?.content?.fields ?? {};
3146
+ return {
3147
+ objectId: dappStorageId,
3148
+ dapp_key: fields.dapp_key ?? "",
3149
+ name: fields.name ?? "",
3150
+ description: fields.description ?? "",
3151
+ website_url: fields.website_url ?? "",
3152
+ admin: fields.admin ?? "",
3153
+ version: Number(fields.version ?? 0),
3154
+ paused: Boolean(fields.paused),
3155
+ free_credit: BigInt(fields.free_credit ?? 0),
3156
+ free_credit_expires_at: BigInt(fields.free_credit_expires_at ?? 0),
3157
+ credit_pool: BigInt(fields.credit_pool ?? 0),
3158
+ min_credit_to_unsuspend: BigInt(fields.min_credit_to_unsuspend ?? 0),
3159
+ suspended: Boolean(fields.suspended),
3160
+ total_settled: BigInt(fields.total_settled ?? 0),
3161
+ base_fee_per_write: BigInt(fields.base_fee_per_write ?? 0),
3162
+ bytes_fee_per_byte: BigInt(fields.bytes_fee_per_byte ?? 0),
3163
+ settlement_mode: Number(fields.settlement_mode ?? 0),
3164
+ write_fee_dapp_share_bps: Number(fields.write_fee_dapp_share_bps ?? 0)
3165
+ };
3166
+ }
3167
+ /**
3168
+ * Append a `dapp_system::settle_writes` moveCall to an existing PTB.
3169
+ *
3170
+ * Does NOT send the transaction — call this before your own moveCall(s) to
3171
+ * settle any accumulated write debt within the same PTB at no extra round-trip.
3172
+ *
3173
+ * The framework function never aborts due to insufficient credit (it silently
3174
+ * skips or partially settles), so prepending this to any PTB is always safe.
3175
+ *
3176
+ * @param tx The Transaction object to append the moveCall to.
3177
+ * @param dappHubId Object ID of the DappHub shared object.
3178
+ * @param dappStorageId Object ID of the DApp's DappStorage (falls back to constructor value).
3179
+ * @param userStorageId Object ID of the user's UserStorage shared object.
3180
+ */
3181
+ buildSettleWritesTx(tx, {
3182
+ dappHubId,
3183
+ dappStorageId: dappStorageIdParam,
3184
+ userStorageId
3185
+ }) {
3186
+ const fwPkg = this.frameworkPackageId;
3187
+ if (!fwPkg) {
3188
+ throw new Error(
3189
+ 'frameworkPackageId is required for buildSettleWritesTx. Set it in the Dubhe constructor ({ frameworkPackageId: "0x..." }).'
3190
+ );
3191
+ }
3192
+ const storageId = dappStorageIdParam ?? this.dappStorageId;
3193
+ if (!storageId) {
3194
+ throw new Error(
3195
+ "dappStorageId is required for buildSettleWritesTx. Pass it directly or set it in the Dubhe constructor."
3196
+ );
3197
+ }
3198
+ const typeArg = this.dappKey;
3199
+ if (!typeArg) {
3200
+ throw new Error(
3201
+ 'dappKey is required for buildSettleWritesTx. Set it in the Dubhe constructor ({ packageId: "0x..." }).'
3202
+ );
3203
+ }
3204
+ tx.moveCall({
3205
+ target: `${fwPkg}::dapp_system::settle_writes`,
3206
+ typeArguments: [typeArg],
3207
+ arguments: [tx.object(dappHubId), tx.object(storageId), tx.object(userStorageId)]
3208
+ });
3209
+ }
3210
+ /**
3211
+ * Settle accumulated write debt for a user (standalone transaction).
3212
+ *
3213
+ * Builds a PTB containing a single `dapp_system::settle_writes` call and
3214
+ * sends it. For embedding settlement into an existing PTB, use
3215
+ * `buildSettleWritesTx` instead.
3216
+ *
3217
+ * This is safe to call at any time — the framework function never aborts
3218
+ * due to insufficient credit; it silently skips or partially settles.
3219
+ *
3220
+ * @param dappHubId Object ID of the DappHub shared object.
3221
+ * @param dappStorageId Object ID of the DApp's DappStorage shared object.
3222
+ * @param userStorageId Object ID of the user's UserStorage shared object.
3223
+ */
3224
+ async settleWrites({
3225
+ dappHubId,
3226
+ dappStorageId,
3227
+ userStorageId,
3228
+ derivePathParams,
3229
+ onSuccess,
3230
+ onError
3231
+ }) {
3232
+ const tx = new Transaction2();
3233
+ this.buildSettleWritesTx(tx, { dappHubId, dappStorageId, userStorageId });
3234
+ return this.signAndSendTxn({
3235
+ tx,
3236
+ derivePathParams,
3237
+ onSuccess,
3238
+ onError
3239
+ });
3240
+ }
3241
+ /**
3242
+ * Recharge a DApp's credit pool by paying with the framework's accepted coin type.
3243
+ *
3244
+ * Wraps `dapp_system::recharge_credit<DappKey, CoinType>`. Any account may call
3245
+ * this — no admin restriction. Payment is forwarded to the framework treasury and
3246
+ * the DApp's `credit_pool` is increased by the coin value.
3247
+ *
3248
+ * Only valid in DAPP_SUBSIDIZES settlement mode. Aborts with
3249
+ * `wrong_settlement_mode` if the DApp is in USER_PAYS mode.
3250
+ *
3251
+ * @param dappHubId Object ID of the DappHub shared object.
3252
+ * @param dappStorageId Object ID of the DApp's DappStorage shared object.
3253
+ * @param coinObjectId Object ID of the Coin to use as payment.
3254
+ * @param coinType Move type of the coin (default: "0x2::sui::SUI").
3255
+ */
3256
+ async rechargeCredit({
3257
+ dappHubId,
3258
+ dappStorageId: dappStorageIdParam,
3259
+ coinObjectId,
3260
+ coinType = "0x2::sui::SUI",
3261
+ derivePathParams,
3262
+ onSuccess,
3263
+ onError
3264
+ }) {
3265
+ const fwPkg = this.frameworkPackageId;
3266
+ if (!fwPkg) {
3267
+ throw new Error(
3268
+ 'frameworkPackageId is required for rechargeCredit. Set it in the Dubhe constructor ({ frameworkPackageId: "0x..." }).'
3269
+ );
3270
+ }
3271
+ const storageId = dappStorageIdParam ?? this.dappStorageId;
3272
+ if (!storageId) {
3273
+ throw new Error(
3274
+ "dappStorageId is required for rechargeCredit. Pass it directly or set it in the Dubhe constructor."
3275
+ );
3276
+ }
3277
+ const typeArg = this.dappKey;
3278
+ if (!typeArg) {
3279
+ throw new Error(
3280
+ 'dappKey is required for rechargeCredit. Set it in the Dubhe constructor ({ packageId: "0x..." }).'
3281
+ );
3282
+ }
3283
+ const tx = new Transaction2();
3284
+ tx.moveCall({
3285
+ target: `${fwPkg}::dapp_system::recharge_credit`,
3286
+ typeArguments: [typeArg, coinType],
3287
+ arguments: [tx.object(dappHubId), tx.object(storageId), tx.object(coinObjectId)]
3288
+ });
3289
+ return this.signAndSendTxn({
3290
+ tx,
3291
+ derivePathParams,
3292
+ onSuccess,
3293
+ onError
3294
+ });
3295
+ }
3296
+ /**
3297
+ * Append a `dapp_system::settle_writes_user_pays` moveCall to an existing PTB.
3298
+ *
3299
+ * Splits `maxPaymentMist` MIST from `tx.gas` as the payment coin, then calls
3300
+ * `settle_writes_user_pays`. The framework returns any overpayment automatically,
3301
+ * so it is safe to over-estimate. This lets a session key pay for settlement
3302
+ * inline without requiring a separate owned Coin object.
3303
+ *
3304
+ * @param tx The Transaction object to append the moveCall to.
3305
+ * @param dappHubId Object ID of the DappHub shared object.
3306
+ * @param dappStorageId Object ID of the DApp's DappStorage (falls back to constructor value).
3307
+ * @param userStorageId Object ID of the user's UserStorage shared object.
3308
+ * @param maxPaymentMist Upper bound for the payment in MIST (default: 50_000_000 = 0.05 SUI).
3309
+ * @param coinType Move type of the payment coin (default: "0x2::sui::SUI").
3310
+ */
3311
+ buildSettleWritesUserPaysTx(tx, {
3312
+ dappHubId,
3313
+ dappStorageId: dappStorageIdParam,
3314
+ userStorageId,
3315
+ maxPaymentMist = 50000000n,
3316
+ coinType = "0x2::sui::SUI"
3317
+ }) {
3318
+ const fwPkg = this.frameworkPackageId;
3319
+ if (!fwPkg) {
3320
+ throw new Error(
3321
+ 'frameworkPackageId is required for buildSettleWritesUserPaysTx. Set it in the Dubhe constructor ({ frameworkPackageId: "0x..." }).'
3322
+ );
3323
+ }
3324
+ const storageId = dappStorageIdParam ?? this.dappStorageId;
3325
+ if (!storageId) {
3326
+ throw new Error(
3327
+ "dappStorageId is required for buildSettleWritesUserPaysTx. Pass it directly or set it in the Dubhe constructor."
3328
+ );
3329
+ }
3330
+ const typeArg = this.dappKey;
3331
+ if (!typeArg) {
3332
+ throw new Error(
3333
+ 'dappKey is required for buildSettleWritesUserPaysTx. Set it in the Dubhe constructor ({ packageId: "0x..." }).'
3334
+ );
3335
+ }
3336
+ const [paymentCoin] = tx.splitCoins(tx.gas, [tx.pure.u64(maxPaymentMist)]);
3337
+ const changeCoin = tx.moveCall({
3338
+ target: `${fwPkg}::dapp_system::settle_writes_user_pays`,
3339
+ typeArguments: [typeArg, coinType],
3340
+ arguments: [tx.object(dappHubId), tx.object(storageId), tx.object(userStorageId), paymentCoin]
3341
+ });
3342
+ tx.mergeCoins(tx.gas, [changeCoin]);
3343
+ }
3344
+ /**
3345
+ * Settle accumulated write debt where the user pays directly (USER_PAYS mode).
3346
+ *
3347
+ * Wraps `dapp_system::settle_writes_user_pays<DappKey, CoinType>`. The exact
3348
+ * cost is computed on-chain; any overpayment from the coin is returned to the
3349
+ * sender after settlement.
3350
+ *
3351
+ * Aborts if:
3352
+ * - The DApp is not in USER_PAYS mode (`wrong_settlement_mode`)
3353
+ * - The coin type does not match the accepted type (`wrong_payment_coin_type`)
3354
+ * - The coin value is less than the total cost (`insufficient_credit`)
3355
+ *
3356
+ * @param dappHubId Object ID of the DappHub shared object.
3357
+ * @param dappStorageId Object ID of the DApp's DappStorage shared object.
3358
+ * @param userStorageId Object ID of the user's UserStorage shared object.
3359
+ * @param coinObjectId Object ID of the Coin to use as payment.
3360
+ * @param coinType Move type of the coin (default: "0x2::sui::SUI").
3361
+ */
3362
+ async settleWritesUserPays({
3363
+ dappHubId,
3364
+ dappStorageId: dappStorageIdParam,
3365
+ userStorageId,
3366
+ coinObjectId,
3367
+ coinType = "0x2::sui::SUI",
3368
+ derivePathParams,
3369
+ onSuccess,
3370
+ onError
3371
+ }) {
3372
+ const fwPkg = this.frameworkPackageId;
3373
+ if (!fwPkg) {
3374
+ throw new Error(
3375
+ 'frameworkPackageId is required for settleWritesUserPays. Set it in the Dubhe constructor ({ frameworkPackageId: "0x..." }).'
3376
+ );
3377
+ }
3378
+ const storageId = dappStorageIdParam ?? this.dappStorageId;
3379
+ if (!storageId) {
3380
+ throw new Error(
3381
+ "dappStorageId is required for settleWritesUserPays. Pass it directly or set it in the Dubhe constructor."
3382
+ );
3383
+ }
3384
+ const typeArg = this.dappKey;
3385
+ if (!typeArg) {
3386
+ throw new Error(
3387
+ 'dappKey is required for settleWritesUserPays. Set it in the Dubhe constructor ({ packageId: "0x..." }).'
3388
+ );
3389
+ }
3390
+ const tx = new Transaction2();
3391
+ tx.moveCall({
3392
+ target: `${fwPkg}::dapp_system::settle_writes_user_pays`,
3393
+ typeArguments: [typeArg, coinType],
3394
+ arguments: [
3395
+ tx.object(dappHubId),
3396
+ tx.object(storageId),
3397
+ tx.object(userStorageId),
3398
+ tx.object(coinObjectId)
3399
+ ]
3400
+ });
3401
+ return this.signAndSendTxn({
3402
+ tx,
3403
+ derivePathParams,
3404
+ onSuccess,
3405
+ onError
3406
+ });
3407
+ }
3408
+ /**
3409
+ * Query `WritesSettled` events from the Sui full node.
3410
+ *
3411
+ * Returns the most recent settlement events across ALL users of this DApp.
3412
+ * Each event captures one user's write debt settlement: number of writes,
3413
+ * bytes, and the amount charged (`paid_cost`).
3414
+ *
3415
+ * Note: Sui full nodes may prune old events. For long-term history use a
3416
+ * dedicated indexer table (`writes_settled_history`).
3417
+ *
3418
+ * @param limit Max events to return (default: 30, max: 50).
3419
+ * @param cursor Pagination cursor from a previous response.
3420
+ */
3421
+ async queryWritesSettledEvents(limit = 30, cursor) {
3422
+ const fwPkg = this.frameworkPackageId;
3423
+ if (!fwPkg)
3424
+ return [];
3425
+ try {
3426
+ const resp = await this.suiInteractor.currentClient.queryEvents({
3427
+ query: { MoveEventType: `${fwPkg}::dubhe_events::WritesSettled` },
3428
+ cursor,
3429
+ limit: Math.min(limit, 50),
3430
+ order: "descending"
3431
+ });
3432
+ return (resp.data ?? []).map((ev) => {
3433
+ const p = ev.parsedJson ?? {};
3434
+ return {
3435
+ txDigest: ev.id?.txDigest ?? "",
3436
+ eventSeq: ev.id?.eventSeq ?? "",
3437
+ timestampMs: Number(ev.timestampMs ?? 0),
3438
+ dappKey: p.dapp_key ?? "",
3439
+ account: p.account ?? "",
3440
+ writes: Number(p.writes ?? 0),
3441
+ bytes: String(p.bytes ?? "0"),
3442
+ freeCost: String(p.free_cost ?? "0"),
3443
+ paidCost: String(p.paid_cost ?? "0")
3444
+ };
3445
+ });
3446
+ } catch {
3447
+ return [];
3448
+ }
3449
+ }
3450
+ /**
3451
+ * Query `MarketplaceFeeSettled` events from the Sui full node.
3452
+ *
3453
+ * Each event records the fee split for a completed marketplace purchase:
3454
+ * total fee, framework treasury share, and DApp revenue share.
3455
+ *
3456
+ * @param limit Max events to return (default: 30, max: 50).
3457
+ * @param cursor Pagination cursor from a previous response.
3458
+ */
3459
+ async queryMarketplaceFeeSettledEvents(limit = 30, cursor) {
3460
+ const fwPkg = this.frameworkPackageId;
3461
+ if (!fwPkg)
3462
+ return [];
3463
+ try {
3464
+ const resp = await this.suiInteractor.currentClient.queryEvents({
3465
+ query: { MoveEventType: `${fwPkg}::dubhe_events::MarketplaceFeeSettled` },
3466
+ cursor,
3467
+ limit: Math.min(limit, 50),
3468
+ order: "descending"
3469
+ });
3470
+ return (resp.data ?? []).map((ev) => {
3471
+ const p = ev.parsedJson ?? {};
3472
+ return {
3473
+ txDigest: ev.id?.txDigest ?? "",
3474
+ eventSeq: ev.id?.eventSeq ?? "",
3475
+ timestampMs: Number(ev.timestampMs ?? 0),
3476
+ dappKey: p.dapp_key ?? "",
3477
+ listingId: p.listing_id ?? "",
3478
+ coinType: p.coin_type ?? "",
3479
+ totalFee: String(p.total_fee ?? "0"),
3480
+ treasuryAmount: String(p.treasury_amount ?? "0"),
3481
+ dappAmount: String(p.dapp_amount ?? "0")
3482
+ };
3483
+ });
3484
+ } catch {
3485
+ return [];
3486
+ }
3487
+ }
3488
+ /**
3489
+ * Read the DApp's accumulated revenue balance directly from the chain.
3490
+ *
3491
+ * `dapp_revenue` is stored as a dynamic field (`DappRevenueKey<CoinType>`)
3492
+ * on the DappStorage object — it is NOT a top-level field, so `getObject`
3493
+ * alone cannot return it. This method calls `getDynamicFields` to locate
3494
+ * the correct child object and returns its balance.
3495
+ *
3496
+ * Returns 0n when the dynamic field has never been created (no revenue yet).
3497
+ *
3498
+ * @param dappStorageId Object ID of the DApp's DappStorage shared object.
3499
+ * @param coinType Move type of the revenue coin (default: "0x2::sui::SUI").
3500
+ */
3501
+ async getDappRevenueBalance(dappStorageId, coinType = "0x2::sui::SUI") {
3502
+ const client = this.suiInteractor.currentClient;
3503
+ const coinTail = coinType.split("::").slice(-2).join("::");
3504
+ try {
3505
+ let cursor = void 0;
3506
+ do {
3507
+ const page = await client.getDynamicFields({
3508
+ parentId: dappStorageId,
3509
+ cursor,
3510
+ limit: 50
3511
+ });
3512
+ for (const field of page.data) {
3513
+ const nameType = field.name?.type ?? "";
3514
+ if (nameType.includes("DappRevenueKey") && nameType.includes(coinTail)) {
3515
+ const fieldObj = await client.getDynamicFieldObject({
3516
+ parentId: dappStorageId,
3517
+ name: field.name
3518
+ });
3519
+ const innerFields = fieldObj?.data?.content?.fields ?? {};
3520
+ const balValue = innerFields?.value?.fields?.value ?? // nested Balance struct
3521
+ innerFields?.value ?? 0;
3522
+ return BigInt(balValue);
3523
+ }
3524
+ }
3525
+ cursor = page.nextCursor;
3526
+ if (!page.hasNextPage)
3527
+ break;
3528
+ } while (cursor);
3529
+ } catch {
3530
+ }
3531
+ return 0n;
3532
+ }
3533
+ /**
3534
+ * Withdraw all accumulated DApp revenue to the DApp admin address.
3535
+ *
3536
+ * Wraps `dapp_system::withdraw_dapp_revenue<DappKey, CoinType>`.
3537
+ * The entire revenue balance is transferred to the DApp admin on-chain.
3538
+ * Aborts if the balance is zero (`no_revenue_to_withdraw`).
3539
+ *
3540
+ * @param dappHubId Object ID of the DappHub shared object.
3541
+ * @param dappStorageId Object ID of the DApp's DappStorage shared object.
3542
+ * @param coinType Move type of the revenue coin (default: "0x2::sui::SUI").
3543
+ */
3544
+ async withdrawDappRevenue({
3545
+ dappHubId,
3546
+ dappStorageId: dappStorageIdParam,
3547
+ coinType = "0x2::sui::SUI",
3548
+ derivePathParams,
3549
+ onSuccess,
3550
+ onError
3551
+ }) {
3552
+ const fwPkg = this.frameworkPackageId;
3553
+ if (!fwPkg) {
3554
+ throw new Error(
3555
+ 'frameworkPackageId is required for withdrawDappRevenue. Set it in the Dubhe constructor ({ frameworkPackageId: "0x..." }).'
3556
+ );
3557
+ }
3558
+ const storageId = dappStorageIdParam ?? this.dappStorageId;
3559
+ if (!storageId) {
3560
+ throw new Error(
3561
+ "dappStorageId is required for withdrawDappRevenue. Pass it directly or set it in the Dubhe constructor."
3562
+ );
3563
+ }
3564
+ const typeArg = this.dappKey;
3565
+ if (!typeArg) {
3566
+ throw new Error(
3567
+ 'dappKey is required for withdrawDappRevenue. Set it in the Dubhe constructor ({ packageId: "0x..." }).'
3568
+ );
3569
+ }
3570
+ const tx = new Transaction2();
3571
+ tx.moveCall({
3572
+ target: `${fwPkg}::dapp_system::withdraw_dapp_revenue`,
3573
+ typeArguments: [typeArg, coinType],
3574
+ arguments: [tx.object(dappHubId), tx.object(storageId)]
3575
+ });
3576
+ return this.signAndSendTxn({
3577
+ tx,
3578
+ derivePathParams,
3579
+ onSuccess,
3580
+ onError
3581
+ });
3582
+ }
3583
+ /**
3584
+ * Return a combined settlement health snapshot for a user.
3585
+ *
3586
+ * Fetches `UserStorage` fields (always) and optionally `DappStorage` fields
3587
+ * (when `dappStorageId` is supplied) to produce high-level diagnostic values:
3588
+ *
3589
+ * - `utilizationPct` — how close the user is to the 2 000-write hard limit
3590
+ * - `isAtRisk` — utilization >= 80 % (settlement recommended soon)
3591
+ * - `isBlocked` — utilization >= 100 % (writes will be rejected)
3592
+ * - `estimatedWritesAffordable` — writes the credit pool can still cover
3593
+ * - `creditAtRisk` — fewer than 500 writes of credit remaining
3594
+ *
3595
+ * @param userStorageId Object ID of the user's UserStorage shared object.
3596
+ * @param dappStorageId Object ID of the DApp's DappStorage (optional).
3597
+ */
3598
+ async getSettlementHealth({
3599
+ userStorageId,
3600
+ dappStorageId
3601
+ }) {
3602
+ const WRITE_LIMIT = 2000n;
3603
+ const userFields = await this.getUserStorageFields(userStorageId);
3604
+ const unsettledCount = userFields.unsettled_count;
3605
+ const utilizationPct = Number(unsettledCount * 100n / WRITE_LIMIT);
3606
+ const result = {
3607
+ unsettledCount,
3608
+ writeLimit: WRITE_LIMIT,
3609
+ utilizationPct,
3610
+ isAtRisk: utilizationPct >= 80,
3611
+ isBlocked: utilizationPct >= 100
3612
+ };
3613
+ if (dappStorageId) {
3614
+ const dappFields = await this.getDappStorageFields(dappStorageId);
3615
+ const creditPool = dappFields.credit_pool;
3616
+ const baseFeePerWrite = dappFields.base_fee_per_write;
3617
+ const estimatedWritesAffordable = baseFeePerWrite > 0n ? creditPool / baseFeePerWrite : BigInt(Number.MAX_SAFE_INTEGER);
3618
+ result.creditPool = creditPool;
3619
+ result.baseFeePerWrite = baseFeePerWrite;
3620
+ result.estimatedWritesAffordable = estimatedWritesAffordable;
3621
+ result.creditAtRisk = estimatedWritesAffordable < 500n;
3622
+ }
3623
+ return result;
3624
+ }
3625
+ // ─── Private helpers ─────────────────────────────────────────────────────────
3626
+ /**
3627
+ * Return the default network configuration for the given network type.
3628
+ *
3629
+ * Useful for reading `frameworkPackageId` without instantiating a full client:
3630
+ *
3631
+ * ```ts
3632
+ * const { frameworkPackageId } = Dubhe.getDefaultConfig('testnet');
3633
+ * // → '0x8817b...' (known constant for testnet/mainnet)
3634
+ * // → undefined (for localnet/devnet — supply after local deployment)
3635
+ * ```
3636
+ */
3637
+ static getDefaultConfig(networkType) {
3638
+ return getDefaultConfig(networkType);
3639
+ }
2840
3640
  // async formatData(type: string, value: Buffer | number[] | Uint8Array) {
2841
3641
  // const u8Value = Uint8Array.from(value);
2842
3642
  // return bcs.de(type, u8Value);
@@ -2942,11 +3742,17 @@ async function loadMetadata(networkType, packageId, fullnodeUrls) {
2942
3742
  export {
2943
3743
  BcsType2 as BcsType,
2944
3744
  Dubhe,
3745
+ MAINNET_DUBHE_FRAMEWORK_PACKAGE_ID,
3746
+ MAINNET_DUBHE_HUB_OBJECT_ID,
2945
3747
  MultiSigClient,
2946
3748
  SuiAccountManager,
2947
3749
  SuiContractFactory,
2948
3750
  SuiTx,
3751
+ TESTNET_DUBHE_FRAMEWORK_PACKAGE_ID,
3752
+ TESTNET_DUBHE_HUB_OBJECT_ID,
2949
3753
  bcs4 as bcs,
3754
+ getDefaultConfig,
3755
+ getDefaultURL,
2950
3756
  loadMetadata
2951
3757
  };
2952
3758
  //# sourceMappingURL=index.mjs.map