@0xobelisk/sui-client 1.2.0-pre.99 → 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/LICENSE +92 -0
- package/dist/dubhe.d.ts +417 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.js +838 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +832 -26
- package/dist/index.mjs.map +1 -1
- package/dist/libs/suiInteractor/defaultConfig.d.ts +26 -0
- package/dist/libs/suiInteractor/index.d.ts +1 -1
- package/dist/types/index.d.ts +14 -0
- package/dist/utils/index.d.ts +19 -1
- package/package.json +21 -21
- package/src/dubhe.ts +1058 -23
- package/src/index.ts +9 -0
- package/src/libs/suiInteractor/defaultConfig.ts +53 -8
- package/src/libs/suiInteractor/index.ts +1 -1
- package/src/types/index.ts +14 -0
- package/src/utils/index.ts +24 -1
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
|
|
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:
|
|
813
|
-
accountExplorer:
|
|
814
|
-
explorer:
|
|
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 =
|
|
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
|
|
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 =
|
|
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:
|
|
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
|
-
|
|
2654
|
-
|
|
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:
|
|
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
|