dipcoin-cli 1.3.3 → 1.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -12921,8 +12921,8 @@ async function signMessage(keypair, messageBytes) {
12921
12921
  */
12922
12922
  function buildSignature(signature, isKeyPair = false) {
12923
12923
  const signatureData = cryptography.parseSerializedSignature(signature);
12924
- let signatureHex = buffer.Buffer.from(signatureData.signature).toString("hex");
12925
- let publicKey = buffer.Buffer.from(signatureData.publicKey).toString("base64");
12924
+ const signatureHex = buffer.Buffer.from(signatureData.signature).toString("hex");
12925
+ const publicKey = buffer.Buffer.from(signatureData.publicKey).toString("base64");
12926
12926
  let flag = SignerTypes.KP_SECP256;
12927
12927
  if (signatureData.signatureScheme === "Secp256k1" ||
12928
12928
  signatureData.signatureScheme === "Secp256r1") {
@@ -13006,13 +13006,11 @@ function getModuleDir() {
13006
13006
  return path__default["default"].dirname(__filename);
13007
13007
  }
13008
13008
  }
13009
- catch (e) {
13009
+ catch {
13010
13010
  // Fall through to CommonJS
13011
13011
  }
13012
13012
  // CommonJS: use __dirname (will be available after compilation)
13013
- // @ts-ignore - __dirname may not be defined in ESM context
13014
13013
  if (typeof __dirname !== "undefined") {
13015
- // @ts-ignore
13016
13014
  return __dirname;
13017
13015
  }
13018
13016
  // Fallback to process.cwd()
@@ -16457,8 +16455,12 @@ class HttpClient {
16457
16455
  const proc = child_process.spawn("curl", args);
16458
16456
  let stdout = "";
16459
16457
  let stderr = "";
16460
- proc.stdout.on("data", (chunk) => { stdout += chunk; });
16461
- proc.stderr.on("data", (chunk) => { stderr += chunk; });
16458
+ proc.stdout.on("data", (chunk) => {
16459
+ stdout += chunk;
16460
+ });
16461
+ proc.stderr.on("data", (chunk) => {
16462
+ stderr += chunk;
16463
+ });
16462
16464
  proc.on("close", (code) => {
16463
16465
  if (!stdout) {
16464
16466
  return reject(new Error(stderr || `curl exited with code ${code}`));
@@ -16653,21 +16655,45 @@ function buildAddMarginTx(deployment, args, tx, gasBudget, sender) {
16653
16655
  if (sender)
16654
16656
  t.setSender(sender);
16655
16657
  const packageId = getPackageId(deployment);
16656
- t.moveCall({
16657
- target: `${packageId}::exchange::add_margin_v3`,
16658
- arguments: [
16659
- t.object(getProtocolConfigId(deployment)),
16660
- t.object(CLOCK_OBJECT_ID),
16661
- t.object(args.perpID || getPerpetualId(deployment, args.market)),
16662
- t.object(getBankId(deployment)),
16663
- t.object(args.subAccountsMapID || getSubAccountsId(deployment)),
16664
- t.object(getTxIndexerId(deployment)),
16665
- t.object(getPriceFeedId(deployment)),
16666
- t.pure.address(args.account || sender || ""),
16667
- t.pure.u128(args.amount.toString()),
16668
- ],
16669
- typeArguments: [getCurrencyType(deployment)],
16670
- });
16658
+ const signed = args.signedPayload;
16659
+ if (signed) {
16660
+ t.moveCall({
16661
+ target: `${packageId}::exchange::add_margin_v4`,
16662
+ arguments: [
16663
+ t.object(getProtocolConfigId(deployment)),
16664
+ t.object(CLOCK_OBJECT_ID),
16665
+ t.object(args.perpID || getPerpetualId(deployment, args.market)),
16666
+ t.object(getBankId(deployment)),
16667
+ t.object(args.subAccountsMapID || getSubAccountsId(deployment)),
16668
+ t.object(getTxIndexerId(deployment)),
16669
+ t.object(getPriceFeedId(deployment)),
16670
+ t.pure.vector("u8", signed.userAddress),
16671
+ t.pure.u128(args.amount.toString()),
16672
+ t.pure.u128(signed.salt.toString()),
16673
+ t.pure.u64(signed.expiration.toString()),
16674
+ t.pure.vector("u8", signed.signature),
16675
+ t.pure.vector("u8", signed.publicKey),
16676
+ ],
16677
+ typeArguments: [getCurrencyType(deployment)],
16678
+ });
16679
+ }
16680
+ else {
16681
+ t.moveCall({
16682
+ target: `${packageId}::exchange::add_margin_v3`,
16683
+ arguments: [
16684
+ t.object(getProtocolConfigId(deployment)),
16685
+ t.object(CLOCK_OBJECT_ID),
16686
+ t.object(args.perpID || getPerpetualId(deployment, args.market)),
16687
+ t.object(getBankId(deployment)),
16688
+ t.object(args.subAccountsMapID || getSubAccountsId(deployment)),
16689
+ t.object(getTxIndexerId(deployment)),
16690
+ t.object(getPriceFeedId(deployment)),
16691
+ t.pure.address(args.account || sender || ""),
16692
+ t.pure.u128(args.amount.toString()),
16693
+ ],
16694
+ typeArguments: [getCurrencyType(deployment)],
16695
+ });
16696
+ }
16671
16697
  return t;
16672
16698
  }
16673
16699
  function buildRemoveMarginTx(deployment, args, tx, gasBudget, sender) {
@@ -16677,21 +16703,45 @@ function buildRemoveMarginTx(deployment, args, tx, gasBudget, sender) {
16677
16703
  if (sender)
16678
16704
  t.setSender(sender);
16679
16705
  const packageId = getPackageId(deployment);
16680
- t.moveCall({
16681
- target: `${packageId}::exchange::remove_margin_v3`,
16682
- arguments: [
16683
- t.object(getProtocolConfigId(deployment)),
16684
- t.object(CLOCK_OBJECT_ID),
16685
- t.object(args.perpID || getPerpetualId(deployment, args.market)),
16686
- t.object(getBankId(deployment)),
16687
- t.object(args.subAccountsMapID || getSubAccountsId(deployment)),
16688
- t.object(getTxIndexerId(deployment)),
16689
- t.object(getPriceFeedId(deployment)),
16690
- t.pure.address(args.account || sender || ""),
16691
- t.pure.u128(args.amount.toString()),
16692
- ],
16693
- typeArguments: [getCurrencyType(deployment)],
16694
- });
16706
+ const signed = args.signedPayload;
16707
+ if (signed) {
16708
+ t.moveCall({
16709
+ target: `${packageId}::exchange::remove_margin_v4`,
16710
+ arguments: [
16711
+ t.object(getProtocolConfigId(deployment)),
16712
+ t.object(CLOCK_OBJECT_ID),
16713
+ t.object(args.perpID || getPerpetualId(deployment, args.market)),
16714
+ t.object(getBankId(deployment)),
16715
+ t.object(args.subAccountsMapID || getSubAccountsId(deployment)),
16716
+ t.object(getTxIndexerId(deployment)),
16717
+ t.object(getPriceFeedId(deployment)),
16718
+ t.pure.vector("u8", signed.userAddress),
16719
+ t.pure.u128(args.amount.toString()),
16720
+ t.pure.u128(signed.salt.toString()),
16721
+ t.pure.u64(signed.expiration.toString()),
16722
+ t.pure.vector("u8", signed.signature),
16723
+ t.pure.vector("u8", signed.publicKey),
16724
+ ],
16725
+ typeArguments: [getCurrencyType(deployment)],
16726
+ });
16727
+ }
16728
+ else {
16729
+ t.moveCall({
16730
+ target: `${packageId}::exchange::remove_margin_v3`,
16731
+ arguments: [
16732
+ t.object(getProtocolConfigId(deployment)),
16733
+ t.object(CLOCK_OBJECT_ID),
16734
+ t.object(args.perpID || getPerpetualId(deployment, args.market)),
16735
+ t.object(getBankId(deployment)),
16736
+ t.object(args.subAccountsMapID || getSubAccountsId(deployment)),
16737
+ t.object(getTxIndexerId(deployment)),
16738
+ t.object(getPriceFeedId(deployment)),
16739
+ t.pure.address(args.account || sender || ""),
16740
+ t.pure.u128(args.amount.toString()),
16741
+ ],
16742
+ typeArguments: [getCurrencyType(deployment)],
16743
+ });
16744
+ }
16695
16745
  return t;
16696
16746
  }
16697
16747
  function buildWithdrawTx(deployment, args, tx, gasBudget, sender) {
@@ -16758,17 +16808,87 @@ function buildSetOraclePriceTx(deployment, args, tx, gasBudget, sender) {
16758
16808
  return t;
16759
16809
  }
16760
16810
 
16811
+ // Copyright (c) 2025 Dipcoin LLC
16812
+ // SPDX-License-Identifier: Apache-2.0
16813
+ const SUI_COIN_TYPE = "0x2::sui::SUI";
16814
+ const STALE_GAS_RETRY_DELAY_MS = 1000;
16815
+ function sleep(ms) {
16816
+ return new Promise((resolve) => setTimeout(resolve, ms));
16817
+ }
16818
+ function getErrorMessage(error) {
16819
+ if (error instanceof Error)
16820
+ return error.message;
16821
+ return String(error);
16822
+ }
16823
+ function isStaleObjectVersionError(error) {
16824
+ const message = getErrorMessage(error);
16825
+ return (message.includes("Transaction needs to be rebuilt") ||
16826
+ message.includes("unavailable for consumption") ||
16827
+ message.includes("current version:"));
16828
+ }
16829
+ async function getFreshGasObjectRef(suiClient, owner) {
16830
+ const coins = [];
16831
+ let cursor;
16832
+ do {
16833
+ const page = await suiClient.getCoins({ owner, coinType: SUI_COIN_TYPE, cursor });
16834
+ coins.push(...page.data);
16835
+ cursor = page.hasNextPage ? page.nextCursor : null;
16836
+ } while (cursor);
16837
+ if (!coins.length) {
16838
+ throw new Error(`No SUI gas coins found for ${owner}`);
16839
+ }
16840
+ const gasCoin = coins.slice().sort((a, b) => {
16841
+ const aBalance = BigInt(a.balance);
16842
+ const bBalance = BigInt(b.balance);
16843
+ if (aBalance === bBalance)
16844
+ return 0;
16845
+ return aBalance < bBalance ? 1 : -1;
16846
+ })[0];
16847
+ const fresh = await suiClient.getObject({ id: gasCoin.coinObjectId });
16848
+ const ref = fresh.data;
16849
+ if (!ref?.objectId || !ref.version || !ref.digest) {
16850
+ throw new Error(`Failed to refresh gas coin object ref for ${gasCoin.coinObjectId}`);
16851
+ }
16852
+ return {
16853
+ objectId: ref.objectId,
16854
+ version: ref.version,
16855
+ digest: ref.digest,
16856
+ };
16857
+ }
16858
+ async function setFreshGasPayment(suiClient, tx, owner) {
16859
+ tx.setGasPayment([await getFreshGasObjectRef(suiClient, owner)]);
16860
+ }
16861
+ async function signAndExecuteTransactionWithFreshGas(suiClient, tx, signer, options) {
16862
+ const sender = signer.getPublicKey().toSuiAddress();
16863
+ tx.setSender(sender);
16864
+ await setFreshGasPayment(suiClient, tx, sender);
16865
+ for (let attempt = 0; attempt < 2; attempt++) {
16866
+ try {
16867
+ return await suiClient.signAndExecuteTransaction({
16868
+ signer,
16869
+ transaction: tx,
16870
+ options,
16871
+ });
16872
+ }
16873
+ catch (error) {
16874
+ if (attempt === 0 && isStaleObjectVersionError(error)) {
16875
+ await sleep(STALE_GAS_RETRY_DELAY_MS);
16876
+ await setFreshGasPayment(suiClient, tx, sender);
16877
+ continue;
16878
+ }
16879
+ throw error;
16880
+ }
16881
+ }
16882
+ throw new Error("Transaction failed after refreshing gas payment");
16883
+ }
16884
+
16761
16885
  // Copyright (c) 2025 Dipcoin LLC
16762
16886
  async function executeTxBlock(suiClient, tx, signer) {
16763
- return suiClient.signAndExecuteTransaction({
16764
- signer,
16765
- transaction: tx,
16766
- options: {
16767
- showObjectChanges: true,
16768
- showEffects: true,
16769
- showEvents: true,
16770
- showInput: true,
16771
- },
16887
+ return signAndExecuteTransactionWithFreshGas(suiClient, tx, signer, {
16888
+ showObjectChanges: true,
16889
+ showEffects: true,
16890
+ showEvents: true,
16891
+ showInput: true,
16772
16892
  });
16773
16893
  }
16774
16894
  function getDeploymentPerpetualID(deployment, market) {
@@ -16777,7 +16897,8 @@ function getDeploymentPerpetualID(deployment, market) {
16777
16897
  async function getOraclePrice(suiClient, deployment, market) {
16778
16898
  const objId = getPriceOracleObjectId(deployment, market);
16779
16899
  const obj = await suiClient.getObject({ id: objId, options: { showContent: true } });
16780
- const fields = obj.data?.content?.fields?.price_info?.fields?.price_feed?.fields?.price?.fields;
16900
+ const fields = obj.data?.content?.fields?.price_info?.fields?.price_feed?.fields?.price
16901
+ ?.fields;
16781
16902
  if (!fields)
16782
16903
  throw new Error(`Failed to read oracle price for ${market}`);
16783
16904
  return Number(fields.price.fields.magnitude) / Math.pow(10, Number(fields.expo.fields.magnitude));
@@ -17736,9 +17857,7 @@ class DipCoinPerpSDK {
17736
17857
  return {
17737
17858
  status: false,
17738
17859
  data: results,
17739
- error: results.tpResult?.message ||
17740
- results.slResult?.message ||
17741
- "Failed to place TP/SL order",
17860
+ error: results.tpResult?.message || results.slResult?.message || "Failed to place TP/SL order",
17742
17861
  };
17743
17862
  }
17744
17863
  catch (error) {
@@ -18307,12 +18426,8 @@ class DipCoinPerpSDK {
18307
18426
  tpOrderPrice: toNormal(raw.tpOrderPrice),
18308
18427
  slTriggerPrice: toNormal(raw.slTriggerPrice),
18309
18428
  slOrderPrice: toNormal(raw.slOrderPrice),
18310
- tpPlanId: planOrderType === "takeProfit"
18311
- ? raw.tpPlanId ?? raw.id ?? null
18312
- : raw.tpPlanId ?? null,
18313
- slPlanId: planOrderType === "stopLoss"
18314
- ? raw.slPlanId ?? raw.id ?? null
18315
- : raw.slPlanId ?? null,
18429
+ tpPlanId: planOrderType === "takeProfit" ? raw.tpPlanId ?? raw.id ?? null : raw.tpPlanId ?? null,
18430
+ slPlanId: planOrderType === "stopLoss" ? raw.slPlanId ?? raw.id ?? null : raw.slPlanId ?? null,
18316
18431
  tpslType: raw.tpslType,
18317
18432
  createdAt: raw.createdAt,
18318
18433
  updatedAt: raw.updatedAt,
@@ -18665,7 +18780,7 @@ class DipCoinPerpSDK {
18665
18780
  * Build ExchangeOnChain call args for margin adjustments
18666
18781
  */
18667
18782
  buildMarginCallArgs(params, action) {
18668
- const { amount, accountAddress, symbol, market, perpId, subAccountsMapId, gasBudget, txHash, } = params;
18783
+ const { amount, accountAddress, symbol, market, perpId, subAccountsMapId, gasBudget, txHash } = params;
18669
18784
  if (!this.isPositiveNumber(amount)) {
18670
18785
  throw new Error(`Amount must be greater than zero to ${action} margin`);
18671
18786
  }
@@ -18688,6 +18803,130 @@ class DipCoinPerpSDK {
18688
18803
  txHash,
18689
18804
  };
18690
18805
  }
18806
+ packPayload(fields) {
18807
+ let result = "{\n";
18808
+ for (const [key, value] of fields) {
18809
+ result += `"${key}":"${value}",\n`;
18810
+ }
18811
+ result += '"domain":"dipcoin.io"\n';
18812
+ result += "}";
18813
+ return result;
18814
+ }
18815
+ normalizeSuiAddress(address) {
18816
+ const hex = address.startsWith("0x") ? address.slice(2) : address;
18817
+ if (!/^[0-9a-fA-F]+$/.test(hex) || hex.length > 64) {
18818
+ throw new Error(`Invalid Sui address: ${address}`);
18819
+ }
18820
+ return `0x${hex.toLowerCase().padStart(64, "0")}`;
18821
+ }
18822
+ toUnifiedSuiAddress(address) {
18823
+ const normalized = this.normalizeSuiAddress(address);
18824
+ return `Sui:0x${normalized.slice(2)}`;
18825
+ }
18826
+ toUnifiedSuiAddressBytes(address) {
18827
+ const normalized = this.normalizeSuiAddress(address);
18828
+ return [0, ...Array.from(Buffer.from(normalized.slice(2), "hex"))];
18829
+ }
18830
+ async signDipcoinActionPayload(fields, addressForVector) {
18831
+ const salt = Date.now();
18832
+ const expiration = salt + 1_800_000;
18833
+ const message = this.packPayload([
18834
+ ...fields,
18835
+ ["salt", String(salt)],
18836
+ ["expiration", String(expiration)],
18837
+ ]);
18838
+ const signed = await this.keypair.signPersonalMessage(new TextEncoder().encode(message));
18839
+ const parsed = cryptography.parseSerializedSignature(typeof signed === "string" ? signed : signed.signature);
18840
+ const scheme = String(parsed.signatureScheme);
18841
+ const flag = /ed25519/i.test(scheme) ? 1 : 0;
18842
+ if (!/ed25519|secp256k1|secp256r1/i.test(scheme)) {
18843
+ throw new Error(`Unsupported signature scheme for signed payload: ${scheme}`);
18844
+ }
18845
+ return {
18846
+ userAddress: this.toUnifiedSuiAddressBytes(addressForVector),
18847
+ salt: String(salt),
18848
+ expiration: String(expiration),
18849
+ signature: [...Array.from(Buffer.from(parsed.signature)), flag],
18850
+ publicKey: Array.from(Buffer.from(parsed.publicKey)),
18851
+ };
18852
+ }
18853
+ async signUserAmountActionPayload(args) {
18854
+ return this.signDipcoinActionPayload([
18855
+ ["action", args.action],
18856
+ ["market", this.normalizeSuiAddress(args.market)],
18857
+ ["user", this.toUnifiedSuiAddress(args.user)],
18858
+ ["amount", args.amount],
18859
+ ], args.user);
18860
+ }
18861
+ async signVaultCreatePayload(args) {
18862
+ return this.signDipcoinActionPayload([
18863
+ ["action", "VaultCreate"],
18864
+ ["creator", this.toUnifiedSuiAddress(args.creator)],
18865
+ ["name", args.name],
18866
+ ["trader", this.toUnifiedSuiAddress(args.trader)],
18867
+ ["maxCap", args.maxCap],
18868
+ ["minDepositAmount", args.minDepositAmount],
18869
+ ["creatorMinimumShareRatio", args.creatorMinimumShareRatio],
18870
+ ["creatorProfitShareRatio", args.creatorProfitShareRatio],
18871
+ ["initialAmount", args.initialAmount],
18872
+ ], args.creator);
18873
+ }
18874
+ async signVaultUserAmountPayload(action, vaultID, user, amount) {
18875
+ return this.signDipcoinActionPayload([
18876
+ ["action", action],
18877
+ ["vault", this.normalizeSuiAddress(vaultID)],
18878
+ ["user", this.toUnifiedSuiAddress(user)],
18879
+ ["amount", amount],
18880
+ ], user);
18881
+ }
18882
+ async signVaultUserPayload(action, vaultID, user) {
18883
+ return this.signDipcoinActionPayload([
18884
+ ["action", action],
18885
+ ["vault", this.normalizeSuiAddress(vaultID)],
18886
+ ["user", this.toUnifiedSuiAddress(user)],
18887
+ ], user);
18888
+ }
18889
+ async signVaultSetTraderPayload(vaultID, creator, newTrader) {
18890
+ return this.signDipcoinActionPayload([
18891
+ ["action", "VaultSetTrader"],
18892
+ ["vault", this.normalizeSuiAddress(vaultID)],
18893
+ ["creator", this.toUnifiedSuiAddress(creator)],
18894
+ ["newTrader", this.toUnifiedSuiAddress(newTrader)],
18895
+ ], creator);
18896
+ }
18897
+ async signVaultSetSubTraderPayload(vaultID, trader, subTrader, status) {
18898
+ return this.signDipcoinActionPayload([
18899
+ ["action", "VaultSetSubTrader"],
18900
+ ["vault", this.normalizeSuiAddress(vaultID)],
18901
+ ["trader", this.toUnifiedSuiAddress(trader)],
18902
+ ["subTrader", this.toUnifiedSuiAddress(subTrader)],
18903
+ ["status", String(status)],
18904
+ ], trader);
18905
+ }
18906
+ async signVaultDepositStatusPayload(vaultID, creator, status) {
18907
+ return this.signDipcoinActionPayload([
18908
+ ["action", "VaultSetDepositStatus"],
18909
+ ["vault", this.normalizeSuiAddress(vaultID)],
18910
+ ["creator", this.toUnifiedSuiAddress(creator)],
18911
+ ["status", String(status)],
18912
+ ], creator);
18913
+ }
18914
+ async signVaultCreatorAmountPayload(action, vaultID, creator, amount) {
18915
+ return this.signDipcoinActionPayload([
18916
+ ["action", action],
18917
+ ["vault", this.normalizeSuiAddress(vaultID)],
18918
+ ["creator", this.toUnifiedSuiAddress(creator)],
18919
+ ["amount", amount],
18920
+ ], creator);
18921
+ }
18922
+ async signVaultAutoClosePayload(vaultID, creator, autoCloseOnWithdraw) {
18923
+ return this.signDipcoinActionPayload([
18924
+ ["action", "VaultSetAutoCloseOnWithdraw"],
18925
+ ["vault", this.normalizeSuiAddress(vaultID)],
18926
+ ["creator", this.toUnifiedSuiAddress(creator)],
18927
+ ["autoCloseOnWithdraw", String(autoCloseOnWithdraw)],
18928
+ ], creator);
18929
+ }
18691
18930
  getDeploymentProtocolConfigId() {
18692
18931
  const protocolId = this.deploymentConfig?.objects?.ProtocolConfig?.id;
18693
18932
  if (!protocolId) {
@@ -18697,6 +18936,17 @@ class DipCoinPerpSDK {
18697
18936
  }
18698
18937
  async buildMarginTransaction(params, action) {
18699
18938
  const payload = this.buildMarginCallArgs(params, action);
18939
+ const signedMarket = payload.perpID ||
18940
+ (payload.market ? this.resolvePerpIdFromDeployment(payload.market) : undefined);
18941
+ if (!signedMarket) {
18942
+ throw new Error("PerpetualID is required for signed margin payload");
18943
+ }
18944
+ const signedPayload = await this.signUserAmountActionPayload({
18945
+ action: action === "add" ? "AddMargin" : "RemoveMargin",
18946
+ market: signedMarket,
18947
+ user: payload.account,
18948
+ amount: payload.amount,
18949
+ });
18700
18950
  // v6+ requires PriceFeed to be fresh (~60s) before the margin call. Operator updates
18701
18951
  // are only hourly so we always fetch + prepend our own update_price_feed step.
18702
18952
  const sym = payload.market || params.symbol;
@@ -18706,9 +18956,9 @@ class DipCoinPerpSDK {
18706
18956
  const baseTx = new transactions.Transaction();
18707
18957
  this.appendPriceFeedUpdates(baseTx, Array.from(feeds.values()));
18708
18958
  if (action === "add") {
18709
- return buildAddMarginTx(this.deploymentConfig, payload, baseTx, params.gasBudget);
18959
+ return buildAddMarginTx(this.deploymentConfig, { ...payload, signedPayload }, baseTx, params.gasBudget);
18710
18960
  }
18711
- return buildRemoveMarginTx(this.deploymentConfig, payload, baseTx, params.gasBudget);
18961
+ return buildRemoveMarginTx(this.deploymentConfig, { ...payload, signedPayload }, baseTx, params.gasBudget);
18712
18962
  }
18713
18963
  async buildUpdatePriceTransaction(symbol) {
18714
18964
  if (!symbol) {
@@ -18924,7 +19174,10 @@ class DipCoinPerpSDK {
18924
19174
  if (!metadata) {
18925
19175
  return { status: false, error: "Coin metadata not found" };
18926
19176
  }
18927
- return { status: true, data: { decimals: metadata.decimals, symbol: metadata.symbol, name: metadata.name } };
19177
+ return {
19178
+ status: true,
19179
+ data: { decimals: metadata.decimals, symbol: metadata.symbol, name: metadata.name },
19180
+ };
18928
19181
  }
18929
19182
  catch (error) {
18930
19183
  return { status: false, error: formatError(error) };
@@ -18997,6 +19250,107 @@ class DipCoinPerpSDK {
18997
19250
  }
18998
19251
  return id;
18999
19252
  }
19253
+ sleep(ms) {
19254
+ return new Promise((resolve) => setTimeout(resolve, ms));
19255
+ }
19256
+ getSignedPriceFeedTimestamps(payload) {
19257
+ let offset = 0;
19258
+ let length = 0;
19259
+ let shift = 0;
19260
+ while (offset < payload.length) {
19261
+ const byte = payload[offset++];
19262
+ length |= (byte & 0x7f) << shift;
19263
+ if ((byte & 0x80) === 0)
19264
+ break;
19265
+ shift += 7;
19266
+ }
19267
+ if (length <= 0)
19268
+ return [];
19269
+ const timestamps = [];
19270
+ for (let i = 0; i < length; i++) {
19271
+ const timestampOffset = offset + 32 + 16;
19272
+ if (timestampOffset + 8 > payload.length)
19273
+ return [];
19274
+ const view = new DataView(payload.buffer, payload.byteOffset + timestampOffset, 8);
19275
+ timestamps.push(Number(view.getBigUint64(0, true)));
19276
+ offset = timestampOffset + 8 + 2;
19277
+ }
19278
+ return timestamps;
19279
+ }
19280
+ getSignedPriceFeedMaxTimestamp(payload) {
19281
+ const timestamps = this.getSignedPriceFeedTimestamps(payload);
19282
+ return timestamps.length ? Math.max(...timestamps) : undefined;
19283
+ }
19284
+ async waitForSignedPriceFeedClock(payload) {
19285
+ const timestamp = this.getSignedPriceFeedMaxTimestamp(payload);
19286
+ if (!timestamp)
19287
+ return;
19288
+ // price_feed::collect_price_updates accepts timestamps no more than 1500ms
19289
+ // ahead of Sui Clock and no more than 60000ms behind it. The DipCoin API can
19290
+ // sign with wall-clock time while Sui Clock lags, so wait for the chain clock
19291
+ // to catch up before submitting the PTB. Public RPC backends can disagree by
19292
+ // a few seconds, so target a timestamp that is already ~10s old.
19293
+ const start = Date.now();
19294
+ const deadline = start + 180_000;
19295
+ while (Date.now() <= deadline) {
19296
+ const clock = await this.suiClient.getObject({
19297
+ id: "0x6",
19298
+ options: { showContent: true },
19299
+ });
19300
+ const clockMs = Number(clock.data?.content?.fields?.timestamp_ms);
19301
+ if (!Number.isFinite(clockMs))
19302
+ return;
19303
+ const waitMs = timestamp - clockMs + 10_000;
19304
+ if (waitMs <= 0)
19305
+ return;
19306
+ if (Date.now() + waitMs > deadline) {
19307
+ throw new Error(`Signed price feed is too far ahead of Sui Clock (${Math.ceil(waitMs / 1000)}s). Try again later.`);
19308
+ }
19309
+ await this.sleep(Math.min(waitMs, 15_000));
19310
+ }
19311
+ }
19312
+ async waitForSignedPriceFeedEntries(entries) {
19313
+ const timestamps = entries.flatMap((entry) => this.getSignedPriceFeedTimestamps(entry.payload));
19314
+ if (!timestamps.length)
19315
+ return;
19316
+ const maxTimestamp = Math.max(...timestamps);
19317
+ const minTimestamp = Math.min(...timestamps);
19318
+ const start = Date.now();
19319
+ const deadline = start + 180_000;
19320
+ while (Date.now() <= deadline) {
19321
+ const clock = await this.suiClient.getObject({
19322
+ id: "0x6",
19323
+ options: { showContent: true },
19324
+ });
19325
+ const clockMs = Number(clock.data?.content?.fields?.timestamp_ms);
19326
+ if (!Number.isFinite(clockMs))
19327
+ return;
19328
+ const waitMs = maxTimestamp - clockMs + 10_000;
19329
+ if (waitMs <= 0) {
19330
+ if (clockMs - minTimestamp > 55_000) {
19331
+ throw new Error("Signed price feed expired while waiting for Sui Clock");
19332
+ }
19333
+ return;
19334
+ }
19335
+ if (Date.now() + waitMs > deadline) {
19336
+ throw new Error(`Signed price feed is too far ahead of Sui Clock (${Math.ceil(waitMs / 1000)}s). Try again later.`);
19337
+ }
19338
+ await this.sleep(Math.min(waitMs, 15_000));
19339
+ }
19340
+ }
19341
+ async requestSignedPriceFeedEntry(symbols) {
19342
+ const qs = symbols.map((s) => `symbols=${encodeURIComponent(s)}`).join("&");
19343
+ const resp = await this.httpClient.get(`${API_ENDPOINTS.SIGNED_PRICE_LATEST}?${qs}`);
19344
+ if (resp.code !== 200 || !resp.data) {
19345
+ throw new Error(`Failed to fetch signed price feed for [${symbols.join(",")}]: ${resp.message || `code ${resp.code}`}`);
19346
+ }
19347
+ const d = resp.data;
19348
+ const b64 = (s) => Uint8Array.from(Buffer.from(s, "base64"));
19349
+ if (!d.payload || !d.signature || !d.publicKey) {
19350
+ throw new Error("Signed price feed response missing payload/signature/publicKey");
19351
+ }
19352
+ return { payload: b64(d.payload), signature: b64(d.signature), publicKey: b64(d.publicKey) };
19353
+ }
19000
19354
  /**
19001
19355
  * Fetch signed price update bytes for the given markets from the DipCoin pricing service.
19002
19356
  * Each entry's payload/signature/publicKey is consumed by
@@ -19010,22 +19364,72 @@ class DipCoinPerpSDK {
19010
19364
  const out = new Map();
19011
19365
  if (symbols.length === 0)
19012
19366
  return out;
19013
- const qs = symbols.map((s) => `symbols=${encodeURIComponent(s)}`).join("&");
19014
- const resp = await this.httpClient.get(`${API_ENDPOINTS.SIGNED_PRICE_LATEST}?${qs}`);
19015
- if (resp.code !== 200 || !resp.data) {
19016
- throw new Error(`Failed to fetch signed price feed for [${symbols.join(",")}]: ${resp.message || `code ${resp.code}`}`);
19367
+ let lastClockError;
19368
+ for (let attempt = 0; attempt < 5; attempt++) {
19369
+ const entry = await this.requestSignedPriceFeedEntry(symbols);
19370
+ try {
19371
+ await this.waitForSignedPriceFeedEntries([entry]);
19372
+ }
19373
+ catch (error) {
19374
+ const message = String(error?.message || error);
19375
+ if (!message.includes("too far ahead") && !message.includes("expired")) {
19376
+ throw error;
19377
+ }
19378
+ lastClockError = error;
19379
+ await this.sleep(2_000 * (attempt + 1));
19380
+ continue;
19381
+ }
19382
+ // API returns a single bundle covering all requested symbols' prices in priceList,
19383
+ // but only one (payload, signature, publicKey) tuple. Map every requested symbol to it.
19384
+ for (const sym of symbols)
19385
+ out.set(sym, entry);
19386
+ return out;
19017
19387
  }
19018
- const d = resp.data;
19019
- const b64 = (s) => Uint8Array.from(Buffer.from(s, "base64"));
19020
- if (!d.payload || !d.signature || !d.publicKey) {
19021
- throw new Error("Signed price feed response missing payload/signature/publicKey");
19388
+ throw lastClockError || new Error("Failed to fetch a usable signed price feed");
19389
+ }
19390
+ async fetchSignedPriceFeedChunks(symbols, chunkSize = 20) {
19391
+ if (symbols.length === 0)
19392
+ return [];
19393
+ let lastClockError;
19394
+ for (let attempt = 0; attempt < 5; attempt++) {
19395
+ const entries = [];
19396
+ for (let i = 0; i < symbols.length; i += chunkSize) {
19397
+ entries.push(await this.requestSignedPriceFeedEntry(symbols.slice(i, i + chunkSize)));
19398
+ }
19399
+ try {
19400
+ await this.waitForSignedPriceFeedEntries(entries);
19401
+ await this.validateSignedPriceFeedEntries(entries);
19402
+ }
19403
+ catch (error) {
19404
+ const message = String(error?.message || error);
19405
+ if (!message.includes("too far ahead") &&
19406
+ !message.includes("expired") &&
19407
+ !message.includes("price_feed")) {
19408
+ throw error;
19409
+ }
19410
+ lastClockError = error;
19411
+ await this.sleep(2_000 * (attempt + 1));
19412
+ continue;
19413
+ }
19414
+ return Array.from(new Map(entries.map((feed) => [Buffer.from(feed.payload).toString("hex"), feed])).values());
19022
19415
  }
19023
- const entry = { payload: b64(d.payload), signature: b64(d.signature), publicKey: b64(d.publicKey) };
19024
- // API returns a single bundle covering all requested symbols' prices in priceList,
19025
- // but only one (payload, signature, publicKey) tuple. Map every requested symbol to it.
19026
- for (const sym of symbols)
19027
- out.set(sym, entry);
19028
- return out;
19416
+ throw lastClockError || new Error("Failed to fetch usable signed price feed chunks");
19417
+ }
19418
+ async validateSignedPriceFeedEntries(entries) {
19419
+ if (!entries.length)
19420
+ return;
19421
+ const tx = new transactions.Transaction();
19422
+ this.appendPriceFeedUpdates(tx, entries);
19423
+ tx.setSender(this.walletAddress);
19424
+ const result = await this.suiClient.devInspectTransactionBlock({
19425
+ sender: this.walletAddress,
19426
+ transactionBlock: tx,
19427
+ });
19428
+ const status = result.effects?.status?.status;
19429
+ if (status === "success")
19430
+ return;
19431
+ const error = result.effects?.status?.error || result.error || "unknown price_feed error";
19432
+ throw new Error(`price_feed validation failed: ${error}`);
19029
19433
  }
19030
19434
  /**
19031
19435
  * Build a `price_feed::update_price_feed` PTB command for each provided signed feed
@@ -19079,11 +19483,16 @@ class DipCoinPerpSDK {
19079
19483
  // a single update_price_feed step. Then iterate the on-chain perpetual_registry (which is
19080
19484
  // what new_vault_nav populates, and is_nav_filled requires every entry to be computed).
19081
19485
  const priceFeedId = this.getPriceFeedId();
19082
- const allSymbols = this.getMarketSymbols();
19083
- const feeds = await this.fetchSignedPriceFeeds(allSymbols);
19084
- // Dedup the bundle entries (the API returns one tuple covering all requested symbols).
19085
- const uniqFeeds = Array.from(new Map(Array.from(feeds.values()).map((f) => [Buffer.from(f.payload).toString("hex"), f])).values());
19086
- this.appendPriceFeedUpdates(t, uniqFeeds);
19486
+ const configSymbols = this.getMarketSymbols();
19487
+ const configSymbolSet = new Set(configSymbols);
19488
+ const apiSymbols = (await this.getCachedTradingPairs())
19489
+ .map((pair) => pair.symbol)
19490
+ .filter((symbol) => Boolean(symbol) && !configSymbolSet.has(symbol));
19491
+ const updateFeeds = [
19492
+ ...(await this.fetchSignedPriceFeedChunks(configSymbols, 40)),
19493
+ ...(await this.fetchSignedPriceFeedChunks(apiSymbols, 40)),
19494
+ ];
19495
+ this.appendPriceFeedUpdates(t, updateFeeds);
19087
19496
  const perpetualIds = await this.getOnChainPerpetualIds();
19088
19497
  const [nav] = t.moveCall({
19089
19498
  target: `${vaultPkg}::vault::new_vault_nav`,
@@ -19105,11 +19514,9 @@ class DipCoinPerpSDK {
19105
19514
  return { nav, tx: t };
19106
19515
  }
19107
19516
  async signAndExecuteVaultTx(tx) {
19108
- tx.setSender(this.keypair.getPublicKey().toSuiAddress());
19109
- return await this.suiClient.signAndExecuteTransaction({
19110
- signer: this.keypair,
19111
- transaction: tx,
19112
- options: { showEffects: true, showObjectChanges: true },
19517
+ return signAndExecuteTransactionWithFreshGas(this.suiClient, tx, this.keypair, {
19518
+ showEffects: true,
19519
+ showObjectChanges: true,
19113
19520
  });
19114
19521
  }
19115
19522
  /**
@@ -19118,8 +19525,22 @@ class DipCoinPerpSDK {
19118
19525
  async createVault(args) {
19119
19526
  const tx = new transactions.Transaction();
19120
19527
  const vaultPkg = this.getVaultPackageId();
19528
+ const creator = this.walletAddress;
19529
+ const maxCap = formatNormalToWei(args.maxCap);
19530
+ const minDepositAmount = formatNormalToWei(args.minDepositAmount);
19531
+ const initialAmount = formatNormalToWei(args.initialAmount);
19532
+ const signed = await this.signVaultCreatePayload({
19533
+ creator,
19534
+ name: args.name,
19535
+ trader: args.trader,
19536
+ maxCap,
19537
+ minDepositAmount,
19538
+ creatorMinimumShareRatio: args.creatorMinimumShareRatio,
19539
+ creatorProfitShareRatio: args.creatorProfitShareRatio,
19540
+ initialAmount,
19541
+ });
19121
19542
  tx.moveCall({
19122
- target: `${vaultPkg}::vault::create_vault`,
19543
+ target: `${vaultPkg}::vault::create_vault_v2`,
19123
19544
  arguments: [
19124
19545
  tx.object(this.getDeploymentProtocolConfigId()),
19125
19546
  tx.object("0x6"),
@@ -19127,13 +19548,18 @@ class DipCoinPerpSDK {
19127
19548
  tx.object(this.getSubAccountsId()),
19128
19549
  tx.object(this.getBankId()),
19129
19550
  tx.object(this.getTxIndexerId()),
19551
+ tx.pure.vector("u8", signed.userAddress),
19130
19552
  tx.pure.string(args.name),
19131
- tx.pure.address(args.trader),
19132
- tx.pure.u128(formatNormalToWei(args.maxCap)),
19133
- tx.pure.u128(formatNormalToWei(args.minDepositAmount)),
19553
+ tx.pure.vector("u8", this.toUnifiedSuiAddressBytes(args.trader)),
19554
+ tx.pure.u128(maxCap),
19555
+ tx.pure.u128(minDepositAmount),
19134
19556
  tx.pure.u128(args.creatorMinimumShareRatio),
19135
19557
  tx.pure.u128(args.creatorProfitShareRatio),
19136
- tx.pure.u128(formatNormalToWei(args.initialAmount)),
19558
+ tx.pure.u128(initialAmount),
19559
+ tx.pure.u128(signed.salt),
19560
+ tx.pure.u64(signed.expiration),
19561
+ tx.pure.vector("u8", signed.signature),
19562
+ tx.pure.vector("u8", signed.publicKey),
19137
19563
  ],
19138
19564
  typeArguments: [this.getCurrencyType()],
19139
19565
  });
@@ -19145,17 +19571,25 @@ class DipCoinPerpSDK {
19145
19571
  async depositToVault(args) {
19146
19572
  const { nav, tx } = await this.buildVaultNavTransaction(args.vaultID, args.markets);
19147
19573
  const vaultPkg = this.getVaultPackageId();
19574
+ const amount = formatNormalToWei(args.amount);
19575
+ const signed = await this.signVaultUserAmountPayload("VaultDeposit", args.vaultID, this.walletAddress, amount);
19148
19576
  tx.moveCall({
19149
- target: `${vaultPkg}::vault::deposit`,
19577
+ target: `${vaultPkg}::vault::deposit_v2`,
19150
19578
  arguments: [
19151
19579
  tx.object(this.getDeploymentProtocolConfigId()),
19152
19580
  tx.object(this.getVaultConfigId()),
19153
19581
  tx.object("0x6"),
19154
19582
  tx.object(this.getBankId()),
19583
+ tx.object(this.getSubAccountsId()),
19155
19584
  tx.object(this.getTxIndexerId()),
19156
19585
  tx.object(args.vaultID),
19157
19586
  nav,
19158
- tx.pure.u128(formatNormalToWei(args.amount)),
19587
+ tx.pure.vector("u8", signed.userAddress),
19588
+ tx.pure.u128(amount),
19589
+ tx.pure.u128(signed.salt),
19590
+ tx.pure.u64(signed.expiration),
19591
+ tx.pure.vector("u8", signed.signature),
19592
+ tx.pure.vector("u8", signed.publicKey),
19159
19593
  ],
19160
19594
  typeArguments: [this.getCurrencyType()],
19161
19595
  });
@@ -19166,32 +19600,25 @@ class DipCoinPerpSDK {
19166
19600
  */
19167
19601
  async requestWithdrawFromVault(args) {
19168
19602
  const tx = new transactions.Transaction();
19169
- // On mainnet, update Pyth oracle prices before withdraw (contract checks freshness)
19170
- if (this.options.network === "mainnet") {
19171
- this.ensurePythClients();
19172
- if (this.priceServiceConnection && this.pythClient) {
19173
- const symbols = this.getMarketSymbols();
19174
- const priceIds = [];
19175
- for (const sym of symbols) {
19176
- const feedId = await this.resolvePriceFeedId(sym);
19177
- if (feedId)
19178
- priceIds.push(feedId);
19179
- }
19180
- if (priceIds.length > 0) {
19181
- const priceUpdateData = await this.priceServiceConnection.getPriceFeedsUpdateData(priceIds);
19182
- await this.pythClient.updatePriceFeeds(tx, priceUpdateData, priceIds);
19183
- }
19184
- }
19185
- }
19186
19603
  const vaultPkg = this.getVaultPackageId();
19604
+ const shares = formatNormalToWei(args.shares);
19605
+ const signed = await this.signVaultUserAmountPayload("VaultRequestWithdraw", args.vaultID, this.walletAddress, shares);
19187
19606
  tx.moveCall({
19188
- target: `${vaultPkg}::vault::request_withdraw`,
19607
+ target: `${vaultPkg}::vault::request_withdraw_v2`,
19189
19608
  arguments: [
19190
19609
  tx.object(this.getDeploymentProtocolConfigId()),
19191
19610
  tx.object(this.getVaultConfigId()),
19192
19611
  tx.object("0x6"),
19612
+ tx.object(this.getBankId()),
19613
+ tx.object(this.getSubAccountsId()),
19614
+ tx.object(this.getTxIndexerId()),
19193
19615
  tx.object(args.vaultID),
19194
- tx.pure.u128(formatNormalToWei(args.shares)),
19616
+ tx.pure.vector("u8", signed.userAddress),
19617
+ tx.pure.u128(shares),
19618
+ tx.pure.u128(signed.salt),
19619
+ tx.pure.u64(signed.expiration),
19620
+ tx.pure.vector("u8", signed.signature),
19621
+ tx.pure.vector("u8", signed.publicKey),
19195
19622
  ],
19196
19623
  typeArguments: [this.getCurrencyType()],
19197
19624
  });
@@ -19207,7 +19634,7 @@ class DipCoinPerpSDK {
19207
19634
  elements: args.withdrawalRequestIDs.map((id) => tx.object(id)),
19208
19635
  });
19209
19636
  tx.moveCall({
19210
- target: `${vaultPkg}::vault::fill_withdrawal_requests`,
19637
+ target: `${vaultPkg}::vault::fill_withdrawal_requests_v2`,
19211
19638
  arguments: [
19212
19639
  tx.object(this.getDeploymentProtocolConfigId()),
19213
19640
  tx.object(this.getVaultConfigId()),
@@ -19228,16 +19655,23 @@ class DipCoinPerpSDK {
19228
19655
  async closeVault(args) {
19229
19656
  const { nav, tx } = await this.buildVaultNavTransaction(args.vaultID, args.markets);
19230
19657
  const vaultPkg = this.getVaultPackageId();
19658
+ const signed = await this.signVaultUserPayload("VaultClose", args.vaultID, this.walletAddress);
19231
19659
  tx.moveCall({
19232
- target: `${vaultPkg}::vault::close_vault_v2`,
19660
+ target: `${vaultPkg}::vault::close_vault_v3`,
19233
19661
  arguments: [
19234
19662
  tx.object(this.getDeploymentProtocolConfigId()),
19235
19663
  tx.object(this.getVaultConfigId()),
19236
19664
  tx.object(this.getSubAccountsId()),
19665
+ tx.object(this.getTxIndexerId()),
19237
19666
  tx.object(args.vaultID),
19238
19667
  tx.object(this.getBankId()),
19239
19668
  tx.object("0x6"),
19240
19669
  nav,
19670
+ tx.pure.vector("u8", signed.userAddress),
19671
+ tx.pure.u128(signed.salt),
19672
+ tx.pure.u64(signed.expiration),
19673
+ tx.pure.vector("u8", signed.signature),
19674
+ tx.pure.vector("u8", signed.publicKey),
19241
19675
  ],
19242
19676
  typeArguments: [this.getCurrencyType()],
19243
19677
  });
@@ -19267,14 +19701,22 @@ class DipCoinPerpSDK {
19267
19701
  async claimClosedVaultFunds(args) {
19268
19702
  const tx = new transactions.Transaction();
19269
19703
  const vaultPkg = this.getVaultPackageId();
19704
+ const signed = await this.signVaultUserPayload("VaultClaimClosedFunds", args.vaultID, this.walletAddress);
19270
19705
  tx.moveCall({
19271
- target: `${vaultPkg}::vault::claim_closed_vault_funds`,
19706
+ target: `${vaultPkg}::vault::claim_closed_vault_funds_v2`,
19272
19707
  arguments: [
19273
19708
  tx.object(this.getDeploymentProtocolConfigId()),
19274
19709
  tx.object(this.getVaultConfigId()),
19275
19710
  tx.object(this.getBankId()),
19711
+ tx.object(this.getSubAccountsId()),
19276
19712
  tx.object(this.getTxIndexerId()),
19277
19713
  tx.object(args.vaultID),
19714
+ tx.object("0x6"),
19715
+ tx.pure.vector("u8", signed.userAddress),
19716
+ tx.pure.u128(signed.salt),
19717
+ tx.pure.u64(signed.expiration),
19718
+ tx.pure.vector("u8", signed.signature),
19719
+ tx.pure.vector("u8", signed.publicKey),
19278
19720
  ],
19279
19721
  typeArguments: [this.getCurrencyType()],
19280
19722
  });
@@ -19286,14 +19728,23 @@ class DipCoinPerpSDK {
19286
19728
  async setVaultTrader(args) {
19287
19729
  const tx = new transactions.Transaction();
19288
19730
  const vaultPkg = this.getVaultPackageId();
19731
+ const signed = await this.signVaultSetTraderPayload(args.vaultID, this.walletAddress, args.newTrader);
19289
19732
  tx.moveCall({
19290
- target: `${vaultPkg}::vault::set_trader`,
19733
+ target: `${vaultPkg}::vault::set_trader_v2`,
19291
19734
  arguments: [
19292
19735
  tx.object(this.getDeploymentProtocolConfigId()),
19293
19736
  tx.object(this.getVaultConfigId()),
19294
19737
  tx.object(args.vaultID),
19295
19738
  tx.object(this.getSubAccountsId()),
19296
- tx.pure.address(args.newTrader),
19739
+ tx.object(this.getTxIndexerId()),
19740
+ tx.object(this.getBankId()),
19741
+ tx.object("0x6"),
19742
+ tx.pure.vector("u8", signed.userAddress),
19743
+ tx.pure.vector("u8", this.toUnifiedSuiAddressBytes(args.newTrader)),
19744
+ tx.pure.u128(signed.salt),
19745
+ tx.pure.u64(signed.expiration),
19746
+ tx.pure.vector("u8", signed.signature),
19747
+ tx.pure.vector("u8", signed.publicKey),
19297
19748
  ],
19298
19749
  typeArguments: [this.getCurrencyType()],
19299
19750
  });
@@ -19305,15 +19756,24 @@ class DipCoinPerpSDK {
19305
19756
  async setVaultSubTrader(args) {
19306
19757
  const tx = new transactions.Transaction();
19307
19758
  const vaultPkg = this.getVaultPackageId();
19759
+ const signed = await this.signVaultSetSubTraderPayload(args.vaultID, this.walletAddress, args.subTrader, args.status);
19308
19760
  tx.moveCall({
19309
- target: `${vaultPkg}::vault::set_sub_trader`,
19761
+ target: `${vaultPkg}::vault::set_sub_trader_v2`,
19310
19762
  arguments: [
19311
19763
  tx.object(this.getDeploymentProtocolConfigId()),
19312
19764
  tx.object(this.getVaultConfigId()),
19313
19765
  tx.object(args.vaultID),
19314
19766
  tx.object(this.getSubAccountsId()),
19315
- tx.pure.address(args.subTrader),
19767
+ tx.object(this.getTxIndexerId()),
19768
+ tx.object(this.getBankId()),
19769
+ tx.object("0x6"),
19770
+ tx.pure.vector("u8", signed.userAddress),
19771
+ tx.pure.vector("u8", this.toUnifiedSuiAddressBytes(args.subTrader)),
19316
19772
  tx.pure.bool(args.status),
19773
+ tx.pure.u128(signed.salt),
19774
+ tx.pure.u64(signed.expiration),
19775
+ tx.pure.vector("u8", signed.signature),
19776
+ tx.pure.vector("u8", signed.publicKey),
19317
19777
  ],
19318
19778
  typeArguments: [this.getCurrencyType()],
19319
19779
  });
@@ -19325,13 +19785,23 @@ class DipCoinPerpSDK {
19325
19785
  async setVaultDepositStatus(args) {
19326
19786
  const tx = new transactions.Transaction();
19327
19787
  const vaultPkg = this.getVaultPackageId();
19788
+ const signed = await this.signVaultDepositStatusPayload(args.vaultID, this.walletAddress, args.status);
19328
19789
  tx.moveCall({
19329
- target: `${vaultPkg}::vault::set_deposit_status`,
19790
+ target: `${vaultPkg}::vault::set_deposit_status_v2`,
19330
19791
  arguments: [
19331
19792
  tx.object(this.getDeploymentProtocolConfigId()),
19332
19793
  tx.object(this.getVaultConfigId()),
19333
19794
  tx.object(args.vaultID),
19795
+ tx.object(this.getSubAccountsId()),
19796
+ tx.object(this.getTxIndexerId()),
19797
+ tx.object(this.getBankId()),
19798
+ tx.object("0x6"),
19799
+ tx.pure.vector("u8", signed.userAddress),
19334
19800
  tx.pure.bool(args.status),
19801
+ tx.pure.u128(signed.salt),
19802
+ tx.pure.u64(signed.expiration),
19803
+ tx.pure.vector("u8", signed.signature),
19804
+ tx.pure.vector("u8", signed.publicKey),
19335
19805
  ],
19336
19806
  typeArguments: [this.getCurrencyType()],
19337
19807
  });
@@ -19343,13 +19813,24 @@ class DipCoinPerpSDK {
19343
19813
  async setVaultMaxCap(args) {
19344
19814
  const tx = new transactions.Transaction();
19345
19815
  const vaultPkg = this.getVaultPackageId();
19816
+ const amount = formatNormalToWei(args.maxCap);
19817
+ const signed = await this.signVaultCreatorAmountPayload("VaultSetMaxCap", args.vaultID, this.walletAddress, amount);
19346
19818
  tx.moveCall({
19347
- target: `${vaultPkg}::vault::set_max_cap`,
19819
+ target: `${vaultPkg}::vault::set_max_cap_v2`,
19348
19820
  arguments: [
19349
19821
  tx.object(this.getDeploymentProtocolConfigId()),
19350
19822
  tx.object(this.getVaultConfigId()),
19351
19823
  tx.object(args.vaultID),
19352
- tx.pure.u128(formatNormalToWei(args.maxCap)),
19824
+ tx.object(this.getSubAccountsId()),
19825
+ tx.object(this.getTxIndexerId()),
19826
+ tx.object(this.getBankId()),
19827
+ tx.object("0x6"),
19828
+ tx.pure.vector("u8", signed.userAddress),
19829
+ tx.pure.u128(amount),
19830
+ tx.pure.u128(signed.salt),
19831
+ tx.pure.u64(signed.expiration),
19832
+ tx.pure.vector("u8", signed.signature),
19833
+ tx.pure.vector("u8", signed.publicKey),
19353
19834
  ],
19354
19835
  typeArguments: [this.getCurrencyType()],
19355
19836
  });
@@ -19361,13 +19842,24 @@ class DipCoinPerpSDK {
19361
19842
  async setVaultMinDepositAmount(args) {
19362
19843
  const tx = new transactions.Transaction();
19363
19844
  const vaultPkg = this.getVaultPackageId();
19845
+ const amount = formatNormalToWei(args.minDepositAmount);
19846
+ const signed = await this.signVaultCreatorAmountPayload("VaultSetMinDepositAmount", args.vaultID, this.walletAddress, amount);
19364
19847
  tx.moveCall({
19365
- target: `${vaultPkg}::vault::set_min_deposit_amount`,
19848
+ target: `${vaultPkg}::vault::set_min_deposit_amount_v2`,
19366
19849
  arguments: [
19367
19850
  tx.object(this.getDeploymentProtocolConfigId()),
19368
19851
  tx.object(this.getVaultConfigId()),
19369
19852
  tx.object(args.vaultID),
19370
- tx.pure.u128(formatNormalToWei(args.minDepositAmount)),
19853
+ tx.object(this.getSubAccountsId()),
19854
+ tx.object(this.getTxIndexerId()),
19855
+ tx.object(this.getBankId()),
19856
+ tx.object("0x6"),
19857
+ tx.pure.vector("u8", signed.userAddress),
19858
+ tx.pure.u128(amount),
19859
+ tx.pure.u128(signed.salt),
19860
+ tx.pure.u64(signed.expiration),
19861
+ tx.pure.vector("u8", signed.signature),
19862
+ tx.pure.vector("u8", signed.publicKey),
19371
19863
  ],
19372
19864
  typeArguments: [this.getCurrencyType()],
19373
19865
  });
@@ -19379,13 +19871,23 @@ class DipCoinPerpSDK {
19379
19871
  async setVaultAutoCloseOnWithdraw(args) {
19380
19872
  const tx = new transactions.Transaction();
19381
19873
  const vaultPkg = this.getVaultPackageId();
19874
+ const signed = await this.signVaultAutoClosePayload(args.vaultID, this.walletAddress, args.autoCloseOnWithdraw);
19382
19875
  tx.moveCall({
19383
- target: `${vaultPkg}::vault::set_auto_close_on_withdraw`,
19876
+ target: `${vaultPkg}::vault::set_auto_close_on_withdraw_v2`,
19384
19877
  arguments: [
19385
19878
  tx.object(this.getDeploymentProtocolConfigId()),
19386
19879
  tx.object(this.getVaultConfigId()),
19387
19880
  tx.object(args.vaultID),
19881
+ tx.object(this.getSubAccountsId()),
19882
+ tx.object(this.getTxIndexerId()),
19883
+ tx.object(this.getBankId()),
19884
+ tx.object("0x6"),
19885
+ tx.pure.vector("u8", signed.userAddress),
19388
19886
  tx.pure.bool(args.autoCloseOnWithdraw),
19887
+ tx.pure.u128(signed.salt),
19888
+ tx.pure.u64(signed.expiration),
19889
+ tx.pure.vector("u8", signed.signature),
19890
+ tx.pure.vector("u8", signed.publicKey),
19389
19891
  ],
19390
19892
  typeArguments: [this.getCurrencyType()],
19391
19893
  });
@@ -19660,12 +20162,12 @@ function initDipCoinPerpSDK(privateKey, options) {
19660
20162
 
19661
20163
  const globalEnv = path__default["default"].join(os__default["default"].homedir(), ".config", "dipcoin", "env");
19662
20164
  const localEnv = path__default["default"].resolve(process.cwd(), ".env");
20165
+ if (fs__default["default"].existsSync(globalEnv)) {
20166
+ dotenv__default["default"].config({ path: globalEnv });
20167
+ }
19663
20168
  if (fs__default["default"].existsSync(localEnv)) {
19664
20169
  dotenv__default["default"].config({ path: localEnv });
19665
20170
  }
19666
- else if (fs__default["default"].existsSync(globalEnv)) {
19667
- dotenv__default["default"].config({ path: globalEnv });
19668
- }
19669
20171
  // Cache SDK instance
19670
20172
  let cachedSDK = null;
19671
20173
  /**
@@ -19712,9 +20214,13 @@ function tryAutoBindReferral(sdk) {
19712
20214
  try {
19713
20215
  fs__default["default"].writeFileSync(markerPath, "");
19714
20216
  }
19715
- catch { }
20217
+ catch {
20218
+ // Marker creation is best-effort; retrying referral binding is harmless.
20219
+ }
19716
20220
  })
19717
- .catch(() => { });
20221
+ .catch(() => {
20222
+ // Auto-binding should never block normal CLI startup.
20223
+ });
19718
20224
  }
19719
20225
  /**
19720
20226
  * Get or create the SDK instance (singleton).
@@ -19724,7 +20230,8 @@ function getSDK() {
19724
20230
  return cachedSDK;
19725
20231
  const keypair = getKeypair();
19726
20232
  const network = getNetwork();
19727
- cachedSDK = initDipCoinPerpSDK(keypair, { network });
20233
+ const customRpc = process.env.DIPCOIN_RPC_URL || process.env.SUI_RPC_URL;
20234
+ cachedSDK = initDipCoinPerpSDK(keypair, { network, customRpc });
19728
20235
  tryAutoBindReferral(cachedSDK);
19729
20236
  return cachedSDK;
19730
20237
  }
@@ -20396,7 +20903,10 @@ function registerMarketCommands(program) {
20396
20903
  const ob = result.data;
20397
20904
  console.log(`\n Order Book: ${symbol}`);
20398
20905
  console.log(" --- Asks (sell) ---");
20399
- ob.asks.slice(0, 10).reverse().forEach((a) => {
20906
+ ob.asks
20907
+ .slice(0, 10)
20908
+ .reverse()
20909
+ .forEach((a) => {
20400
20910
  console.log(` ${formatWei(a.price).padStart(14)} | ${formatWei(a.quantity)}`);
20401
20911
  });
20402
20912
  console.log(" --- Bids (buy) ---");
@@ -20703,7 +21213,9 @@ function registerVaultCommands(program) {
20703
21213
  return handleError(result.error);
20704
21214
  let vaults = result.data || [];
20705
21215
  if (!vaults.length)
20706
- return isJson(program) ? printJson({ records: [], total: 0, pageNum: 1, pageSize: 10 }) : console.log("No vaults found.");
21216
+ return isJson(program)
21217
+ ? printJson({ records: [], total: 0, pageNum: 1, pageSize: 10 })
21218
+ : console.log("No vaults found.");
20707
21219
  // Client-side filter
20708
21220
  const filter = opts.filter;
20709
21221
  if (filter && filter !== "All") {
@@ -20731,7 +21243,16 @@ function registerVaultCommands(program) {
20731
21243
  return printJson({ records: pageVaults, total, pageNum, pageSize, totalPages });
20732
21244
  if (!pageVaults.length)
20733
21245
  return console.log(`No vaults on page ${pageNum}. Total: ${total}, pages: ${totalPages}.`);
20734
- printTable(["Name", "Vault ID", "TVL (USDC)", "APR (%)", "Depositors", "Age (d)", "Deposit", "Status"], pageVaults.map((v) => {
21246
+ printTable([
21247
+ "Name",
21248
+ "Vault ID",
21249
+ "TVL (USDC)",
21250
+ "APR (%)",
21251
+ "Depositors",
21252
+ "Age (d)",
21253
+ "Deposit",
21254
+ "Status",
21255
+ ], pageVaults.map((v) => {
20735
21256
  const tvl = new BigNumber__default["default"](v.tvl || "0").dividedBy(new BigNumber__default["default"](10).pow(18)).toFixed(2);
20736
21257
  const apr = new BigNumber__default["default"](v.apr || "0").dividedBy(new BigNumber__default["default"](10).pow(14)).toFixed(2);
20737
21258
  const depositors = new BigNumber__default["default"](v.currentDepositors || "0")
@@ -20773,7 +21294,10 @@ function registerVaultCommands(program) {
20773
21294
  ["Name", d.name || "-"],
20774
21295
  ["Creator", d.creator || "-"],
20775
21296
  ["Trader", d.trader || "-"],
20776
- ["Deposit Status", d.deposit_status === true || d.deposit_status === "true" ? "Open" : "Closed"],
21297
+ [
21298
+ "Deposit Status",
21299
+ d.deposit_status === true || d.deposit_status === "true" ? "Open" : "Closed",
21300
+ ],
20777
21301
  ["Total Shares", formatWeiValue(d.total_shares)],
20778
21302
  ["Max Cap", formatWeiValue(d.max_cap)],
20779
21303
  ["Min Deposit", formatWeiValue(d.min_deposit_amount)],
@@ -21045,7 +21569,10 @@ function registerVaultCommands(program) {
21045
21569
  .action(async (vaultId, amount) => {
21046
21570
  try {
21047
21571
  const sdk = getSDK();
21048
- const tx = await sdk.setVaultMinDepositAmount({ vaultID: vaultId, minDepositAmount: parseAmount(amount) });
21572
+ const tx = await sdk.setVaultMinDepositAmount({
21573
+ vaultID: vaultId,
21574
+ minDepositAmount: parseAmount(amount),
21575
+ });
21049
21576
  printTxResult(program, tx, `Min deposit set to ${amount} USDC.`);
21050
21577
  }
21051
21578
  catch (e) {
@@ -21062,7 +21589,10 @@ function registerVaultCommands(program) {
21062
21589
  try {
21063
21590
  const sdk = getSDK();
21064
21591
  const enabled = !opts.disable;
21065
- const tx = await sdk.setVaultAutoCloseOnWithdraw({ vaultID: vaultId, autoCloseOnWithdraw: enabled });
21592
+ const tx = await sdk.setVaultAutoCloseOnWithdraw({
21593
+ vaultID: vaultId,
21594
+ autoCloseOnWithdraw: enabled,
21595
+ });
21066
21596
  printTxResult(program, tx, `Auto-close on withdraw ${enabled ? "enabled" : "disabled"}.`);
21067
21597
  }
21068
21598
  catch (e) {