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 +665 -135
- package/dist/cli.cjs.map +1 -1
- package/dist/config/deployed/mainnet/main_contract.json +4 -3
- package/package.json +2 -2
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
|
-
|
|
12925
|
-
|
|
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
|
|
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) => {
|
|
16461
|
-
|
|
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
|
-
|
|
16657
|
-
|
|
16658
|
-
|
|
16659
|
-
|
|
16660
|
-
|
|
16661
|
-
|
|
16662
|
-
|
|
16663
|
-
|
|
16664
|
-
|
|
16665
|
-
|
|
16666
|
-
|
|
16667
|
-
|
|
16668
|
-
|
|
16669
|
-
|
|
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
|
-
|
|
16681
|
-
|
|
16682
|
-
|
|
16683
|
-
|
|
16684
|
-
|
|
16685
|
-
|
|
16686
|
-
|
|
16687
|
-
|
|
16688
|
-
|
|
16689
|
-
|
|
16690
|
-
|
|
16691
|
-
|
|
16692
|
-
|
|
16693
|
-
|
|
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
|
|
16764
|
-
|
|
16765
|
-
|
|
16766
|
-
|
|
16767
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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 {
|
|
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
|
-
|
|
19014
|
-
|
|
19015
|
-
|
|
19016
|
-
|
|
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
|
-
|
|
19019
|
-
|
|
19020
|
-
|
|
19021
|
-
|
|
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
|
-
|
|
19024
|
-
|
|
19025
|
-
|
|
19026
|
-
|
|
19027
|
-
|
|
19028
|
-
|
|
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
|
|
19083
|
-
const
|
|
19084
|
-
|
|
19085
|
-
|
|
19086
|
-
|
|
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
|
-
|
|
19109
|
-
|
|
19110
|
-
|
|
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::
|
|
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.
|
|
19132
|
-
tx.pure.u128(
|
|
19133
|
-
tx.pure.u128(
|
|
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(
|
|
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::
|
|
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.
|
|
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::
|
|
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.
|
|
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::
|
|
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::
|
|
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::
|
|
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::
|
|
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.
|
|
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::
|
|
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.
|
|
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::
|
|
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::
|
|
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.
|
|
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::
|
|
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.
|
|
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::
|
|
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
|
-
|
|
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
|
|
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)
|
|
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([
|
|
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
|
-
[
|
|
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({
|
|
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({
|
|
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) {
|