dipcoin-cli 1.3.3 → 1.3.4

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
@@ -16653,21 +16653,45 @@ function buildAddMarginTx(deployment, args, tx, gasBudget, sender) {
16653
16653
  if (sender)
16654
16654
  t.setSender(sender);
16655
16655
  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
- });
16656
+ const signed = args.signedPayload;
16657
+ if (signed) {
16658
+ t.moveCall({
16659
+ target: `${packageId}::exchange::add_margin_v4`,
16660
+ arguments: [
16661
+ t.object(getProtocolConfigId(deployment)),
16662
+ t.object(CLOCK_OBJECT_ID),
16663
+ t.object(args.perpID || getPerpetualId(deployment, args.market)),
16664
+ t.object(getBankId(deployment)),
16665
+ t.object(args.subAccountsMapID || getSubAccountsId(deployment)),
16666
+ t.object(getTxIndexerId(deployment)),
16667
+ t.object(getPriceFeedId(deployment)),
16668
+ t.pure.vector("u8", signed.userAddress),
16669
+ t.pure.u128(args.amount.toString()),
16670
+ t.pure.u128(signed.salt.toString()),
16671
+ t.pure.u64(signed.expiration.toString()),
16672
+ t.pure.vector("u8", signed.signature),
16673
+ t.pure.vector("u8", signed.publicKey),
16674
+ ],
16675
+ typeArguments: [getCurrencyType(deployment)],
16676
+ });
16677
+ }
16678
+ else {
16679
+ t.moveCall({
16680
+ target: `${packageId}::exchange::add_margin_v3`,
16681
+ arguments: [
16682
+ t.object(getProtocolConfigId(deployment)),
16683
+ t.object(CLOCK_OBJECT_ID),
16684
+ t.object(args.perpID || getPerpetualId(deployment, args.market)),
16685
+ t.object(getBankId(deployment)),
16686
+ t.object(args.subAccountsMapID || getSubAccountsId(deployment)),
16687
+ t.object(getTxIndexerId(deployment)),
16688
+ t.object(getPriceFeedId(deployment)),
16689
+ t.pure.address(args.account || sender || ""),
16690
+ t.pure.u128(args.amount.toString()),
16691
+ ],
16692
+ typeArguments: [getCurrencyType(deployment)],
16693
+ });
16694
+ }
16671
16695
  return t;
16672
16696
  }
16673
16697
  function buildRemoveMarginTx(deployment, args, tx, gasBudget, sender) {
@@ -16677,21 +16701,45 @@ function buildRemoveMarginTx(deployment, args, tx, gasBudget, sender) {
16677
16701
  if (sender)
16678
16702
  t.setSender(sender);
16679
16703
  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
- });
16704
+ const signed = args.signedPayload;
16705
+ if (signed) {
16706
+ t.moveCall({
16707
+ target: `${packageId}::exchange::remove_margin_v4`,
16708
+ arguments: [
16709
+ t.object(getProtocolConfigId(deployment)),
16710
+ t.object(CLOCK_OBJECT_ID),
16711
+ t.object(args.perpID || getPerpetualId(deployment, args.market)),
16712
+ t.object(getBankId(deployment)),
16713
+ t.object(args.subAccountsMapID || getSubAccountsId(deployment)),
16714
+ t.object(getTxIndexerId(deployment)),
16715
+ t.object(getPriceFeedId(deployment)),
16716
+ t.pure.vector("u8", signed.userAddress),
16717
+ t.pure.u128(args.amount.toString()),
16718
+ t.pure.u128(signed.salt.toString()),
16719
+ t.pure.u64(signed.expiration.toString()),
16720
+ t.pure.vector("u8", signed.signature),
16721
+ t.pure.vector("u8", signed.publicKey),
16722
+ ],
16723
+ typeArguments: [getCurrencyType(deployment)],
16724
+ });
16725
+ }
16726
+ else {
16727
+ t.moveCall({
16728
+ target: `${packageId}::exchange::remove_margin_v3`,
16729
+ arguments: [
16730
+ t.object(getProtocolConfigId(deployment)),
16731
+ t.object(CLOCK_OBJECT_ID),
16732
+ t.object(args.perpID || getPerpetualId(deployment, args.market)),
16733
+ t.object(getBankId(deployment)),
16734
+ t.object(args.subAccountsMapID || getSubAccountsId(deployment)),
16735
+ t.object(getTxIndexerId(deployment)),
16736
+ t.object(getPriceFeedId(deployment)),
16737
+ t.pure.address(args.account || sender || ""),
16738
+ t.pure.u128(args.amount.toString()),
16739
+ ],
16740
+ typeArguments: [getCurrencyType(deployment)],
16741
+ });
16742
+ }
16695
16743
  return t;
16696
16744
  }
16697
16745
  function buildWithdrawTx(deployment, args, tx, gasBudget, sender) {
@@ -18688,6 +18736,130 @@ class DipCoinPerpSDK {
18688
18736
  txHash,
18689
18737
  };
18690
18738
  }
