dipcoin-cli 1.2.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -16596,6 +16596,13 @@ function getPerpetualId(deployment, market) {
16596
16596
  throw new Error(`Deployment config missing Perpetual id for ${m}`);
16597
16597
  return id;
16598
16598
  }
16599
+ function getPriceFeedId(deployment) {
16600
+ const id = deployment?.objects?.PriceFeed?.id;
16601
+ if (!id) {
16602
+ throw new Error("Deployment config missing PriceFeed id. Add objects.PriceFeed to the network's main_contract.json — required for v6+ on-chain margin/vault writes.");
16603
+ }
16604
+ return id;
16605
+ }
16599
16606
  function getPriceOracleObjectId(deployment, market) {
16600
16607
  const m = market?.toUpperCase() || "ETH-PERP";
16601
16608
  const id = deployment?.markets?.[m]?.Objects?.PriceInfoObject?.id;
@@ -16641,7 +16648,7 @@ function buildAddMarginTx(deployment, args, tx, gasBudget, sender) {
16641
16648
  t.setSender(sender);
16642
16649
  const packageId = getPackageId(deployment);
16643
16650
  t.moveCall({
16644
- target: `${packageId}::exchange::add_margin_v2`,
16651
+ target: `${packageId}::exchange::add_margin_v3`,
16645
16652
  arguments: [
16646
16653
  t.object(getProtocolConfigId(deployment)),
16647
16654
  t.object(CLOCK_OBJECT_ID),
@@ -16649,7 +16656,7 @@ function buildAddMarginTx(deployment, args, tx, gasBudget, sender) {
16649
16656
  t.object(getBankId(deployment)),
16650
16657
  t.object(args.subAccountsMapID || getSubAccountsId(deployment)),
16651
16658
  t.object(getTxIndexerId(deployment)),
16652
- t.object(getPriceOracleObjectId(deployment, args.market)),
16659
+ t.object(getPriceFeedId(deployment)),
16653
16660
  t.pure.address(args.account || sender || ""),
16654
16661
  t.pure.u128(args.amount.toString()),
16655
16662
  ],
@@ -16665,7 +16672,7 @@ function buildRemoveMarginTx(deployment, args, tx, gasBudget, sender) {
16665
16672
  t.setSender(sender);
16666
16673
  const packageId = getPackageId(deployment);
16667
16674
  t.moveCall({
16668
- target: `${packageId}::exchange::remove_margin_v2`,
16675
+ target: `${packageId}::exchange::remove_margin_v3`,
16669
16676
  arguments: [
16670
16677
  t.object(getProtocolConfigId(deployment)),
16671
16678
  t.object(CLOCK_OBJECT_ID),
@@ -16673,7 +16680,7 @@ function buildRemoveMarginTx(deployment, args, tx, gasBudget, sender) {
16673
16680
  t.object(getBankId(deployment)),
16674
16681
  t.object(args.subAccountsMapID || getSubAccountsId(deployment)),
16675
16682
  t.object(getTxIndexerId(deployment)),
16676
- t.object(getPriceOracleObjectId(deployment, args.market)),
16683
+ t.object(getPriceFeedId(deployment)),
16677
16684
  t.pure.address(args.account || sender || ""),
16678
16685
  t.pure.u128(args.amount.toString()),
16679
16686
  ],
@@ -16744,17 +16751,6 @@ function buildSetOraclePriceTx(deployment, args, tx, gasBudget, sender) {
16744
16751
  });
16745
16752
  return t;
16746
16753
  }
16747
- function buildBatchSetOraclePriceTx(deployment, args, tx, gasBudget, sender) {
16748
- let t = tx || new transactions.Transaction();
16749
- for (const priceArgs of args.prices) {
16750
- t = buildSetOraclePriceTx(deployment, priceArgs, t, undefined, undefined);
16751
- }
16752
- if (gasBudget)
16753
- t.setGasBudget(gasBudget);
16754
- if (sender)
16755
- t.setSender(sender);
16756
- return t;
16757
- }
16758
16754
 
16759
16755
  // Copyright (c) 2025 Dipcoin LLC
16760
16756
  async function executeTxBlock(suiClient, tx, signer) {
@@ -18624,10 +18620,9 @@ class DipCoinPerpSDK {
18624
18620
  }
18625
18621
  async buildMarginTransaction(params, action) {
18626
18622
  const payload = this.buildMarginCallArgs(params, action);
18627
- const updatePriceTx = payload.market
18628
- ? await this.buildUpdatePriceTransaction(payload.market)
18629
- : undefined;
18630
- const baseTx = updatePriceTx || new transactions.Transaction();
18623
+ // v6+ uses add_margin_v3/remove_margin_v3 which read &PriceFeed (a shared object
18624
+ // maintained off-band by an operator). No Pyth update is needed on the user-side tx.
18625
+ const baseTx = new transactions.Transaction();
18631
18626
  if (action === "add") {
18632
18627
  return buildAddMarginTx(this.deploymentConfig, payload, baseTx, params.gasBudget);
18633
18628
  }
@@ -18913,45 +18908,40 @@ class DipCoinPerpSDK {
18913
18908
  throw new Error(`Deployment config missing PriceInfoObject id for ${symbol}`);
18914
18909
  return id;
18915
18910
  }
18916
- async buildVaultNavTransaction(vaultID, markets, tx) {
18911
+ getPriceFeedId() {
18912
+ const id = this.deploymentConfig?.objects?.PriceFeed?.id;
18913
+ if (!id) {
18914
+ throw new Error("Deployment config missing PriceFeed id. Add objects.PriceFeed to the network's main_contract.json — required for v6+ vault/margin writes.");
18915
+ }
18916
+ return id;
18917
+ }
18918
+ async getOnChainPerpetualIds() {
18919
+ const pcId = this.getDeploymentProtocolConfigId();
18920
+ const df = await this.suiClient.getDynamicFieldObject({
18921
+ parentId: pcId,
18922
+ name: {
18923
+ type: "vector<u8>",
18924
+ value: Array.from(Buffer.from("perpetual_registry", "utf8")),
18925
+ },
18926
+ });
18927
+ const content = df.data?.content;
18928
+ const contents = content?.fields?.value?.fields?.perpetuals?.fields?.contents;
18929
+ if (!Array.isArray(contents)) {
18930
+ throw new Error("Could not read perpetual_registry from on-chain ProtocolConfig. Vault NAV requires the full list of perpetuals registered in the protocol.");
18931
+ }
18932
+ return contents;
18933
+ }
18934
+ async buildVaultNavTransaction(vaultID, _markets, tx) {
18917
18935
  const t = tx || new transactions.Transaction();
18918
18936
  const vaultPkg = this.getVaultPackageId();
18919
18937
  const currencyType = this.getCurrencyType();
18920
- const symbols = markets && markets.length > 0 ? markets : this.getMarketSymbols();
18921
- if (this.options.network === "mainnet") {
18922
- // On mainnet, use Pyth to fetch fresh price VAAs and update on-chain oracles
18923
- this.ensurePythClients();
18924
- if (this.priceServiceConnection && this.pythClient) {
18925
- const priceIds = [];
18926
- for (const sym of symbols) {
18927
- const feedId = await this.resolvePriceFeedId(sym);
18928
- if (feedId)
18929
- priceIds.push(feedId);
18930
- }
18931
- if (priceIds.length > 0) {
18932
- const priceUpdateData = await this.priceServiceConnection.getPriceFeedsUpdateData(priceIds);
18933
- await this.pythClient.updatePriceFeeds(t, priceUpdateData, priceIds);
18934
- }
18935
- }
18936
- }
18937
- else {
18938
- // On testnet, prepend price oracle updates so PriceInfoObjects are fresh
18939
- const prices = [];
18940
- for (const sym of symbols) {
18941
- try {
18942
- const oraclePrice = await getOraclePrice(this.suiClient, this.deploymentConfig, sym);
18943
- if (oraclePrice !== undefined && oraclePrice !== null) {
18944
- prices.push({ price: Number(oraclePrice), market: sym });
18945
- }
18946
- }
18947
- catch {
18948
- // skip symbols where oracle price is unavailable
18949
- }
18950
- }
18951
- if (prices.length > 0) {
18952
- buildBatchSetOraclePriceTx(this.deploymentConfig, { prices }, t);
18953
- }
18954
- }
18938
+ // v6+: PriceFeed is a single shared object maintained by an operator off-band.
18939
+ // new_vault_nav populates `perpetuals` from ProtocolConfig.perpetual_registry, and
18940
+ // is_nav_filled requires EVERY registered perpetual to have a position value entry.
18941
+ // So we must compute against the on-chain registry, not the local deployment.markets
18942
+ // list (which can drift behind chain additions).
18943
+ const priceFeedId = this.getPriceFeedId();
18944
+ const perpetualIds = await this.getOnChainPerpetualIds();
18955
18945
  const [nav] = t.moveCall({
18956
18946
  target: `${vaultPkg}::vault::new_vault_nav`,
18957
18947
  arguments: [
@@ -18962,12 +18952,10 @@ class DipCoinPerpSDK {
18962
18952
  ],
18963
18953
  typeArguments: [currencyType],
18964
18954
  });
18965
- for (const sym of symbols) {
18966
- const perpId = this.getVaultPerpetualId(sym);
18967
- const priceInfoId = this.getVaultPriceInfoObjectId(sym);
18955
+ for (const perpId of perpetualIds) {
18968
18956
  t.moveCall({
18969
- target: `${vaultPkg}::vault::compute_perpetual_position_value`,
18970
- arguments: [nav, t.object("0x6"), t.object(perpId), t.object(priceInfoId)],
18957
+ target: `${vaultPkg}::vault::compute_perpetual_position_value_v2`,
18958
+ arguments: [nav, t.object("0x6"), t.object(perpId), t.object(priceFeedId)],
18971
18959
  typeArguments: [],
18972
18960
  });
18973
18961
  }
@@ -19996,11 +19984,16 @@ function registerPositionCommands(program) {
19996
19984
  .description("Add margin to position")
19997
19985
  .argument("<symbol>", "Trading pair")
19998
19986
  .argument("<amount>", "Amount in USDC")
19999
- .action(async (symbol, amount) => {
19987
+ .option("--vault <address>", "Target a vault's position (vault id) instead of personal account")
19988
+ .action(async (symbol, amount, opts) => {
20000
19989
  try {
20001
19990
  const sdk = getSDK();
20002
19991
  symbol = normalizeSymbol(symbol);
20003
- const tx = await sdk.addMargin({ symbol, amount: parseAmount(amount) });
19992
+ const tx = await sdk.addMargin({
19993
+ symbol,
19994
+ amount: parseAmount(amount),
19995
+ accountAddress: opts.vault,
19996
+ });
20004
19997
  if (isJson(program))
20005
19998
  return printJson({ digest: tx?.digest, status: "ok" });
20006
19999
  console.log(`Added ${amount} margin to ${symbol}. Tx: ${tx?.digest || JSON.stringify(tx)}`);
@@ -20014,11 +20007,16 @@ function registerPositionCommands(program) {
20014
20007
  .description("Remove margin from position")
20015
20008
  .argument("<symbol>", "Trading pair")
20016
20009
  .argument("<amount>", "Amount in USDC")
20017
- .action(async (symbol, amount) => {
20010
+ .option("--vault <address>", "Target a vault's position (vault id) instead of personal account")
20011
+ .action(async (symbol, amount, opts) => {
20018
20012
  try {
20019
20013
  const sdk = getSDK();
20020
20014
  symbol = normalizeSymbol(symbol);
20021
- const tx = await sdk.removeMargin({ symbol, amount: parseAmount(amount) });
20015
+ const tx = await sdk.removeMargin({
20016
+ symbol,
20017
+ amount: parseAmount(amount),
20018
+ accountAddress: opts.vault,
20019
+ });
20022
20020
  if (isJson(program))
20023
20021
  return printJson({ digest: tx?.digest, status: "ok" });
20024
20022
  console.log(`Removed ${amount} margin from ${symbol}. Tx: ${tx?.digest || JSON.stringify(tx)}`);