@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/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.js
CHANGED
|
@@ -50,11 +50,17 @@ var src_exports = {};
|
|
|
50
50
|
__export(src_exports, {
|
|
51
51
|
BcsType: () => import_bcs4.BcsType,
|
|
52
52
|
Dubhe: () => Dubhe,
|
|
53
|
+
MAINNET_DUBHE_FRAMEWORK_PACKAGE_ID: () => MAINNET_DUBHE_FRAMEWORK_PACKAGE_ID,
|
|
54
|
+
MAINNET_DUBHE_HUB_OBJECT_ID: () => MAINNET_DUBHE_HUB_OBJECT_ID,
|
|
53
55
|
MultiSigClient: () => MultiSigClient,
|
|
54
56
|
SuiAccountManager: () => SuiAccountManager,
|
|
55
57
|
SuiContractFactory: () => SuiContractFactory,
|
|
56
58
|
SuiTx: () => SuiTx,
|
|
59
|
+
TESTNET_DUBHE_FRAMEWORK_PACKAGE_ID: () => TESTNET_DUBHE_FRAMEWORK_PACKAGE_ID,
|
|
60
|
+
TESTNET_DUBHE_HUB_OBJECT_ID: () => TESTNET_DUBHE_HUB_OBJECT_ID,
|
|
57
61
|
bcs: () => import_bcs4.bcs,
|
|
62
|
+
getDefaultConfig: () => getDefaultConfig,
|
|
63
|
+
getDefaultURL: () => getDefaultURL,
|
|
58
64
|
loadMetadata: () => loadMetadata
|
|
59
65
|
});
|
|
60
66
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -838,19 +844,26 @@ var SuiInteractor = class {
|
|
|
838
844
|
};
|
|
839
845
|
|
|
840
846
|
// src/libs/suiInteractor/defaultConfig.ts
|
|
841
|
-
var
|
|
847
|
+
var TESTNET_DUBHE_FRAMEWORK_PACKAGE_ID = "0xae33be6675639d6f1a5c468ae6bbc457ae4e22e57cd7526741d6143ce219d995";
|
|
848
|
+
var TESTNET_DUBHE_HUB_OBJECT_ID = "0x80391929a8772aba091156fd1f099bf79240d2cf4471a14026b4aa68e3a240cc";
|
|
849
|
+
var MAINNET_DUBHE_FRAMEWORK_PACKAGE_ID = "0x5bc40de124588b1e3514ca50f5e7109869351c4d9544e5f0cb9d30dd47bf8de7";
|
|
850
|
+
var MAINNET_DUBHE_HUB_OBJECT_ID = "0x96e92d9baccfd0a2927b4f209756e1edbc18904bff34e486e4bb4a151320e1e5";
|
|
851
|
+
var getDefaultConfig = (networkType = "testnet") => {
|
|
842
852
|
switch (networkType) {
|
|
843
|
-
case "localnet":
|
|
853
|
+
case "localnet": {
|
|
854
|
+
const localRpc = encodeURIComponent("http://127.0.0.1:9000");
|
|
844
855
|
return {
|
|
845
856
|
fullNode: "http://127.0.0.1:9000",
|
|
846
857
|
graphql: "http://127.0.0.1:9125",
|
|
847
858
|
network: "localnet",
|
|
848
|
-
txExplorer:
|
|
849
|
-
accountExplorer:
|
|
850
|
-
explorer:
|
|
859
|
+
txExplorer: `https://custom.suiscan.xyz/custom/tx/:txHash?network=${localRpc}`,
|
|
860
|
+
accountExplorer: `https://custom.suiscan.xyz/custom/account/:address?network=${localRpc}`,
|
|
861
|
+
explorer: `https://custom.suiscan.xyz/custom?network=${localRpc}`,
|
|
851
862
|
indexerUrl: "http://127.0.0.1:3001",
|
|
852
863
|
channelUrl: "http://127.0.0.1:8080"
|
|
864
|
+
// frameworkPackageId: undefined — set after deploying dubhe locally
|
|
853
865
|
};
|
|
866
|
+
}
|
|
854
867
|
case "devnet":
|
|
855
868
|
return {
|
|
856
869
|
fullNode: "https://fullnode.devnet.sui.io:443",
|
|
@@ -860,6 +873,7 @@ var getDefaultURL = (networkType = "testnet") => {
|
|
|
860
873
|
explorer: "https://suiscan.xyz/devnet",
|
|
861
874
|
indexerUrl: "http://127.0.0.1:3001",
|
|
862
875
|
channelUrl: "http://127.0.0.1:8080"
|
|
876
|
+
// frameworkPackageId: undefined — no persistent deployment on devnet
|
|
863
877
|
};
|
|
864
878
|
case "testnet":
|
|
865
879
|
return {
|
|
@@ -870,7 +884,9 @@ var getDefaultURL = (networkType = "testnet") => {
|
|
|
870
884
|
accountExplorer: "https://suiscan.xyz/testnet/address/:address",
|
|
871
885
|
explorer: "https://suiscan.xyz/testnet",
|
|
872
886
|
indexerUrl: "http://127.0.0.1:3001",
|
|
873
|
-
channelUrl: "http://127.0.0.1:8080"
|
|
887
|
+
channelUrl: "http://127.0.0.1:8080",
|
|
888
|
+
frameworkPackageId: TESTNET_DUBHE_FRAMEWORK_PACKAGE_ID,
|
|
889
|
+
dappHubId: TESTNET_DUBHE_HUB_OBJECT_ID
|
|
874
890
|
};
|
|
875
891
|
case "mainnet":
|
|
876
892
|
return {
|
|
@@ -881,7 +897,9 @@ var getDefaultURL = (networkType = "testnet") => {
|
|
|
881
897
|
accountExplorer: "https://suiscan.xyz/mainnet/address/:address",
|
|
882
898
|
explorer: "https://suiscan.xyz/mainnet",
|
|
883
899
|
indexerUrl: "http://127.0.0.1:3001",
|
|
884
|
-
channelUrl: "http://127.0.0.1:8080"
|
|
900
|
+
channelUrl: "http://127.0.0.1:8080",
|
|
901
|
+
frameworkPackageId: MAINNET_DUBHE_FRAMEWORK_PACKAGE_ID || void 0,
|
|
902
|
+
dappHubId: MAINNET_DUBHE_HUB_OBJECT_ID || void 0
|
|
885
903
|
};
|
|
886
904
|
default:
|
|
887
905
|
return {
|
|
@@ -892,10 +910,13 @@ var getDefaultURL = (networkType = "testnet") => {
|
|
|
892
910
|
accountExplorer: "https://suiscan.xyz/testnet/address/:address",
|
|
893
911
|
explorer: "https://suiscan.xyz/testnet",
|
|
894
912
|
indexerUrl: "http://127.0.0.1:3001",
|
|
895
|
-
channelUrl: "http://127.0.0.1:8080"
|
|
913
|
+
channelUrl: "http://127.0.0.1:8080",
|
|
914
|
+
frameworkPackageId: TESTNET_DUBHE_FRAMEWORK_PACKAGE_ID,
|
|
915
|
+
dappHubId: TESTNET_DUBHE_HUB_OBJECT_ID
|
|
896
916
|
};
|
|
897
917
|
}
|
|
898
918
|
};
|
|
919
|
+
var getDefaultURL = getDefaultConfig;
|
|
899
920
|
|
|
900
921
|
// src/libs/suiContractFactory/index.ts
|
|
901
922
|
var SuiContractFactory = class {
|
|
@@ -1083,6 +1104,10 @@ function normalizePackageId(input) {
|
|
|
1083
1104
|
const normalized = withoutPrefix.replace(/^0+/, "");
|
|
1084
1105
|
return "0x" + normalized;
|
|
1085
1106
|
}
|
|
1107
|
+
function normalizeDappKey(packageId) {
|
|
1108
|
+
const raw = packageId.replace(/^0x/, "");
|
|
1109
|
+
return `${raw.padStart(64, "0")}::dapp_key::DappKey`;
|
|
1110
|
+
}
|
|
1086
1111
|
|
|
1087
1112
|
// src/errors/index.ts
|
|
1088
1113
|
var ContractDataParsingError = class extends Error {
|
|
@@ -1381,6 +1406,7 @@ function createTx(meta, fn) {
|
|
|
1381
1406
|
}
|
|
1382
1407
|
);
|
|
1383
1408
|
}
|
|
1409
|
+
var SUI_CLOCK_OBJECT_ID = "0x0000000000000000000000000000000000000000000000000000000000000006";
|
|
1384
1410
|
var _query, _tx, _object, _exec, _read, _getVectorDepth, _bcs, _bcsenum, _processKeyParameter, processKeyParameter_fn;
|
|
1385
1411
|
var Dubhe = class {
|
|
1386
1412
|
/**
|
|
@@ -1396,6 +1422,7 @@ var Dubhe = class {
|
|
|
1396
1422
|
* @param packageId
|
|
1397
1423
|
* @param metadata
|
|
1398
1424
|
* @param channelUrl, the base URL for Dubhe Channel API, optional
|
|
1425
|
+
* @param frameworkPackageId, the published package ID of the dubhe framework, required for proxy operations
|
|
1399
1426
|
*/
|
|
1400
1427
|
constructor({
|
|
1401
1428
|
mnemonics,
|
|
@@ -1403,8 +1430,12 @@ var Dubhe = class {
|
|
|
1403
1430
|
networkType,
|
|
1404
1431
|
fullnodeUrls,
|
|
1405
1432
|
packageId,
|
|
1433
|
+
dappKey: dappKeyParam,
|
|
1406
1434
|
metadata,
|
|
1407
|
-
channelUrl
|
|
1435
|
+
channelUrl,
|
|
1436
|
+
frameworkPackageId,
|
|
1437
|
+
dappStorageId,
|
|
1438
|
+
dappHubId
|
|
1408
1439
|
} = {}) {
|
|
1409
1440
|
// async getTransactions({
|
|
1410
1441
|
// first,
|
|
@@ -2105,7 +2136,7 @@ var Dubhe = class {
|
|
|
2105
2136
|
};
|
|
2106
2137
|
});
|
|
2107
2138
|
networkType = networkType ?? "mainnet";
|
|
2108
|
-
const defaultParams =
|
|
2139
|
+
const defaultParams = getDefaultConfig(networkType);
|
|
2109
2140
|
this.accountManager = new SuiAccountManager({ mnemonics, secretKey });
|
|
2110
2141
|
fullnodeUrls = fullnodeUrls || [defaultParams.fullNode];
|
|
2111
2142
|
this.suiInteractor = new SuiInteractor(fullnodeUrls, networkType);
|
|
@@ -2113,6 +2144,11 @@ var Dubhe = class {
|
|
|
2113
2144
|
baseUrl: channelUrl || defaultParams.channelUrl
|
|
2114
2145
|
});
|
|
2115
2146
|
this.packageId = packageId ? normalizePackageId(packageId) : void 0;
|
|
2147
|
+
this.dappKey = dappKeyParam;
|
|
2148
|
+
const networkDefault = defaultParams.frameworkPackageId;
|
|
2149
|
+
this.frameworkPackageId = frameworkPackageId ? normalizePackageId(frameworkPackageId) : networkDefault ? normalizePackageId(networkDefault) : void 0;
|
|
2150
|
+
this.dappStorageId = dappStorageId;
|
|
2151
|
+
this.dappHubId = dappHubId;
|
|
2116
2152
|
if (metadata !== void 0) {
|
|
2117
2153
|
this.metadata = metadata;
|
|
2118
2154
|
const maxLoopNum = 5;
|
|
@@ -2403,6 +2439,27 @@ var Dubhe = class {
|
|
|
2403
2439
|
getPackageId() {
|
|
2404
2440
|
return this.contractFactory.packageId;
|
|
2405
2441
|
}
|
|
2442
|
+
/**
|
|
2443
|
+
* Return the fully-qualified Move type string for this DApp's `DappKey`.
|
|
2444
|
+
*
|
|
2445
|
+
* The format matches what `std::type_name::get<DappKey>()` returns on-chain:
|
|
2446
|
+
* `<64-char-zero-padded-address>::dapp_key::DappKey`
|
|
2447
|
+
*
|
|
2448
|
+
* Use this whenever you need to pass a type argument or build a `target`
|
|
2449
|
+
* string that references the DApp's DappKey type.
|
|
2450
|
+
*
|
|
2451
|
+
* @example
|
|
2452
|
+
* ```typescript
|
|
2453
|
+
* tx.moveCall({
|
|
2454
|
+
* target: `${frameworkPackageId}::dapp_system::settle_writes`,
|
|
2455
|
+
* typeArguments: [contract.getDappKey()],
|
|
2456
|
+
* arguments: [...],
|
|
2457
|
+
* });
|
|
2458
|
+
* ```
|
|
2459
|
+
*/
|
|
2460
|
+
getDappKey() {
|
|
2461
|
+
return this.dappKey;
|
|
2462
|
+
}
|
|
2406
2463
|
getMetadata() {
|
|
2407
2464
|
return this.contractFactory.metadata;
|
|
2408
2465
|
}
|
|
@@ -2410,7 +2467,7 @@ var Dubhe = class {
|
|
|
2410
2467
|
return this.suiInteractor.network;
|
|
2411
2468
|
}
|
|
2412
2469
|
getNetworkConfig() {
|
|
2413
|
-
return
|
|
2470
|
+
return getDefaultConfig(this.getNetwork());
|
|
2414
2471
|
}
|
|
2415
2472
|
getTxExplorerUrl(txHash) {
|
|
2416
2473
|
return this.getNetworkConfig().txExplorer.replace(":txHash", txHash);
|
|
@@ -2436,7 +2493,7 @@ var Dubhe = class {
|
|
|
2436
2493
|
const fullnodeUrlsChanged = config.fullnodeUrls !== void 0;
|
|
2437
2494
|
if (networkChanged || fullnodeUrlsChanged) {
|
|
2438
2495
|
const newNetworkType = config.networkType ?? this.suiInteractor.network;
|
|
2439
|
-
const defaultParams =
|
|
2496
|
+
const defaultParams = getDefaultConfig(newNetworkType);
|
|
2440
2497
|
const newFullnodeUrls = config.fullnodeUrls || [defaultParams.fullNode];
|
|
2441
2498
|
this.suiInteractor = new SuiInteractor(newFullnodeUrls, newNetworkType);
|
|
2442
2499
|
}
|
|
@@ -2447,6 +2504,9 @@ var Dubhe = class {
|
|
|
2447
2504
|
}
|
|
2448
2505
|
const packageIdChanged = config.packageId !== void 0 && config.packageId !== this.packageId;
|
|
2449
2506
|
const metadataChanged = config.metadata !== void 0 && config.metadata !== this.metadata;
|
|
2507
|
+
if (config.dappKey !== void 0) {
|
|
2508
|
+
this.dappKey = config.dappKey;
|
|
2509
|
+
}
|
|
2450
2510
|
if (packageIdChanged || metadataChanged) {
|
|
2451
2511
|
if (config.packageId !== void 0) {
|
|
2452
2512
|
this.packageId = normalizePackageId(config.packageId);
|
|
@@ -2638,11 +2698,8 @@ var Dubhe = class {
|
|
|
2638
2698
|
if (account && account.startsWith("0x")) {
|
|
2639
2699
|
account = account.slice(2);
|
|
2640
2700
|
}
|
|
2641
|
-
if (packageId && packageId.startsWith("0x")) {
|
|
2642
|
-
packageId = packageId.slice(2);
|
|
2643
|
-
}
|
|
2644
2701
|
const payload = {
|
|
2645
|
-
dapp_key:
|
|
2702
|
+
dapp_key: normalizeDappKey(packageId ?? ""),
|
|
2646
2703
|
account,
|
|
2647
2704
|
table,
|
|
2648
2705
|
key
|
|
@@ -2681,22 +2738,16 @@ var Dubhe = class {
|
|
|
2681
2738
|
* );
|
|
2682
2739
|
*/
|
|
2683
2740
|
async subscribeChannelTable({
|
|
2684
|
-
packageId,
|
|
2741
|
+
packageId: _packageId,
|
|
2685
2742
|
account,
|
|
2686
2743
|
table,
|
|
2687
2744
|
key
|
|
2688
2745
|
} = {}, options) {
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
if (account.startsWith("0x")) {
|
|
2692
|
-
account = account.slice(2);
|
|
2693
|
-
}
|
|
2694
|
-
}
|
|
2695
|
-
if (packageId && packageId.startsWith("0x")) {
|
|
2696
|
-
packageId = packageId.slice(2);
|
|
2746
|
+
if (account !== void 0 && account.startsWith("0x")) {
|
|
2747
|
+
account = account.slice(2);
|
|
2697
2748
|
}
|
|
2698
2749
|
const payload = {
|
|
2699
|
-
dapp_key:
|
|
2750
|
+
dapp_key: this.dappKey
|
|
2700
2751
|
};
|
|
2701
2752
|
if (account !== void 0) {
|
|
2702
2753
|
payload.account = account;
|
|
@@ -2873,6 +2924,761 @@ var Dubhe = class {
|
|
|
2873
2924
|
async entity_key_from_u256(x) {
|
|
2874
2925
|
return numberToAddressHex(x);
|
|
2875
2926
|
}
|
|
2927
|
+
// ─── UserStorage helpers ────────────────────────────────────────────────────
|
|
2928
|
+
/**
|
|
2929
|
+
* Call the DApp's generated `user_storage_init::init_user_storage` entry function,
|
|
2930
|
+
* which creates and shares a `UserStorage` object for the signer.
|
|
2931
|
+
*
|
|
2932
|
+
* Each DApp exposes this entry point under its own package ID.
|
|
2933
|
+
* On Sui, the `UserStorage` object is shared immediately so all transactions
|
|
2934
|
+
* can reference it without knowing the object ID in advance.
|
|
2935
|
+
*
|
|
2936
|
+
* @param dappHubId Object ID of the DappHub shared object
|
|
2937
|
+
* @param dappStorageId Object ID of the DApp's DappStorage shared object
|
|
2938
|
+
*/
|
|
2939
|
+
async initUserStorage({
|
|
2940
|
+
dappHubId,
|
|
2941
|
+
dappStorageId: dappStorageIdParam,
|
|
2942
|
+
derivePathParams,
|
|
2943
|
+
onSuccess,
|
|
2944
|
+
onError
|
|
2945
|
+
}) {
|
|
2946
|
+
const storageId = dappStorageIdParam ?? this.dappStorageId;
|
|
2947
|
+
if (!storageId) {
|
|
2948
|
+
throw new Error(
|
|
2949
|
+
"dappStorageId is required for initUserStorage. Pass it directly or set it in the Dubhe constructor."
|
|
2950
|
+
);
|
|
2951
|
+
}
|
|
2952
|
+
const pkg = this.packageId;
|
|
2953
|
+
if (!pkg)
|
|
2954
|
+
throw new Error("packageId is required for initUserStorage.");
|
|
2955
|
+
const tx = new import_transactions3.Transaction();
|
|
2956
|
+
tx.moveCall({
|
|
2957
|
+
target: `${pkg}::user_storage_init::init_user_storage`,
|
|
2958
|
+
arguments: [tx.object(dappHubId), tx.object(storageId)]
|
|
2959
|
+
});
|
|
2960
|
+
return this.signAndSendTxn({
|
|
2961
|
+
tx,
|
|
2962
|
+
derivePathParams,
|
|
2963
|
+
onSuccess,
|
|
2964
|
+
onError
|
|
2965
|
+
});
|
|
2966
|
+
}
|
|
2967
|
+
/**
|
|
2968
|
+
* Activate a session key for the signer's `UserStorage`.
|
|
2969
|
+
*
|
|
2970
|
+
* The signer must be the `canonical_owner` of the `UserStorage` object.
|
|
2971
|
+
* Calls `<frameworkPackageId>::dapp_system::activate_session<DappKey>`.
|
|
2972
|
+
*
|
|
2973
|
+
* @param userStorageId Object ID of the UserStorage to activate a session for
|
|
2974
|
+
* @param sessionWallet Sui address of the session wallet
|
|
2975
|
+
* @param durationMs Session duration in milliseconds ([60_000, 604_800_000])
|
|
2976
|
+
* @param clockObjectId Object ID of the Sui Clock (default: 0x6)
|
|
2977
|
+
*/
|
|
2978
|
+
async activateSession({
|
|
2979
|
+
userStorageId,
|
|
2980
|
+
sessionWallet,
|
|
2981
|
+
durationMs,
|
|
2982
|
+
dappHubId: dappHubIdParam,
|
|
2983
|
+
clockObjectId,
|
|
2984
|
+
derivePathParams,
|
|
2985
|
+
onSuccess,
|
|
2986
|
+
onError
|
|
2987
|
+
}) {
|
|
2988
|
+
const fwPkg = this.frameworkPackageId;
|
|
2989
|
+
if (!fwPkg) {
|
|
2990
|
+
throw new Error(
|
|
2991
|
+
'frameworkPackageId is required for activateSession. Set it in the Dubhe constructor ({ frameworkPackageId: "0x..." }).'
|
|
2992
|
+
);
|
|
2993
|
+
}
|
|
2994
|
+
const typeArg = this.dappKey;
|
|
2995
|
+
if (!typeArg) {
|
|
2996
|
+
throw new Error(
|
|
2997
|
+
'dappKey is required for activateSession. Set it in the Dubhe constructor ({ packageId: "0x..." }).'
|
|
2998
|
+
);
|
|
2999
|
+
}
|
|
3000
|
+
const hubId = dappHubIdParam ?? this.dappHubId;
|
|
3001
|
+
if (!hubId) {
|
|
3002
|
+
throw new Error(
|
|
3003
|
+
'dappHubId is required for activateSession. Pass it directly or set it in the Dubhe constructor ({ dappHubId: "0x..." }).'
|
|
3004
|
+
);
|
|
3005
|
+
}
|
|
3006
|
+
const clockId = clockObjectId ?? SUI_CLOCK_OBJECT_ID;
|
|
3007
|
+
const tx = new import_transactions3.Transaction();
|
|
3008
|
+
tx.moveCall({
|
|
3009
|
+
target: `${fwPkg}::dapp_system::activate_session`,
|
|
3010
|
+
typeArguments: [typeArg],
|
|
3011
|
+
arguments: [
|
|
3012
|
+
tx.object(hubId),
|
|
3013
|
+
tx.object(userStorageId),
|
|
3014
|
+
tx.pure.address(sessionWallet),
|
|
3015
|
+
tx.pure.u64(durationMs),
|
|
3016
|
+
tx.object(clockId)
|
|
3017
|
+
]
|
|
3018
|
+
});
|
|
3019
|
+
return this.signAndSendTxn({
|
|
3020
|
+
tx,
|
|
3021
|
+
derivePathParams,
|
|
3022
|
+
onSuccess,
|
|
3023
|
+
onError
|
|
3024
|
+
});
|
|
3025
|
+
}
|
|
3026
|
+
/**
|
|
3027
|
+
* Deactivate the current session key on a `UserStorage`.
|
|
3028
|
+
*
|
|
3029
|
+
* Allowed callers:
|
|
3030
|
+
* - The `canonical_owner` (revoke at any time).
|
|
3031
|
+
* - The session key itself (voluntary sign-out).
|
|
3032
|
+
* - Anyone, once the session has expired.
|
|
3033
|
+
*
|
|
3034
|
+
* Calls `<frameworkPackageId>::dapp_system::deactivate_session<DappKey>`.
|
|
3035
|
+
*
|
|
3036
|
+
* @param userStorageId Object ID of the UserStorage to deactivate the session on
|
|
3037
|
+
*/
|
|
3038
|
+
async deactivateSession({
|
|
3039
|
+
userStorageId,
|
|
3040
|
+
dappHubId: dappHubIdParam,
|
|
3041
|
+
derivePathParams,
|
|
3042
|
+
onSuccess,
|
|
3043
|
+
onError
|
|
3044
|
+
}) {
|
|
3045
|
+
const fwPkg = this.frameworkPackageId;
|
|
3046
|
+
if (!fwPkg) {
|
|
3047
|
+
throw new Error(
|
|
3048
|
+
'frameworkPackageId is required for deactivateSession. Set it in the Dubhe constructor ({ frameworkPackageId: "0x..." }).'
|
|
3049
|
+
);
|
|
3050
|
+
}
|
|
3051
|
+
const typeArg = this.dappKey;
|
|
3052
|
+
if (!typeArg) {
|
|
3053
|
+
throw new Error(
|
|
3054
|
+
'dappKey is required for deactivateSession. Set it in the Dubhe constructor ({ packageId: "0x..." }).'
|
|
3055
|
+
);
|
|
3056
|
+
}
|
|
3057
|
+
const hubId = dappHubIdParam ?? this.dappHubId;
|
|
3058
|
+
if (!hubId) {
|
|
3059
|
+
throw new Error(
|
|
3060
|
+
'dappHubId is required for deactivateSession. Pass it directly or set it in the Dubhe constructor ({ dappHubId: "0x..." }).'
|
|
3061
|
+
);
|
|
3062
|
+
}
|
|
3063
|
+
const tx = new import_transactions3.Transaction();
|
|
3064
|
+
tx.moveCall({
|
|
3065
|
+
target: `${fwPkg}::dapp_system::deactivate_session`,
|
|
3066
|
+
typeArguments: [typeArg],
|
|
3067
|
+
arguments: [tx.object(hubId), tx.object(userStorageId)]
|
|
3068
|
+
});
|
|
3069
|
+
return this.signAndSendTxn({
|
|
3070
|
+
tx,
|
|
3071
|
+
derivePathParams,
|
|
3072
|
+
onSuccess,
|
|
3073
|
+
onError
|
|
3074
|
+
});
|
|
3075
|
+
}
|
|
3076
|
+
/**
|
|
3077
|
+
* Look up the `UserStorage` object ID for the given address in this DApp.
|
|
3078
|
+
*
|
|
3079
|
+
* Scans the transaction history for `user_storage_init::init_user_storage`
|
|
3080
|
+
* calls from the given address and extracts the created `UserStorage` object
|
|
3081
|
+
* ID from the transaction's object changes.
|
|
3082
|
+
*
|
|
3083
|
+
* Returns the object ID on success, or `null` when:
|
|
3084
|
+
* - `packageId` or `frameworkPackageId` is not set.
|
|
3085
|
+
* - The user has not yet called `initUserStorage`.
|
|
3086
|
+
*
|
|
3087
|
+
* @param userAddress Sui address to look up (with or without 0x prefix).
|
|
3088
|
+
*/
|
|
3089
|
+
async getUserStorageId(userAddress) {
|
|
3090
|
+
const pkg = this.packageId;
|
|
3091
|
+
const fwPkg = this.frameworkPackageId;
|
|
3092
|
+
if (!pkg || !fwPkg)
|
|
3093
|
+
return null;
|
|
3094
|
+
const normalizedUser = userAddress.startsWith("0x") ? userAddress : `0x${userAddress}`;
|
|
3095
|
+
const expectedType = `${fwPkg}::dapp_service::UserStorage`;
|
|
3096
|
+
let cursor = null;
|
|
3097
|
+
let hasNextPage = true;
|
|
3098
|
+
while (hasNextPage) {
|
|
3099
|
+
const result = await this.suiInteractor.currentClient.queryTransactionBlocks({
|
|
3100
|
+
filter: {
|
|
3101
|
+
MoveFunction: {
|
|
3102
|
+
package: pkg,
|
|
3103
|
+
module: "user_storage_init",
|
|
3104
|
+
function: "init_user_storage"
|
|
3105
|
+
}
|
|
3106
|
+
},
|
|
3107
|
+
options: { showObjectChanges: true },
|
|
3108
|
+
...cursor !== null && cursor !== void 0 ? { cursor } : {},
|
|
3109
|
+
limit: 50
|
|
3110
|
+
});
|
|
3111
|
+
for (const tx of result.data) {
|
|
3112
|
+
if (!tx.objectChanges)
|
|
3113
|
+
continue;
|
|
3114
|
+
const created = tx.objectChanges.find(
|
|
3115
|
+
(c) => c.type === "created" && typeof c.objectType === "string" && c.objectType === expectedType && c.sender === normalizedUser
|
|
3116
|
+
);
|
|
3117
|
+
if (created?.objectId)
|
|
3118
|
+
return created.objectId;
|
|
3119
|
+
}
|
|
3120
|
+
hasNextPage = result.hasNextPage;
|
|
3121
|
+
cursor = result.nextCursor ?? null;
|
|
3122
|
+
}
|
|
3123
|
+
return null;
|
|
3124
|
+
}
|
|
3125
|
+
/**
|
|
3126
|
+
* Fetch and return the on-chain fields of a `UserStorage` shared object.
|
|
3127
|
+
*
|
|
3128
|
+
* The returned object mirrors Move's `UserStorage` struct plus two
|
|
3129
|
+
* convenience fields computed from the raw counters:
|
|
3130
|
+
* - `unsettled_count` = write_count − settled_count
|
|
3131
|
+
* - `unsettled_bytes` = write_bytes − settled_bytes
|
|
3132
|
+
*
|
|
3133
|
+
* @param userStorageId Object ID of the UserStorage to inspect.
|
|
3134
|
+
*/
|
|
3135
|
+
async getUserStorageFields(userStorageId) {
|
|
3136
|
+
const obj = await this.suiInteractor.getObject(userStorageId);
|
|
3137
|
+
const fields = obj?.content?.fields ?? {};
|
|
3138
|
+
const writeCount = BigInt(fields.write_count ?? 0);
|
|
3139
|
+
const settledCount = BigInt(fields.settled_count ?? 0);
|
|
3140
|
+
const writeBytes = BigInt(fields.write_bytes ?? 0);
|
|
3141
|
+
const settledBytes = BigInt(fields.settled_bytes ?? 0);
|
|
3142
|
+
return {
|
|
3143
|
+
objectId: userStorageId,
|
|
3144
|
+
dapp_key: fields.dapp_key ?? "",
|
|
3145
|
+
canonical_owner: fields.canonical_owner ?? "",
|
|
3146
|
+
session_key: fields.session_key ?? "",
|
|
3147
|
+
session_expires_at: BigInt(fields.session_expires_at ?? 0),
|
|
3148
|
+
write_count: writeCount,
|
|
3149
|
+
settled_count: settledCount,
|
|
3150
|
+
write_bytes: writeBytes,
|
|
3151
|
+
settled_bytes: settledBytes,
|
|
3152
|
+
unsettled_count: writeCount - settledCount,
|
|
3153
|
+
unsettled_bytes: writeBytes - settledBytes
|
|
3154
|
+
};
|
|
3155
|
+
}
|
|
3156
|
+
/**
|
|
3157
|
+
* Fetch and return the on-chain fields of a `DappStorage` shared object.
|
|
3158
|
+
*
|
|
3159
|
+
* Covers the full metadata + fee/credit subset of the Move struct.
|
|
3160
|
+
*
|
|
3161
|
+
* @param dappStorageId Object ID of the DappStorage to inspect.
|
|
3162
|
+
*/
|
|
3163
|
+
/**
|
|
3164
|
+
* Read the framework-level fields from the DappHub shared object.
|
|
3165
|
+
*
|
|
3166
|
+
* Key fields:
|
|
3167
|
+
* - `treasury` — address that directly receives framework fee income
|
|
3168
|
+
* - `pending_treasury` — queued treasury address during two-step rotation
|
|
3169
|
+
* - `framework_admin` — address that can update framework configuration
|
|
3170
|
+
* - `version` — current framework version
|
|
3171
|
+
*/
|
|
3172
|
+
async getDappHubFields(dappHubId) {
|
|
3173
|
+
const obj = await this.suiInteractor.getObject(dappHubId);
|
|
3174
|
+
const fields = obj?.content?.fields ?? {};
|
|
3175
|
+
const feeConfig = fields.fee_config?.fields ?? fields.fee_config ?? {};
|
|
3176
|
+
const adminConfig = fields.admin_config?.fields ?? fields.admin_config ?? {};
|
|
3177
|
+
return {
|
|
3178
|
+
objectId: dappHubId,
|
|
3179
|
+
version: Number(fields.version ?? 0),
|
|
3180
|
+
framework_admin: adminConfig.admin ?? fields.framework_admin ?? "",
|
|
3181
|
+
treasury: feeConfig.treasury ?? fields.treasury ?? "",
|
|
3182
|
+
pending_treasury: feeConfig.pending_treasury ?? fields.pending_treasury ?? ""
|
|
3183
|
+
};
|
|
3184
|
+
}
|
|
3185
|
+
async getDappStorageFields(dappStorageId) {
|
|
3186
|
+
const obj = await this.suiInteractor.getObject(dappStorageId);
|
|
3187
|
+
const fields = obj?.content?.fields ?? {};
|
|
3188
|
+
return {
|
|
3189
|
+
objectId: dappStorageId,
|
|
3190
|
+
dapp_key: fields.dapp_key ?? "",
|
|
3191
|
+
name: fields.name ?? "",
|
|
3192
|
+
description: fields.description ?? "",
|
|
3193
|
+
website_url: fields.website_url ?? "",
|
|
3194
|
+
admin: fields.admin ?? "",
|
|
3195
|
+
version: Number(fields.version ?? 0),
|
|
3196
|
+
paused: Boolean(fields.paused),
|
|
3197
|
+
free_credit: BigInt(fields.free_credit ?? 0),
|
|
3198
|
+
free_credit_expires_at: BigInt(fields.free_credit_expires_at ?? 0),
|
|
3199
|
+
credit_pool: BigInt(fields.credit_pool ?? 0),
|
|
3200
|
+
min_credit_to_unsuspend: BigInt(fields.min_credit_to_unsuspend ?? 0),
|
|
3201
|
+
suspended: Boolean(fields.suspended),
|
|
3202
|
+
total_settled: BigInt(fields.total_settled ?? 0),
|
|
3203
|
+
base_fee_per_write: BigInt(fields.base_fee_per_write ?? 0),
|
|
3204
|
+
bytes_fee_per_byte: BigInt(fields.bytes_fee_per_byte ?? 0),
|
|
3205
|
+
settlement_mode: Number(fields.settlement_mode ?? 0),
|
|
3206
|
+
write_fee_dapp_share_bps: Number(fields.write_fee_dapp_share_bps ?? 0)
|
|
3207
|
+
};
|
|
3208
|
+
}
|
|
3209
|
+
/**
|
|
3210
|
+
* Append a `dapp_system::settle_writes` moveCall to an existing PTB.
|
|
3211
|
+
*
|
|
3212
|
+
* Does NOT send the transaction — call this before your own moveCall(s) to
|
|
3213
|
+
* settle any accumulated write debt within the same PTB at no extra round-trip.
|
|
3214
|
+
*
|
|
3215
|
+
* The framework function never aborts due to insufficient credit (it silently
|
|
3216
|
+
* skips or partially settles), so prepending this to any PTB is always safe.
|
|
3217
|
+
*
|
|
3218
|
+
* @param tx The Transaction object to append the moveCall to.
|
|
3219
|
+
* @param dappHubId Object ID of the DappHub shared object.
|
|
3220
|
+
* @param dappStorageId Object ID of the DApp's DappStorage (falls back to constructor value).
|
|
3221
|
+
* @param userStorageId Object ID of the user's UserStorage shared object.
|
|
3222
|
+
*/
|
|
3223
|
+
buildSettleWritesTx(tx, {
|
|
3224
|
+
dappHubId,
|
|
3225
|
+
dappStorageId: dappStorageIdParam,
|
|
3226
|
+
userStorageId
|
|
3227
|
+
}) {
|
|
3228
|
+
const fwPkg = this.frameworkPackageId;
|
|
3229
|
+
if (!fwPkg) {
|
|
3230
|
+
throw new Error(
|
|
3231
|
+
'frameworkPackageId is required for buildSettleWritesTx. Set it in the Dubhe constructor ({ frameworkPackageId: "0x..." }).'
|
|
3232
|
+
);
|
|
3233
|
+
}
|
|
3234
|
+
const storageId = dappStorageIdParam ?? this.dappStorageId;
|
|
3235
|
+
if (!storageId) {
|
|
3236
|
+
throw new Error(
|
|
3237
|
+
"dappStorageId is required for buildSettleWritesTx. Pass it directly or set it in the Dubhe constructor."
|
|
3238
|
+
);
|
|
3239
|
+
}
|
|
3240
|
+
const typeArg = this.dappKey;
|
|
3241
|
+
if (!typeArg) {
|
|
3242
|
+
throw new Error(
|
|
3243
|
+
'dappKey is required for buildSettleWritesTx. Set it in the Dubhe constructor ({ packageId: "0x..." }).'
|
|
3244
|
+
);
|
|
3245
|
+
}
|
|
3246
|
+
tx.moveCall({
|
|
3247
|
+
target: `${fwPkg}::dapp_system::settle_writes`,
|
|
3248
|
+
typeArguments: [typeArg],
|
|
3249
|
+
arguments: [tx.object(dappHubId), tx.object(storageId), tx.object(userStorageId)]
|
|
3250
|
+
});
|
|
3251
|
+
}
|
|
3252
|
+
/**
|
|
3253
|
+
* Settle accumulated write debt for a user (standalone transaction).
|
|
3254
|
+
*
|
|
3255
|
+
* Builds a PTB containing a single `dapp_system::settle_writes` call and
|
|
3256
|
+
* sends it. For embedding settlement into an existing PTB, use
|
|
3257
|
+
* `buildSettleWritesTx` instead.
|
|
3258
|
+
*
|
|
3259
|
+
* This is safe to call at any time — the framework function never aborts
|
|
3260
|
+
* due to insufficient credit; it silently skips or partially settles.
|
|
3261
|
+
*
|
|
3262
|
+
* @param dappHubId Object ID of the DappHub shared object.
|
|
3263
|
+
* @param dappStorageId Object ID of the DApp's DappStorage shared object.
|
|
3264
|
+
* @param userStorageId Object ID of the user's UserStorage shared object.
|
|
3265
|
+
*/
|
|
3266
|
+
async settleWrites({
|
|
3267
|
+
dappHubId,
|
|
3268
|
+
dappStorageId,
|
|
3269
|
+
userStorageId,
|
|
3270
|
+
derivePathParams,
|
|
3271
|
+
onSuccess,
|
|
3272
|
+
onError
|
|
3273
|
+
}) {
|
|
3274
|
+
const tx = new import_transactions3.Transaction();
|
|
3275
|
+
this.buildSettleWritesTx(tx, { dappHubId, dappStorageId, userStorageId });
|
|
3276
|
+
return this.signAndSendTxn({
|
|
3277
|
+
tx,
|
|
3278
|
+
derivePathParams,
|
|
3279
|
+
onSuccess,
|
|
3280
|
+
onError
|
|
3281
|
+
});
|
|
3282
|
+
}
|
|
3283
|
+
/**
|
|
3284
|
+
* Recharge a DApp's credit pool by paying with the framework's accepted coin type.
|
|
3285
|
+
*
|
|
3286
|
+
* Wraps `dapp_system::recharge_credit<DappKey, CoinType>`. Any account may call
|
|
3287
|
+
* this — no admin restriction. Payment is forwarded to the framework treasury and
|
|
3288
|
+
* the DApp's `credit_pool` is increased by the coin value.
|
|
3289
|
+
*
|
|
3290
|
+
* Only valid in DAPP_SUBSIDIZES settlement mode. Aborts with
|
|
3291
|
+
* `wrong_settlement_mode` if the DApp is in USER_PAYS mode.
|
|
3292
|
+
*
|
|
3293
|
+
* @param dappHubId Object ID of the DappHub shared object.
|
|
3294
|
+
* @param dappStorageId Object ID of the DApp's DappStorage shared object.
|
|
3295
|
+
* @param coinObjectId Object ID of the Coin to use as payment.
|
|
3296
|
+
* @param coinType Move type of the coin (default: "0x2::sui::SUI").
|
|
3297
|
+
*/
|
|
3298
|
+
async rechargeCredit({
|
|
3299
|
+
dappHubId,
|
|
3300
|
+
dappStorageId: dappStorageIdParam,
|
|
3301
|
+
coinObjectId,
|
|
3302
|
+
coinType = "0x2::sui::SUI",
|
|
3303
|
+
derivePathParams,
|
|
3304
|
+
onSuccess,
|
|
3305
|
+
onError
|
|
3306
|
+
}) {
|
|
3307
|
+
const fwPkg = this.frameworkPackageId;
|
|
3308
|
+
if (!fwPkg) {
|
|
3309
|
+
throw new Error(
|
|
3310
|
+
'frameworkPackageId is required for rechargeCredit. Set it in the Dubhe constructor ({ frameworkPackageId: "0x..." }).'
|
|
3311
|
+
);
|
|
3312
|
+
}
|
|
3313
|
+
const storageId = dappStorageIdParam ?? this.dappStorageId;
|
|
3314
|
+
if (!storageId) {
|
|
3315
|
+
throw new Error(
|
|
3316
|
+
"dappStorageId is required for rechargeCredit. Pass it directly or set it in the Dubhe constructor."
|
|
3317
|
+
);
|
|
3318
|
+
}
|
|
3319
|
+
const typeArg = this.dappKey;
|
|
3320
|
+
if (!typeArg) {
|
|
3321
|
+
throw new Error(
|
|
3322
|
+
'dappKey is required for rechargeCredit. Set it in the Dubhe constructor ({ packageId: "0x..." }).'
|
|
3323
|
+
);
|
|
3324
|
+
}
|
|
3325
|
+
const tx = new import_transactions3.Transaction();
|
|
3326
|
+
tx.moveCall({
|
|
3327
|
+
target: `${fwPkg}::dapp_system::recharge_credit`,
|
|
3328
|
+
typeArguments: [typeArg, coinType],
|
|
3329
|
+
arguments: [tx.object(dappHubId), tx.object(storageId), tx.object(coinObjectId)]
|
|
3330
|
+
});
|
|
3331
|
+
return this.signAndSendTxn({
|
|
3332
|
+
tx,
|
|
3333
|
+
derivePathParams,
|
|
3334
|
+
onSuccess,
|
|
3335
|
+
onError
|
|
3336
|
+
});
|
|
3337
|
+
}
|
|
3338
|
+
/**
|
|
3339
|
+
* Append a `dapp_system::settle_writes_user_pays` moveCall to an existing PTB.
|
|
3340
|
+
*
|
|
3341
|
+
* Splits `maxPaymentMist` MIST from `tx.gas` as the payment coin, then calls
|
|
3342
|
+
* `settle_writes_user_pays`. The framework returns any overpayment automatically,
|
|
3343
|
+
* so it is safe to over-estimate. This lets a session key pay for settlement
|
|
3344
|
+
* inline without requiring a separate owned Coin object.
|
|
3345
|
+
*
|
|
3346
|
+
* @param tx The Transaction object to append the moveCall to.
|
|
3347
|
+
* @param dappHubId Object ID of the DappHub shared object.
|
|
3348
|
+
* @param dappStorageId Object ID of the DApp's DappStorage (falls back to constructor value).
|
|
3349
|
+
* @param userStorageId Object ID of the user's UserStorage shared object.
|
|
3350
|
+
* @param maxPaymentMist Upper bound for the payment in MIST (default: 50_000_000 = 0.05 SUI).
|
|
3351
|
+
* @param coinType Move type of the payment coin (default: "0x2::sui::SUI").
|
|
3352
|
+
*/
|
|
3353
|
+
buildSettleWritesUserPaysTx(tx, {
|
|
3354
|
+
dappHubId,
|
|
3355
|
+
dappStorageId: dappStorageIdParam,
|
|
3356
|
+
userStorageId,
|
|
3357
|
+
maxPaymentMist = 50000000n,
|
|
3358
|
+
coinType = "0x2::sui::SUI"
|
|
3359
|
+
}) {
|
|
3360
|
+
const fwPkg = this.frameworkPackageId;
|
|
3361
|
+
if (!fwPkg) {
|
|
3362
|
+
throw new Error(
|
|
3363
|
+
'frameworkPackageId is required for buildSettleWritesUserPaysTx. Set it in the Dubhe constructor ({ frameworkPackageId: "0x..." }).'
|
|
3364
|
+
);
|
|
3365
|
+
}
|
|
3366
|
+
const storageId = dappStorageIdParam ?? this.dappStorageId;
|
|
3367
|
+
if (!storageId) {
|
|
3368
|
+
throw new Error(
|
|
3369
|
+
"dappStorageId is required for buildSettleWritesUserPaysTx. Pass it directly or set it in the Dubhe constructor."
|
|
3370
|
+
);
|
|
3371
|
+
}
|
|
3372
|
+
const typeArg = this.dappKey;
|
|
3373
|
+
if (!typeArg) {
|
|
3374
|
+
throw new Error(
|
|
3375
|
+
'dappKey is required for buildSettleWritesUserPaysTx. Set it in the Dubhe constructor ({ packageId: "0x..." }).'
|
|
3376
|
+
);
|
|
3377
|
+
}
|
|
3378
|
+
const [paymentCoin] = tx.splitCoins(tx.gas, [tx.pure.u64(maxPaymentMist)]);
|
|
3379
|
+
const changeCoin = tx.moveCall({
|
|
3380
|
+
target: `${fwPkg}::dapp_system::settle_writes_user_pays`,
|
|
3381
|
+
typeArguments: [typeArg, coinType],
|
|
3382
|
+
arguments: [tx.object(dappHubId), tx.object(storageId), tx.object(userStorageId), paymentCoin]
|
|
3383
|
+
});
|
|
3384
|
+
tx.mergeCoins(tx.gas, [changeCoin]);
|
|
3385
|
+
}
|
|
3386
|
+
/**
|
|
3387
|
+
* Settle accumulated write debt where the user pays directly (USER_PAYS mode).
|
|
3388
|
+
*
|
|
3389
|
+
* Wraps `dapp_system::settle_writes_user_pays<DappKey, CoinType>`. The exact
|
|
3390
|
+
* cost is computed on-chain; any overpayment from the coin is returned to the
|
|
3391
|
+
* sender after settlement.
|
|
3392
|
+
*
|
|
3393
|
+
* Aborts if:
|
|
3394
|
+
* - The DApp is not in USER_PAYS mode (`wrong_settlement_mode`)
|
|
3395
|
+
* - The coin type does not match the accepted type (`wrong_payment_coin_type`)
|
|
3396
|
+
* - The coin value is less than the total cost (`insufficient_credit`)
|
|
3397
|
+
*
|
|
3398
|
+
* @param dappHubId Object ID of the DappHub shared object.
|
|
3399
|
+
* @param dappStorageId Object ID of the DApp's DappStorage shared object.
|
|
3400
|
+
* @param userStorageId Object ID of the user's UserStorage shared object.
|
|
3401
|
+
* @param coinObjectId Object ID of the Coin to use as payment.
|
|
3402
|
+
* @param coinType Move type of the coin (default: "0x2::sui::SUI").
|
|
3403
|
+
*/
|
|
3404
|
+
async settleWritesUserPays({
|
|
3405
|
+
dappHubId,
|
|
3406
|
+
dappStorageId: dappStorageIdParam,
|
|
3407
|
+
userStorageId,
|
|
3408
|
+
coinObjectId,
|
|
3409
|
+
coinType = "0x2::sui::SUI",
|
|
3410
|
+
derivePathParams,
|
|
3411
|
+
onSuccess,
|
|
3412
|
+
onError
|
|
3413
|
+
}) {
|
|
3414
|
+
const fwPkg = this.frameworkPackageId;
|
|
3415
|
+
if (!fwPkg) {
|
|
3416
|
+
throw new Error(
|
|
3417
|
+
'frameworkPackageId is required for settleWritesUserPays. Set it in the Dubhe constructor ({ frameworkPackageId: "0x..." }).'
|
|
3418
|
+
);
|
|
3419
|
+
}
|
|
3420
|
+
const storageId = dappStorageIdParam ?? this.dappStorageId;
|
|
3421
|
+
if (!storageId) {
|
|
3422
|
+
throw new Error(
|
|
3423
|
+
"dappStorageId is required for settleWritesUserPays. Pass it directly or set it in the Dubhe constructor."
|
|
3424
|
+
);
|
|
3425
|
+
}
|
|
3426
|
+
const typeArg = this.dappKey;
|
|
3427
|
+
if (!typeArg) {
|
|
3428
|
+
throw new Error(
|
|
3429
|
+
'dappKey is required for settleWritesUserPays. Set it in the Dubhe constructor ({ packageId: "0x..." }).'
|
|
3430
|
+
);
|
|
3431
|
+
}
|
|
3432
|
+
const tx = new import_transactions3.Transaction();
|
|
3433
|
+
tx.moveCall({
|
|
3434
|
+
target: `${fwPkg}::dapp_system::settle_writes_user_pays`,
|
|
3435
|
+
typeArguments: [typeArg, coinType],
|
|
3436
|
+
arguments: [
|
|
3437
|
+
tx.object(dappHubId),
|
|
3438
|
+
tx.object(storageId),
|
|
3439
|
+
tx.object(userStorageId),
|
|
3440
|
+
tx.object(coinObjectId)
|
|
3441
|
+
]
|
|
3442
|
+
});
|
|
3443
|
+
return this.signAndSendTxn({
|
|
3444
|
+
tx,
|
|
3445
|
+
derivePathParams,
|
|
3446
|
+
onSuccess,
|
|
3447
|
+
onError
|
|
3448
|
+
});
|
|
3449
|
+
}
|
|
3450
|
+
/**
|
|
3451
|
+
* Query `WritesSettled` events from the Sui full node.
|
|
3452
|
+
*
|
|
3453
|
+
* Returns the most recent settlement events across ALL users of this DApp.
|
|
3454
|
+
* Each event captures one user's write debt settlement: number of writes,
|
|
3455
|
+
* bytes, and the amount charged (`paid_cost`).
|
|
3456
|
+
*
|
|
3457
|
+
* Note: Sui full nodes may prune old events. For long-term history use a
|
|
3458
|
+
* dedicated indexer table (`writes_settled_history`).
|
|
3459
|
+
*
|
|
3460
|
+
* @param limit Max events to return (default: 30, max: 50).
|
|
3461
|
+
* @param cursor Pagination cursor from a previous response.
|
|
3462
|
+
*/
|
|
3463
|
+
async queryWritesSettledEvents(limit = 30, cursor) {
|
|
3464
|
+
const fwPkg = this.frameworkPackageId;
|
|
3465
|
+
if (!fwPkg)
|
|
3466
|
+
return [];
|
|
3467
|
+
try {
|
|
3468
|
+
const resp = await this.suiInteractor.currentClient.queryEvents({
|
|
3469
|
+
query: { MoveEventType: `${fwPkg}::dubhe_events::WritesSettled` },
|
|
3470
|
+
cursor,
|
|
3471
|
+
limit: Math.min(limit, 50),
|
|
3472
|
+
order: "descending"
|
|
3473
|
+
});
|
|
3474
|
+
return (resp.data ?? []).map((ev) => {
|
|
3475
|
+
const p = ev.parsedJson ?? {};
|
|
3476
|
+
return {
|
|
3477
|
+
txDigest: ev.id?.txDigest ?? "",
|
|
3478
|
+
eventSeq: ev.id?.eventSeq ?? "",
|
|
3479
|
+
timestampMs: Number(ev.timestampMs ?? 0),
|
|
3480
|
+
dappKey: p.dapp_key ?? "",
|
|
3481
|
+
account: p.account ?? "",
|
|
3482
|
+
writes: Number(p.writes ?? 0),
|
|
3483
|
+
bytes: String(p.bytes ?? "0"),
|
|
3484
|
+
freeCost: String(p.free_cost ?? "0"),
|
|
3485
|
+
paidCost: String(p.paid_cost ?? "0")
|
|
3486
|
+
};
|
|
3487
|
+
});
|
|
3488
|
+
} catch {
|
|
3489
|
+
return [];
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3492
|
+
/**
|
|
3493
|
+
* Query `MarketplaceFeeSettled` events from the Sui full node.
|
|
3494
|
+
*
|
|
3495
|
+
* Each event records the fee split for a completed marketplace purchase:
|
|
3496
|
+
* total fee, framework treasury share, and DApp revenue share.
|
|
3497
|
+
*
|
|
3498
|
+
* @param limit Max events to return (default: 30, max: 50).
|
|
3499
|
+
* @param cursor Pagination cursor from a previous response.
|
|
3500
|
+
*/
|
|
3501
|
+
async queryMarketplaceFeeSettledEvents(limit = 30, cursor) {
|
|
3502
|
+
const fwPkg = this.frameworkPackageId;
|
|
3503
|
+
if (!fwPkg)
|
|
3504
|
+
return [];
|
|
3505
|
+
try {
|
|
3506
|
+
const resp = await this.suiInteractor.currentClient.queryEvents({
|
|
3507
|
+
query: { MoveEventType: `${fwPkg}::dubhe_events::MarketplaceFeeSettled` },
|
|
3508
|
+
cursor,
|
|
3509
|
+
limit: Math.min(limit, 50),
|
|
3510
|
+
order: "descending"
|
|
3511
|
+
});
|
|
3512
|
+
return (resp.data ?? []).map((ev) => {
|
|
3513
|
+
const p = ev.parsedJson ?? {};
|
|
3514
|
+
return {
|
|
3515
|
+
txDigest: ev.id?.txDigest ?? "",
|
|
3516
|
+
eventSeq: ev.id?.eventSeq ?? "",
|
|
3517
|
+
timestampMs: Number(ev.timestampMs ?? 0),
|
|
3518
|
+
dappKey: p.dapp_key ?? "",
|
|
3519
|
+
listingId: p.listing_id ?? "",
|
|
3520
|
+
coinType: p.coin_type ?? "",
|
|
3521
|
+
totalFee: String(p.total_fee ?? "0"),
|
|
3522
|
+
treasuryAmount: String(p.treasury_amount ?? "0"),
|
|
3523
|
+
dappAmount: String(p.dapp_amount ?? "0")
|
|
3524
|
+
};
|
|
3525
|
+
});
|
|
3526
|
+
} catch {
|
|
3527
|
+
return [];
|
|
3528
|
+
}
|
|
3529
|
+
}
|
|
3530
|
+
/**
|
|
3531
|
+
* Read the DApp's accumulated revenue balance directly from the chain.
|
|
3532
|
+
*
|
|
3533
|
+
* `dapp_revenue` is stored as a dynamic field (`DappRevenueKey<CoinType>`)
|
|
3534
|
+
* on the DappStorage object — it is NOT a top-level field, so `getObject`
|
|
3535
|
+
* alone cannot return it. This method calls `getDynamicFields` to locate
|
|
3536
|
+
* the correct child object and returns its balance.
|
|
3537
|
+
*
|
|
3538
|
+
* Returns 0n when the dynamic field has never been created (no revenue yet).
|
|
3539
|
+
*
|
|
3540
|
+
* @param dappStorageId Object ID of the DApp's DappStorage shared object.
|
|
3541
|
+
* @param coinType Move type of the revenue coin (default: "0x2::sui::SUI").
|
|
3542
|
+
*/
|
|
3543
|
+
async getDappRevenueBalance(dappStorageId, coinType = "0x2::sui::SUI") {
|
|
3544
|
+
const client = this.suiInteractor.currentClient;
|
|
3545
|
+
const coinTail = coinType.split("::").slice(-2).join("::");
|
|
3546
|
+
try {
|
|
3547
|
+
let cursor = void 0;
|
|
3548
|
+
do {
|
|
3549
|
+
const page = await client.getDynamicFields({
|
|
3550
|
+
parentId: dappStorageId,
|
|
3551
|
+
cursor,
|
|
3552
|
+
limit: 50
|
|
3553
|
+
});
|
|
3554
|
+
for (const field of page.data) {
|
|
3555
|
+
const nameType = field.name?.type ?? "";
|
|
3556
|
+
if (nameType.includes("DappRevenueKey") && nameType.includes(coinTail)) {
|
|
3557
|
+
const fieldObj = await client.getDynamicFieldObject({
|
|
3558
|
+
parentId: dappStorageId,
|
|
3559
|
+
name: field.name
|
|
3560
|
+
});
|
|
3561
|
+
const innerFields = fieldObj?.data?.content?.fields ?? {};
|
|
3562
|
+
const balValue = innerFields?.value?.fields?.value ?? // nested Balance struct
|
|
3563
|
+
innerFields?.value ?? 0;
|
|
3564
|
+
return BigInt(balValue);
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
cursor = page.nextCursor;
|
|
3568
|
+
if (!page.hasNextPage)
|
|
3569
|
+
break;
|
|
3570
|
+
} while (cursor);
|
|
3571
|
+
} catch {
|
|
3572
|
+
}
|
|
3573
|
+
return 0n;
|
|
3574
|
+
}
|
|
3575
|
+
/**
|
|
3576
|
+
* Withdraw all accumulated DApp revenue to the DApp admin address.
|
|
3577
|
+
*
|
|
3578
|
+
* Wraps `dapp_system::withdraw_dapp_revenue<DappKey, CoinType>`.
|
|
3579
|
+
* The entire revenue balance is transferred to the DApp admin on-chain.
|
|
3580
|
+
* Aborts if the balance is zero (`no_revenue_to_withdraw`).
|
|
3581
|
+
*
|
|
3582
|
+
* @param dappHubId Object ID of the DappHub shared object.
|
|
3583
|
+
* @param dappStorageId Object ID of the DApp's DappStorage shared object.
|
|
3584
|
+
* @param coinType Move type of the revenue coin (default: "0x2::sui::SUI").
|
|
3585
|
+
*/
|
|
3586
|
+
async withdrawDappRevenue({
|
|
3587
|
+
dappHubId,
|
|
3588
|
+
dappStorageId: dappStorageIdParam,
|
|
3589
|
+
coinType = "0x2::sui::SUI",
|
|
3590
|
+
derivePathParams,
|
|
3591
|
+
onSuccess,
|
|
3592
|
+
onError
|
|
3593
|
+
}) {
|
|
3594
|
+
const fwPkg = this.frameworkPackageId;
|
|
3595
|
+
if (!fwPkg) {
|
|
3596
|
+
throw new Error(
|
|
3597
|
+
'frameworkPackageId is required for withdrawDappRevenue. Set it in the Dubhe constructor ({ frameworkPackageId: "0x..." }).'
|
|
3598
|
+
);
|
|
3599
|
+
}
|
|
3600
|
+
const storageId = dappStorageIdParam ?? this.dappStorageId;
|
|
3601
|
+
if (!storageId) {
|
|
3602
|
+
throw new Error(
|
|
3603
|
+
"dappStorageId is required for withdrawDappRevenue. Pass it directly or set it in the Dubhe constructor."
|
|
3604
|
+
);
|
|
3605
|
+
}
|
|
3606
|
+
const typeArg = this.dappKey;
|
|
3607
|
+
if (!typeArg) {
|
|
3608
|
+
throw new Error(
|
|
3609
|
+
'dappKey is required for withdrawDappRevenue. Set it in the Dubhe constructor ({ packageId: "0x..." }).'
|
|
3610
|
+
);
|
|
3611
|
+
}
|
|
3612
|
+
const tx = new import_transactions3.Transaction();
|
|
3613
|
+
tx.moveCall({
|
|
3614
|
+
target: `${fwPkg}::dapp_system::withdraw_dapp_revenue`,
|
|
3615
|
+
typeArguments: [typeArg, coinType],
|
|
3616
|
+
arguments: [tx.object(dappHubId), tx.object(storageId)]
|
|
3617
|
+
});
|
|
3618
|
+
return this.signAndSendTxn({
|
|
3619
|
+
tx,
|
|
3620
|
+
derivePathParams,
|
|
3621
|
+
onSuccess,
|
|
3622
|
+
onError
|
|
3623
|
+
});
|
|
3624
|
+
}
|
|
3625
|
+
/**
|
|
3626
|
+
* Return a combined settlement health snapshot for a user.
|
|
3627
|
+
*
|
|
3628
|
+
* Fetches `UserStorage` fields (always) and optionally `DappStorage` fields
|
|
3629
|
+
* (when `dappStorageId` is supplied) to produce high-level diagnostic values:
|
|
3630
|
+
*
|
|
3631
|
+
* - `utilizationPct` — how close the user is to the 2 000-write hard limit
|
|
3632
|
+
* - `isAtRisk` — utilization >= 80 % (settlement recommended soon)
|
|
3633
|
+
* - `isBlocked` — utilization >= 100 % (writes will be rejected)
|
|
3634
|
+
* - `estimatedWritesAffordable` — writes the credit pool can still cover
|
|
3635
|
+
* - `creditAtRisk` — fewer than 500 writes of credit remaining
|
|
3636
|
+
*
|
|
3637
|
+
* @param userStorageId Object ID of the user's UserStorage shared object.
|
|
3638
|
+
* @param dappStorageId Object ID of the DApp's DappStorage (optional).
|
|
3639
|
+
*/
|
|
3640
|
+
async getSettlementHealth({
|
|
3641
|
+
userStorageId,
|
|
3642
|
+
dappStorageId
|
|
3643
|
+
}) {
|
|
3644
|
+
const WRITE_LIMIT = 2000n;
|
|
3645
|
+
const userFields = await this.getUserStorageFields(userStorageId);
|
|
3646
|
+
const unsettledCount = userFields.unsettled_count;
|
|
3647
|
+
const utilizationPct = Number(unsettledCount * 100n / WRITE_LIMIT);
|
|
3648
|
+
const result = {
|
|
3649
|
+
unsettledCount,
|
|
3650
|
+
writeLimit: WRITE_LIMIT,
|
|
3651
|
+
utilizationPct,
|
|
3652
|
+
isAtRisk: utilizationPct >= 80,
|
|
3653
|
+
isBlocked: utilizationPct >= 100
|
|
3654
|
+
};
|
|
3655
|
+
if (dappStorageId) {
|
|
3656
|
+
const dappFields = await this.getDappStorageFields(dappStorageId);
|
|
3657
|
+
const creditPool = dappFields.credit_pool;
|
|
3658
|
+
const baseFeePerWrite = dappFields.base_fee_per_write;
|
|
3659
|
+
const estimatedWritesAffordable = baseFeePerWrite > 0n ? creditPool / baseFeePerWrite : BigInt(Number.MAX_SAFE_INTEGER);
|
|
3660
|
+
result.creditPool = creditPool;
|
|
3661
|
+
result.baseFeePerWrite = baseFeePerWrite;
|
|
3662
|
+
result.estimatedWritesAffordable = estimatedWritesAffordable;
|
|
3663
|
+
result.creditAtRisk = estimatedWritesAffordable < 500n;
|
|
3664
|
+
}
|
|
3665
|
+
return result;
|
|
3666
|
+
}
|
|
3667
|
+
// ─── Private helpers ─────────────────────────────────────────────────────────
|
|
3668
|
+
/**
|
|
3669
|
+
* Return the default network configuration for the given network type.
|
|
3670
|
+
*
|
|
3671
|
+
* Useful for reading `frameworkPackageId` without instantiating a full client:
|
|
3672
|
+
*
|
|
3673
|
+
* ```ts
|
|
3674
|
+
* const { frameworkPackageId } = Dubhe.getDefaultConfig('testnet');
|
|
3675
|
+
* // → '0x8817b...' (known constant for testnet/mainnet)
|
|
3676
|
+
* // → undefined (for localnet/devnet — supply after local deployment)
|
|
3677
|
+
* ```
|
|
3678
|
+
*/
|
|
3679
|
+
static getDefaultConfig(networkType) {
|
|
3680
|
+
return getDefaultConfig(networkType);
|
|
3681
|
+
}
|
|
2876
3682
|
// async formatData(type: string, value: Buffer | number[] | Uint8Array) {
|
|
2877
3683
|
// const u8Value = Uint8Array.from(value);
|
|
2878
3684
|
// return bcs.de(type, u8Value);
|
|
@@ -2979,11 +3785,17 @@ async function loadMetadata(networkType, packageId, fullnodeUrls) {
|
|
|
2979
3785
|
0 && (module.exports = {
|
|
2980
3786
|
BcsType,
|
|
2981
3787
|
Dubhe,
|
|
3788
|
+
MAINNET_DUBHE_FRAMEWORK_PACKAGE_ID,
|
|
3789
|
+
MAINNET_DUBHE_HUB_OBJECT_ID,
|
|
2982
3790
|
MultiSigClient,
|
|
2983
3791
|
SuiAccountManager,
|
|
2984
3792
|
SuiContractFactory,
|
|
2985
3793
|
SuiTx,
|
|
3794
|
+
TESTNET_DUBHE_FRAMEWORK_PACKAGE_ID,
|
|
3795
|
+
TESTNET_DUBHE_HUB_OBJECT_ID,
|
|
2986
3796
|
bcs,
|
|
3797
|
+
getDefaultConfig,
|
|
3798
|
+
getDefaultURL,
|
|
2987
3799
|
loadMetadata,
|
|
2988
3800
|
...require("@mysten/sui/client"),
|
|
2989
3801
|
...require("@mysten/sui/utils"),
|