18739
+ packPayload(fields) {
18740
+ let result = "{\n";
18741
+ for (const [key, value] of fields) {
18742
+ result += `"${key}":"${value}",\n`;
18743
+ }
18744
+ result += '"domain":"dipcoin.io"\n';
18745
+ result += "}";
18746
+ return result;
18747
+ }
18748
+ normalizeSuiAddress(address) {
18749
+ const hex = address.startsWith("0x") ? address.slice(2) : address;
18750
+ if (!/^[0-9a-fA-F]+$/.test(hex) || hex.length > 64) {
18751
+ throw new Error(`Invalid Sui address: ${address}`);
18752
+ }
18753
+ return `0x${hex.toLowerCase().padStart(64, "0")}`;
18754
+ }
18755
+ toUnifiedSuiAddress(address) {
18756
+ const normalized = this.normalizeSuiAddress(address);
18757
+ return `Sui:0x${normalized.slice(2)}`;
18758
+ }
18759
+ toUnifiedSuiAddressBytes(address) {
18760
+ const normalized = this.normalizeSuiAddress(address);
18761
+ return [0, ...Array.from(Buffer.from(normalized.slice(2), "hex"))];
18762
+ }
18763
+ async signDipcoinActionPayload(fields, addressForVector) {
18764
+ const salt = Date.now();
18765
+ const expiration = salt + 1_800_000;
18766
+ const message = this.packPayload([
18767
+ ...fields,
18768
+ ["salt", String(salt)],
18769
+ ["expiration", String(expiration)],
18770
+ ]);
18771
+ const signed = await this.keypair.signPersonalMessage(new TextEncoder().encode(message));
18772
+ const parsed = cryptography.parseSerializedSignature(typeof signed === "string" ? signed : signed.signature);
18773
+ const scheme = String(parsed.signatureScheme);
18774
+ const flag = /ed25519/i.test(scheme) ? 1 : 0;
18775
+ if (!/ed25519|secp256k1|secp256r1/i.test(scheme)) {
18776
+ throw new Error(`Unsupported signature scheme for signed payload: ${scheme}`);
18777
+ }
18778
+ return {
18779
+ userAddress: this.toUnifiedSuiAddressBytes(addressForVector),
18780
+ salt: String(salt),
18781
+ expiration: String(expiration),
18782
+ signature: [...Array.from(Buffer.from(parsed.signature)), flag],
18783
+ publicKey: Array.from(Buffer.from(parsed.publicKey)),
18784
+ };
18785
+ }
18786
+ async signUserAmountActionPayload(args) {
18787
+ return this.signDipcoinActionPayload([
18788
+ ["action", args.action],
18789
+ ["market", this.normalizeSuiAddress(args.market)],
18790
+ ["user", this.toUnifiedSuiAddress(args.user)],
18791
+ ["amount", args.amount],
18792
+ ], args.user);
18793
+ }
18794
+ async signVaultCreatePayload(args) {
18795
+ return this.signDipcoinActionPayload([
18796
+ ["action", "VaultCreate"],
18797
+ ["creator", this.toUnifiedSuiAddress(args.creator)],
18798
+ ["name", args.name],
18799
+ ["trader", this.toUnifiedSuiAddress(args.trader)],
18800
+ ["maxCap", args.maxCap],
18801
+ ["minDepositAmount", args.minDepositAmount],
18802
+ ["creatorMinimumShareRatio", args.creatorMinimumShareRatio],
18803
+ ["creatorProfitShareRatio", args.creatorProfitShareRatio],
18804
+ ["initialAmount", args.initialAmount],
18805
+ ], args.creator);
18806
+ }
18807
+ async signVaultUserAmountPayload(action, vaultID, user, amount) {
18808
+ return this.signDipcoinActionPayload([
18809
+ ["action", action],
18810
+ ["vault", this.normalizeSuiAddress(vaultID)],
18811
+ ["user", this.toUnifiedSuiAddress(user)],
18812
+ ["amount", amount],
18813
+ ], user);
18814
+ }
18815
+ async signVaultUserPayload(action, vaultID, user) {
18816
+ return this.signDipcoinActionPayload([
18817
+ ["action", action],
18818
+ ["vault", this.normalizeSuiAddress(vaultID)],
18819
+ ["user", this.toUnifiedSuiAddress(user)],
18820
+ ], user);
18821
+ }
18822
+ async signVaultSetTraderPayload(vaultID, creator, newTrader) {
18823
+ return this.signDipcoinActionPayload([
18824
+ ["action", "VaultSetTrader"],
18825
+ ["vault", this.normalizeSuiAddress(vaultID)],
18826
+ ["creator", this.toUnifiedSuiAddress(creator)],
18827
+ ["newTrader", this.toUnifiedSuiAddress(newTrader)],
18828
+ ], creator);
18829
+ }
18830
+ async signVaultSetSubTraderPayload(vaultID, trader, subTrader, status) {
18831
+ return this.signDipcoinActionPayload([
18832
+ ["action", "VaultSetSubTrader"],
18833
+ ["vault", this.normalizeSuiAddress(vaultID)],
18834
+ ["trader", this.toUnifiedSuiAddress(trader)],
18835
+ ["subTrader", this.toUnifiedSuiAddress(subTrader)],
18836
+ ["status", String(status)],
18837
+ ], trader);
18838
+ }
18839
+ async signVaultDepositStatusPayload(vaultID, creator, status) {
18840
+ return this.signDipcoinActionPayload([
18841
+ ["action", "VaultSetDepositStatus"],
18842
+ ["vault", this.normalizeSuiAddress(vaultID)],
18843
+ ["creator", this.toUnifiedSuiAddress(creator)],
18844
+ ["status", String(status)],
18845
+ ], creator);
18846
+ }
18847
+ async signVaultCreatorAmountPayload(action, vaultID, creator, amount) {
18848
+ return this.signDipcoinActionPayload([
18849
+ ["action", action],
18850
+ ["vault", this.normalizeSuiAddress(vaultID)],
18851
+ ["creator", this.toUnifiedSuiAddress(creator)],
18852
+ ["amount", amount],
18853
+ ], creator);
18854
+ }
18855
+ async signVaultAutoClosePayload(vaultID, creator, autoCloseOnWithdraw) {
18856
+ return this.signDipcoinActionPayload([
18857
+ ["action", "VaultSetAutoCloseOnWithdraw"],
18858
+ ["vault", this.normalizeSuiAddress(vaultID)],
18859
+ ["creator", this.toUnifiedSuiAddress(creator)],
18860
+ ["autoCloseOnWithdraw", String(autoCloseOnWithdraw)],
18861
+ ], creator);
18862
+ }
18691
18863
  getDeploymentProtocolConfigId() {
18692
18864
  const protocolId = this.deploymentConfig?.objects?.ProtocolConfig?.id;
18693
18865
  if (!protocolId) {
@@ -18697,6 +18869,16 @@ class DipCoinPerpSDK {
18697
18869
  }
18698
18870
  async buildMarginTransaction(params, action) {
18699
18871
  const payload = this.buildMarginCallArgs(params, action);
18872
+ const signedMarket = payload.perpID || (payload.market ? this.resolvePerpIdFromDeployment(payload.market) : undefined);
18873
+ if (!signedMarket) {
18874
+ throw new Error("PerpetualID is required for signed margin payload");
18875
+ }
18876
+ const signedPayload = await this.signUserAmountActionPayload({
18877
+ action: action === "add" ? "AddMargin" : "RemoveMargin",
18878
+ market: signedMarket,
18879
+ user: payload.account,
18880
+ amount: payload.amount,
18881
+ });
18700
18882
  // v6+ requires PriceFeed to be fresh (~60s) before the margin call. Operator updates
18701
18883
  // are only hourly so we always fetch + prepend our own update_price_feed step.
18702
18884
  const sym = payload.market || params.symbol;
@@ -18706,9 +18888,9 @@ class DipCoinPerpSDK {
18706
18888
  const baseTx = new transactions.Transaction();
18707
18889
  this.appendPriceFeedUpdates(baseTx, Array.from(feeds.values()));
18708
18890
  if (action === "add") {
18709
- return buildAddMarginTx(this.deploymentConfig, payload, baseTx, params.gasBudget);
18891
+ return buildAddMarginTx(this.deploymentConfig, { ...payload, signedPayload }, baseTx, params.gasBudget);
18710
18892
  }
18711
- return buildRemoveMarginTx(this.deploymentConfig, payload, baseTx, params.gasBudget);
18893
+ return buildRemoveMarginTx(this.deploymentConfig, { ...payload, signedPayload }, baseTx, params.gasBudget);
18712
18894
  }
18713
18895
  async buildUpdatePriceTransaction(symbol) {
18714
18896
  if (!symbol) {
@@ -18997,6 +19179,105 @@ class DipCoinPerpSDK {
18997
19179
  }
18998
19180
  return id;
18999
19181
  }
19182
+ sleep(ms) {
19183
+ return new Promise((resolve) => setTimeout(resolve, ms));
19184
+ }
19185
+ getSignedPriceFeedTimestamps(payload) {
19186
+ let offset = 0;
19187
+ let length = 0;
19188
+ let shift = 0;
19189
+ while (offset < payload.length) {
19190
+ const byte = payload[offset++];
19191
+ length |= (byte & 0x7f) << shift;
19192
+ if ((byte & 0x80) === 0)
19193
+ break;
19194
+ shift += 7;
19195
+ }
19196
+ if (length <= 0)
19197
+ return [];
19198
+ const timestamps = [];
19199
+ for (let i = 0; i < length; i++) {
19200
+ const timestampOffset = offset + 32 + 16;
19201
+ if (timestampOffset + 8 > payload.length)
19202
+ return [];
19203
+ const view = new DataView(payload.buffer, payload.byteOffset + timestampOffset, 8);
19204
+ timestamps.push(Number(view.getBigUint64(0, true)));
19205
+ offset = timestampOffset + 8 + 2;
19206
+ }
19207
+ return timestamps;
19208
+ }
19209
+ getSignedPriceFeedMaxTimestamp(payload) {
19210
+ const timestamps = this.getSignedPriceFeedTimestamps(payload);
19211
+ return timestamps.length ? Math.max(...timestamps) : undefined;
19212
+ }
19213
+ async waitForSignedPriceFeedClock(payload) {
19214
+ const timestamp = this.getSignedPriceFeedMaxTimestamp(payload);
19215
+ if (!timestamp)
19216
+ return;
19217
+ // price_feed::collect_price_updates accepts timestamps no more than 1500ms
19218
+ // ahead of Sui Clock and no more than 60000ms behind it. The DipCoin API can
19219
+ // sign with wall-clock time while Sui Clock lags, so wait for the chain clock
19220
+ // to catch up before submitting the PTB. Public RPC backends can disagree by
19221
+ // a few seconds, so target a timestamp that is already ~10s old.
19222
+ const start = Date.now();
19223
+ while (true) {
19224
+ const clock = await this.suiClient.getObject({
19225
+ id: "0x6",
19226
+ options: { showContent: true },
19227
+ });
19228
+ const clockMs = Number(clock.data?.content?.fields?.timestamp_ms);
19229
+ if (!Number.isFinite(clockMs))
19230
+ return;
19231
+ const waitMs = timestamp - clockMs + 10_000;
19232
+ if (waitMs <= 0)
19233
+ return;
19234
+ if (Date.now() - start + waitMs > 180_000) {
19235
+ throw new Error(`Signed price feed is too far ahead of Sui Clock (${Math.ceil(waitMs / 1000)}s). Try again later.`);
19236
+ }
19237
+ await this.sleep(Math.min(waitMs, 15_000));
19238
+ }
19239
+ }
19240
+ async waitForSignedPriceFeedEntries(entries) {
19241
+ const timestamps = entries.flatMap((entry) => this.getSignedPriceFeedTimestamps(entry.payload));
19242
+ if (!timestamps.length)
19243
+ return;
19244
+ const maxTimestamp = Math.max(...timestamps);
19245
+ const minTimestamp = Math.min(...timestamps);
19246
+ const start = Date.now();
19247
+ while (true) {
19248
+ const clock = await this.suiClient.getObject({
19249
+ id: "0x6",
19250
+ options: { showContent: true },
19251
+ });
19252
+ const clockMs = Number(clock.data?.content?.fields?.timestamp_ms);
19253
+ if (!Number.isFinite(clockMs))
19254
+ return;
19255
+ const waitMs = maxTimestamp - clockMs + 10_000;
19256
+ if (waitMs <= 0) {
19257
+ if (clockMs - minTimestamp > 55_000) {
19258
+ throw new Error("Signed price feed expired while waiting for Sui Clock");
19259
+ }
19260
+ return;
19261
+ }
19262
+ if (Date.now() - start + waitMs > 180_000) {
19263
+ throw new Error(`Signed price feed is too far ahead of Sui Clock (${Math.ceil(waitMs / 1000)}s). Try again later.`);
19264
+ }
19265
+ await this.sleep(Math.min(waitMs, 15_000));
19266
+ }
19267
+ }
19268
+ async requestSignedPriceFeedEntry(symbols) {
19269
+ const qs = symbols.map((s) => `symbols=${encodeURIComponent(s)}`).join("&");
19270
+ const resp = await this.httpClient.get(`${API_ENDPOINTS.SIGNED_PRICE_LATEST}?${qs}`);
19271
+ if (resp.code !== 200 || !resp.data) {
19272
+ throw new Error(`Failed to fetch signed price feed for [${symbols.join(",")}]: ${resp.message || `code ${resp.code}`}`);
19273
+ }
19274
+ const d = resp.data;
19275
+ const b64 = (s) => Uint8Array.from(Buffer.from(s, "base64"));
19276
+ if (!d.payload || !d.signature || !d.publicKey) {
19277
+ throw new Error("Signed price feed response missing payload/signature/publicKey");
19278
+ }
19279
+ return { payload: b64(d.payload), signature: b64(d.signature), publicKey: b64(d.publicKey) };
19280
+ }
19000
19281
  /**
19001
19282
  * Fetch signed price update bytes for the given markets from the DipCoin pricing service.
19002
19283
  * Each entry's payload/signature/publicKey is consumed by
@@ -19010,22 +19291,75 @@ class DipCoinPerpSDK {
19010
19291
  const out = new Map();
19011
19292
  if (symbols.length === 0)
19012
19293
  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}`}`);
19294
+ let lastClockError;
19295
+ for (let attempt = 0; attempt < 5; attempt++) {
19296
+ const entry = await this.requestSignedPriceFeedEntry(symbols);
19297
+ try {
19298
+ await this.waitForSignedPriceFeedEntries([entry]);
19299
+ }
19300
+ catch (error) {
19301
+ const message = String(error?.message || error);
19302
+ if (!message.includes("too far ahead") && !message.includes("expired")) {
19303
+ throw error;
19304
+ }
19305
+ lastClockError = error;
19306
+ await this.sleep(2_000 * (attempt + 1));
19307
+ continue;
19308
+ }
19309
+ // API returns a single bundle covering all requested symbols' prices in priceList,
19310
+ // but only one (payload, signature, publicKey) tuple. Map every requested symbol to it.
19311
+ for (const sym of symbols)
19312
+ out.set(sym, entry);
19313
+ return out;
19017
19314
  }
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");
19315
+ throw lastClockError || new Error("Failed to fetch a usable signed price feed");
19316
+ }
19317
+ async fetchSignedPriceFeedChunks(symbols, chunkSize = 20) {
19318
+ if (symbols.length === 0)
19319
+ return [];
19320
+ let lastClockError;
19321
+ for (let attempt = 0; attempt < 5; attempt++) {
19322
+ const entries = [];
19323
+ for (let i = 0; i < symbols.length; i += chunkSize) {
19324
+ entries.push(await this.requestSignedPriceFeedEntry(symbols.slice(i, i + chunkSize)));
19325
+ }
19326
+ try {
19327
+ await this.waitForSignedPriceFeedEntries(entries);
19328
+ await this.validateSignedPriceFeedEntries(entries);
19329
+ }
19330
+ catch (error) {
19331
+ const message = String(error?.message || error);
19332
+ if (!message.includes("too far ahead") &&
19333
+ !message.includes("expired") &&
19334
+ !message.includes("price_feed")) {
19335
+ throw error;
19336
+ }
19337
+ lastClockError = error;
19338
+ await this.sleep(2_000 * (attempt + 1));
19339
+ continue;
19340
+ }
19341
+ return Array.from(new Map(entries.map((feed) => [
19342
+ Buffer.from(feed.payload).toString("hex"),
19343
+ feed,
19344
+ ])).values());
19022
19345
  }
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;
19346
+ throw lastClockError || new Error("Failed to fetch usable signed price feed chunks");
19347
+ }
19348
+ async validateSignedPriceFeedEntries(entries) {
19349
+ if (!entries.length)
19350
+ return;
19351
+ const tx = new transactions.Transaction();
19352
+ this.appendPriceFeedUpdates(tx, entries);
19353
+ tx.setSender(this.walletAddress);
19354
+ const result = await this.suiClient.devInspectTransactionBlock({
19355
+ sender: this.walletAddress,
19356
+ transactionBlock: tx,
19357
+ });
19358
+ const status = result.effects?.status?.status;
19359
+ if (status === "success")
19360
+ return;
19361
+ const error = result.effects?.status?.error || result.error || "unknown price_feed error";
19362
+ throw new Error(`price_feed validation failed: ${error}`);
19029
19363
  }
19030
19364
  /**
19031
19365
  * Build a `price_feed::update_price_feed` PTB command for each provided signed feed
@@ -19079,11 +19413,16 @@ class DipCoinPerpSDK {
19079
19413
  // a single update_price_feed step. Then iterate the on-chain perpetual_registry (which is
19080
19414
  // what new_vault_nav populates, and is_nav_filled requires every entry to be computed).
19081
19415
  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);
19416
+ const configSymbols = this.getMarketSymbols();
19417
+ const configSymbolSet = new Set(configSymbols);
19418
+ const apiSymbols = (await this.getCachedTradingPairs())
19419
+ .map((pair) => pair.symbol)
19420
+ .filter((symbol) => Boolean(symbol) && !configSymbolSet.has(symbol));
19421
+ const updateFeeds = [
19422
+ ...(await this.fetchSignedPriceFeedChunks(configSymbols, 40)),
19423
+ ...(await this.fetchSignedPriceFeedChunks(apiSymbols, 40)),
19424
+ ];
19425
+ this.appendPriceFeedUpdates(t, updateFeeds);
19087
19426
  const perpetualIds = await this.getOnChainPerpetualIds();
19088
19427
  const [nav] = t.moveCall({
19089
19428
  target: `${vaultPkg}::vault::new_vault_nav`,
@@ -19118,8 +19457,22 @@ class DipCoinPerpSDK {
19118
19457
  async createVault(args) {
19119
19458
  const tx = new transactions.Transaction();
19120
19459
  const vaultPkg = this.getVaultPackageId();
19460
+ const creator = this.walletAddress;
19461
+ const maxCap = formatNormalToWei(args.maxCap);
19462
+ const minDepositAmount = formatNormalToWei(args.minDepositAmount);
19463
+ const initialAmount = formatNormalToWei(args.initialAmount);
19464
+ const signed = await this.signVaultCreatePayload({
19465
+ creator,
19466
+ name: args.name,
19467
+ trader: args.trader,
19468
+ maxCap,
19469
+ minDepositAmount,
19470
+ creatorMinimumShareRatio: args.creatorMinimumShareRatio,
19471
+ creatorProfitShareRatio: args.creatorProfitShareRatio,
19472
+ initialAmount,
19473
+ });
19121
19474
  tx.moveCall({
19122
- target: `${vaultPkg}::vault::create_vault`,
19475
+ target: `${vaultPkg}::vault::create_vault_v2`,
19123
19476
  arguments: [
19124
19477
  tx.object(this.getDeploymentProtocolConfigId()),
19125
19478
  tx.object("0x6"),
@@ -19127,13 +19480,18 @@ class DipCoinPerpSDK {
19127
19480
  tx.object(this.getSubAccountsId()),
19128
19481
  tx.object(this.getBankId()),
19129
19482
  tx.object(this.getTxIndexerId()),
19483
+ tx.pure.vector("u8", signed.userAddress),
19130
19484
  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)),
19485
+ tx.pure.vector("u8", this.toUnifiedSuiAddressBytes(args.trader)),
19486
+ tx.pure.u128(maxCap),
19487
+ tx.pure.u128(minDepositAmount),
19134
19488
  tx.pure.u128(args.creatorMinimumShareRatio),
19135
19489
  tx.pure.u128(args.creatorProfitShareRatio),
19136
- tx.pure.u128(formatNormalToWei(args.initialAmount)),
19490
+ tx.pure.u128(initialAmount),
19491
+ tx.pure.u128(signed.salt),
19492
+ tx.pure.u64(signed.expiration),
19493
+ tx.pure.vector("u8", signed.signature),
19494
+ tx.pure.vector("u8", signed.publicKey),
19137
19495
  ],
19138
19496
  typeArguments: [this.getCurrencyType()],
19139
19497
  });
@@ -19145,17 +19503,25 @@ class DipCoinPerpSDK {
19145
19503
  async depositToVault(args) {
19146
19504
  const { nav, tx } = await this.buildVaultNavTransaction(args.vaultID, args.markets);
19147
19505
  const vaultPkg = this.getVaultPackageId();
19506
+ const amount = formatNormalToWei(args.amount);
19507
+ const signed = await this.signVaultUserAmountPayload("VaultDeposit", args.vaultID, this.walletAddress, amount);
19148
19508
  tx.moveCall({
19149
- target: `${vaultPkg}::vault::deposit`,
19509
+ target: `${vaultPkg}::vault::deposit_v2`,
19150
19510
  arguments: [
19151
19511
  tx.object(this.getDeploymentProtocolConfigId()),
19152
19512
  tx.object(this.getVaultConfigId()),
19153
19513
  tx.object("0x6"),
19154
19514
  tx.object(this.getBankId()),
19515
+ tx.object(this.getSubAccountsId()),
19155
19516
  tx.object(this.getTxIndexerId()),
19156
19517
  tx.object(args.vaultID),
19157
19518
  nav,
19158
- tx.pure.u128(formatNormalToWei(args.amount)),
19519
+ tx.pure.vector("u8", signed.userAddress),
19520
+ tx.pure.u128(amount),
19521
+ tx.pure.u128(signed.salt),
19522
+ tx.pure.u64(signed.expiration),
19523
+ tx.pure.vector("u8", signed.signature),
19524
+ tx.pure.vector("u8", signed.publicKey),
19159
19525
  ],
19160
19526
  typeArguments: [this.getCurrencyType()],
19161
19527
  });
@@ -19166,32 +19532,25 @@ class DipCoinPerpSDK {
19166
19532
  */
19167
19533
  async requestWithdrawFromVault(args) {
19168
19534
  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
19535
  const vaultPkg = this.getVaultPackageId();
19536
+ const shares = formatNormalToWei(args.shares);
19537
+ const signed = await this.signVaultUserAmountPayload("VaultRequestWithdraw", args.vaultID, this.walletAddress, shares);
19187
19538
  tx.moveCall({
19188
- target: `${vaultPkg}::vault::request_withdraw`,
19539
+ target: `${vaultPkg}::vault::request_withdraw_v2`,
19189
19540
  arguments: [
19190
19541
  tx.object(this.getDeploymentProtocolConfigId()),
19191
19542
  tx.object(this.getVaultConfigId()),
19192
19543
  tx.object("0x6"),
19544
+ tx.object(this.getBankId()),
19545
+ tx.object(this.getSubAccountsId()),
19546
+ tx.object(this.getTxIndexerId()),
19193
19547
  tx.object(args.vaultID),
19194
- tx.pure.u128(formatNormalToWei(args.shares)),
19548
+ tx.pure.vector("u8", signed.userAddress),
19549
+ tx.pure.u128(shares),
19550
+ tx.pure.u128(signed.salt),
19551
+ tx.pure.u64(signed.expiration),
19552
+ tx.pure.vector("u8", signed.signature),
19553
+ tx.pure.vector("u8", signed.publicKey),
19195
19554
  ],
19196
19555
  typeArguments: [this.getCurrencyType()],
19197
19556
  });
@@ -19207,7 +19566,7 @@ class DipCoinPerpSDK {
19207
19566
  elements: args.withdrawalRequestIDs.map((id) => tx.object(id)),
19208
19567
  });
19209
19568
  tx.moveCall({
19210
- target: `${vaultPkg}::vault::fill_withdrawal_requests`,
19569
+ target: `${vaultPkg}::vault::fill_withdrawal_requests_v2`,
19211
19570
  arguments: [
19212
19571
  tx.object(this.getDeploymentProtocolConfigId()),
19213
19572
  tx.object(this.getVaultConfigId()),
@@ -19228,16 +19587,23 @@ class DipCoinPerpSDK {
19228
19587
  async closeVault(args) {
19229
19588
  const { nav, tx } = await this.buildVaultNavTransaction(args.vaultID, args.markets);
19230
19589
  const vaultPkg = this.getVaultPackageId();
19590
+ const signed = await this.signVaultUserPayload("VaultClose", args.vaultID, this.walletAddress);
19231
19591
  tx.moveCall({
19232
- target: `${vaultPkg}::vault::close_vault_v2`,
19592
+ target: `${vaultPkg}::vault::close_vault_v3`,
19233
19593
  arguments: [
19234
19594
  tx.object(this.getDeploymentProtocolConfigId()),
19235
19595
  tx.object(this.getVaultConfigId()),
19236
19596
  tx.object(this.getSubAccountsId()),
19597
+ tx.object(this.getTxIndexerId()),
19237
19598
  tx.object(args.vaultID),
19238
19599
  tx.object(this.getBankId()),
19239
19600
  tx.object("0x6"),
19240
19601
  nav,
19602
+ tx.pure.vector("u8", signed.userAddress),
19603
+ tx.pure.u128(signed.salt),
19604
+ tx.pure.u64(signed.expiration),
19605
+ tx.pure.vector("u8", signed.signature),
19606
+ tx.pure.vector("u8", signed.publicKey),
19241
19607
  ],
19242
19608
  typeArguments: [this.getCurrencyType()],
19243
19609
  });
@@ -19267,14 +19633,22 @@ class DipCoinPerpSDK {
19267
19633
  async claimClosedVaultFunds(args) {
19268
19634
  const tx = new transactions.Transaction();
19269
19635
  const vaultPkg = this.getVaultPackageId();
19636
+ const signed = await this.signVaultUserPayload("VaultClaimClosedFunds", args.vaultID, this.walletAddress);
19270
19637
  tx.moveCall({
19271
- target: `${vaultPkg}::vault::claim_closed_vault_funds`,
19638
+ target: `${vaultPkg}::vault::claim_closed_vault_funds_v2`,
19272
19639
  arguments: [
19273
19640
  tx.object(this.getDeploymentProtocolConfigId()),
19274
19641
  tx.object(this.getVaultConfigId()),
19275
19642
  tx.object(this.getBankId()),
19643
+ tx.object(this.getSubAccountsId()),
19276
19644
  tx.object(this.getTxIndexerId()),
19277
19645
  tx.object(args.vaultID),
19646
+ tx.object("0x6"),
19647
+ tx.pure.vector("u8", signed.userAddress),
19648
+ tx.pure.u128(signed.salt),
19649
+ tx.pure.u64(signed.expiration),
19650
+ tx.pure.vector("u8", signed.signature),
19651
+ tx.pure.vector("u8", signed.publicKey),
19278
19652
  ],
19279
19653
  typeArguments: [this.getCurrencyType()],
19280
19654
  });
@@ -19286,14 +19660,23 @@ class DipCoinPerpSDK {
19286
19660
  async setVaultTrader(args) {
19287
19661
  const tx = new transactions.Transaction();
19288
19662
  const vaultPkg = this.getVaultPackageId();
19663
+ const signed = await this.signVaultSetTraderPayload(args.vaultID, this.walletAddress, args.newTrader);
19289
19664
  tx.moveCall({
19290
- target: `${vaultPkg}::vault::set_trader`,
19665
+ target: `${vaultPkg}::vault::set_trader_v2`,
19291
19666
  arguments: [
19292
19667
  tx.object(this.getDeploymentProtocolConfigId()),
19293
19668
  tx.object(this.getVaultConfigId()),
19294
19669
  tx.object(args.vaultID),
19295
19670
  tx.object(this.getSubAccountsId()),
19296
- tx.pure.address(args.newTrader),
19671
+ tx.object(this.getTxIndexerId()),
19672
+ tx.object(this.getBankId()),
19673
+ tx.object("0x6"),
19674
+ tx.pure.vector("u8", signed.userAddress),
19675
+ tx.pure.vector("u8", this.toUnifiedSuiAddressBytes(args.newTrader)),
19676
+ tx.pure.u128(signed.salt),
19677
+ tx.pure.u64(signed.expiration),
19678
+ tx.pure.vector("u8", signed.signature),
19679
+ tx.pure.vector("u8", signed.publicKey),
19297
19680
  ],
19298
19681
  typeArguments: [this.getCurrencyType()],
19299
19682
  });
@@ -19305,15 +19688,24 @@ class DipCoinPerpSDK {
19305
19688
  async setVaultSubTrader(args) {
19306
19689
  const tx = new transactions.Transaction();
19307
19690
  const vaultPkg = this.getVaultPackageId();
19691
+ const signed = await this.signVaultSetSubTraderPayload(args.vaultID, this.walletAddress, args.subTrader, args.status);
19308
19692
  tx.moveCall({
19309
- target: `${vaultPkg}::vault::set_sub_trader`,
19693
+ target: `${vaultPkg}::vault::set_sub_trader_v2`,
19310
19694
  arguments: [
19311
19695
  tx.object(this.getDeploymentProtocolConfigId()),
19312
19696
  tx.object(this.getVaultConfigId()),
19313
19697
  tx.object(args.vaultID),
19314
19698
  tx.object(this.getSubAccountsId()),
19315
- tx.pure.address(args.subTrader),
19699
+ tx.object(this.getTxIndexerId()),
19700
+ tx.object(this.getBankId()),
19701
+ tx.object("0x6"),
19702
+ tx.pure.vector("u8", signed.userAddress),
19703
+ tx.pure.vector("u8", this.toUnifiedSuiAddressBytes(args.subTrader)),
19316
19704
  tx.pure.bool(args.status),
19705
+ tx.pure.u128(signed.salt),
19706
+ tx.pure.u64(signed.expiration),
19707
+ tx.pure.vector("u8", signed.signature),
19708
+ tx.pure.vector("u8", signed.publicKey),
19317
19709
  ],
19318
19710
  typeArguments: [this.getCurrencyType()],
19319
19711
  });
@@ -19325,13 +19717,23 @@ class DipCoinPerpSDK {
19325
19717
  async setVaultDepositStatus(args) {
19326
19718
  const tx = new transactions.Transaction();
19327
19719
  const vaultPkg = this.getVaultPackageId();
19720
+ const signed = await this.signVaultDepositStatusPayload(args.vaultID, this.walletAddress, args.status);
19328
19721
  tx.moveCall({
19329
- target: `${vaultPkg}::vault::set_deposit_status`,
19722
+ target: `${vaultPkg}::vault::set_deposit_status_v2`,
19330
19723
  arguments: [
19331
19724
  tx.object(this.getDeploymentProtocolConfigId()),
19332
19725
  tx.object(this.getVaultConfigId()),
19333
19726
  tx.object(args.vaultID),
19727
+ tx.object(this.getSubAccountsId()),
19728
+ tx.object(this.getTxIndexerId()),
19729
+ tx.object(this.getBankId()),
19730
+ tx.object("0x6"),
19731
+ tx.pure.vector("u8", signed.userAddress),
19334
19732
  tx.pure.bool(args.status),
19733
+ tx.pure.u128(signed.salt),
19734
+ tx.pure.u64(signed.expiration),
19735
+ tx.pure.vector("u8", signed.signature),
19736
+ tx.pure.vector("u8", signed.publicKey),
19335
19737
  ],
19336
19738
  typeArguments: [this.getCurrencyType()],
19337
19739
  });
@@ -19343,13 +19745,24 @@ class DipCoinPerpSDK {
19343
19745
  async setVaultMaxCap(args) {
19344
19746
  const tx = new transactions.Transaction();
19345
19747
  const vaultPkg = this.getVaultPackageId();
19748
+ const amount = formatNormalToWei(args.maxCap);
19749
+ const signed = await this.signVaultCreatorAmountPayload("VaultSetMaxCap", args.vaultID, this.walletAddress, amount);
19346
19750
  tx.moveCall({
19347
- target: `${vaultPkg}::vault::set_max_cap`,
19751
+ target: `${vaultPkg}::vault::set_max_cap_v2`,
19348
19752
  arguments: [
19349
19753
  tx.object(this.getDeploymentProtocolConfigId()),
19350
19754
  tx.object(this.getVaultConfigId()),
19351
19755
  tx.object(args.vaultID),
19352
- tx.pure.u128(formatNormalToWei(args.maxCap)),
19756
+ tx.object(this.getSubAccountsId()),
19757
+ tx.object(this.getTxIndexerId()),
19758
+ tx.object(this.getBankId()),
19759
+ tx.object("0x6"),
19760
+ tx.pure.vector("u8", signed.userAddress),
19761
+ tx.pure.u128(amount),
19762
+ tx.pure.u128(signed.salt),
19763
+ tx.pure.u64(signed.expiration),
19764
+ tx.pure.vector("u8", signed.signature),
19765
+ tx.pure.vector("u8", signed.publicKey),
19353
19766
  ],
19354
19767
  typeArguments: [this.getCurrencyType()],
19355
19768
  });
@@ -19361,13 +19774,24 @@ class DipCoinPerpSDK {
19361
19774
  async setVaultMinDepositAmount(args) {
19362
19775
  const tx = new transactions.Transaction();
19363
19776
  const vaultPkg = this.getVaultPackageId();
19777
+ const amount = formatNormalToWei(args.minDepositAmount);
19778
+ const signed = await this.signVaultCreatorAmountPayload("VaultSetMinDepositAmount", args.vaultID, this.walletAddress, amount);
19364
19779
  tx.moveCall({
19365
- target: `${vaultPkg}::vault::set_min_deposit_amount`,
19780
+ target: `${vaultPkg}::vault::set_min_deposit_amount_v2`,
19366
19781
  arguments: [
19367
19782
  tx.object(this.getDeploymentProtocolConfigId()),
19368
19783
  tx.object(this.getVaultConfigId()),
19369
19784
  tx.object(args.vaultID),
19370
- tx.pure.u128(formatNormalToWei(args.minDepositAmount)),
19785
+ tx.object(this.getSubAccountsId()),
19786
+ tx.object(this.getTxIndexerId()),
19787
+ tx.object(this.getBankId()),
19788
+ tx.object("0x6"),
19789
+ tx.pure.vector("u8", signed.userAddress),
19790
+ tx.pure.u128(amount),
19791
+ tx.pure.u128(signed.salt),
19792
+ tx.pure.u64(signed.expiration),
19793
+ tx.pure.vector("u8", signed.signature),
19794
+ tx.pure.vector("u8", signed.publicKey),
19371
19795
  ],
19372
19796
  typeArguments: [this.getCurrencyType()],
19373
19797
  });
@@ -19379,13 +19803,23 @@ class DipCoinPerpSDK {
19379
19803
  async setVaultAutoCloseOnWithdraw(args) {
19380
19804
  const tx = new transactions.Transaction();
19381
19805
  const vaultPkg = this.getVaultPackageId();
19806
+ const signed = await this.signVaultAutoClosePayload(args.vaultID, this.walletAddress, args.autoCloseOnWithdraw);
19382
19807
  tx.moveCall({
19383
- target: `${vaultPkg}::vault::set_auto_close_on_withdraw`,
19808
+ target: `${vaultPkg}::vault::set_auto_close_on_withdraw_v2`,
19384
19809
  arguments: [
19385
19810
  tx.object(this.getDeploymentProtocolConfigId()),
19386
19811
  tx.object(this.getVaultConfigId()),
19387
19812
  tx.object(args.vaultID),
19813
+ tx.object(this.getSubAccountsId()),
19814
+ tx.object(this.getTxIndexerId()),
19815
+ tx.object(this.getBankId()),
19816
+ tx.object("0x6"),
19817
+ tx.pure.vector("u8", signed.userAddress),
19388
19818
  tx.pure.bool(args.autoCloseOnWithdraw),
19819
+ tx.pure.u128(signed.salt),
19820
+ tx.pure.u64(signed.expiration),
19821
+ tx.pure.vector("u8", signed.signature),
19822
+ tx.pure.vector("u8", signed.publicKey),
19389
19823
  ],
19390
19824
  typeArguments: [this.getCurrencyType()],
19391
19825
  });
@@ -19660,12 +20094,12 @@ function initDipCoinPerpSDK(privateKey, options) {
19660
20094
 
19661
20095
  const globalEnv = path__default["default"].join(os__default["default"].homedir(), ".config", "dipcoin", "env");
19662
20096
  const localEnv = path__default["default"].resolve(process.cwd(), ".env");
20097
+ if (fs__default["default"].existsSync(globalEnv)) {
20098
+ dotenv__default["default"].config({ path: globalEnv });
20099
+ }
19663
20100
  if (fs__default["default"].existsSync(localEnv)) {
19664
20101
  dotenv__default["default"].config({ path: localEnv });
19665
20102
  }
19666
- else if (fs__default["default"].existsSync(globalEnv)) {
19667
- dotenv__default["default"].config({ path: globalEnv });
19668
- }
19669
20103
  // Cache SDK instance
19670
20104
  let cachedSDK = null;
19671
20105
  /**
@@ -19724,7 +20158,8 @@ function getSDK() {
19724
20158
  return cachedSDK;
19725
20159
  const keypair = getKeypair();
19726
20160
  const network = getNetwork();
19727
- cachedSDK = initDipCoinPerpSDK(keypair, { network });
20161
+ const customRpc = process.env.DIPCOIN_RPC_URL || process.env.SUI_RPC_URL;
20162
+ cachedSDK = initDipCoinPerpSDK(keypair, { network, customRpc });
19728
20163
  tryAutoBindReferral(cachedSDK);
19729
20164
  return cachedSDK;
19730
20165
  }