@drift-labs/common
Version:
Common functions for Drift
624 lines • 26.8 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.COMMON_UI_UTILS = void 0;
const sdk_1 = require("@drift-labs/sdk");
const utils_1 = require("../utils");
const web3_js_1 = require("@solana/web3.js");
const bcryptjs_react_1 = __importDefault(require("bcryptjs-react"));
const tweetnacl_1 = __importStar(require("tweetnacl"));
const spl_token_1 = require("@solana/spl-token");
const user_1 = require("./user");
const trading_1 = require("./trading");
const market_1 = require("./market");
const order_1 = require("./order");
const trade_1 = require("../constants/trade");
// When creating an account, try 5 times over 5 seconds to wait for the new account to hit the blockchain.
const ACCOUNT_INITIALIZATION_RETRY_DELAY_MS = 1000;
const ACCOUNT_INITIALIZATION_RETRY_ATTEMPTS = 5;
const abbreviateAddress = (address, length = 4) => {
if (!address)
return '';
const authString = address.toString();
return `${authString.slice(0, length)}...${authString.slice(-length)}`;
};
/**
* Get a unique key for an authority's subaccount
* @param userId
* @param authority
* @returns
*/
const getUserKey = (userId, authority) => {
if (userId == undefined || !authority)
return '';
return `${userId}_${authority.toString()}`;
};
/**
* Get the authority and subAccountId from a user's account key
* @param key
* @returns
*/
const getIdAndAuthorityFromKey = (key) => {
const splitKey = key === null || key === void 0 ? void 0 : key.split('_');
if (!splitKey || splitKey.length !== 2)
return { userId: undefined, userAuthority: undefined };
return {
userId: Number(splitKey[0]),
userAuthority: new sdk_1.PublicKey(splitKey[1]),
};
};
const fetchCurrentSubaccounts = (driftClient) => {
return driftClient.getUsers().map((user) => user.getUserAccount());
};
const fetchUserClientsAndAccounts = (driftClient) => {
const accounts = fetchCurrentSubaccounts(driftClient);
const allUsersAndUserAccounts = accounts.map((acct) => {
return {
user: driftClient.getUser(acct.subAccountId, acct.authority),
userAccount: acct,
};
});
return allUsersAndUserAccounts;
};
const awaitAccountInitializationChainState = async (driftClient, userId, authority) => {
var _a;
const user = driftClient.getUser(userId, authority);
if (!user.isSubscribed) {
await user.subscribe();
}
let retryCount = 0;
do {
try {
await updateUserAccount(user);
if (((_a = user === null || user === void 0 ? void 0 : user.getUserAccountAndSlot()) === null || _a === void 0 ? void 0 : _a.data) !== undefined) {
return true;
}
}
catch (err) {
retryCount++;
await (0, utils_1.sleep)(ACCOUNT_INITIALIZATION_RETRY_DELAY_MS);
}
} while (retryCount < ACCOUNT_INITIALIZATION_RETRY_ATTEMPTS);
throw new Error('awaitAccountInitializationFailed');
};
/**
* Using your own callback to do the account initialization, this method will run the initialization step, switch to the drift user, await for the account to be available on chain, subscribe to the user account, and switch to the user account using the drift client.
*
* It provides extra callbacks to handle steps directly after the initialiation tx, and after fully initializing+subscribing to the account.
*
* Callbacks available:
* - initializationStep: This callback should send the transaction to initialize the user account
* - postInitializationStep: This callback will run after the successful initialization transaction, but before trying to load/subscribe to the new account
* - handleSuccessStep: This callback will run after everything has initialized+subscribed successfully
*
* // TODO : Need to do the subscription step
*/
const initializeAndSubscribeToNewUserAccount = async (driftClient, userIdToInit, authority, callbacks) => {
var _a;
await driftClient.addUser(userIdToInit, authority);
const accountAlreadyExisted = await ((_a = driftClient
.getUser(userIdToInit)) === null || _a === void 0 ? void 0 : _a.exists());
// Do the account initialization step
let result = await callbacks.initializationStep();
// Fetch account to make sure it's loaded
await updateUserAccount(driftClient.getUser(userIdToInit));
if (!result) {
return 'failed_initializationStep';
}
// Do the post-initialization step
result = callbacks.postInitializationStep
? await callbacks.postInitializationStep()
: result;
if (!result) {
return 'failed_postInitializationStep';
}
// Await the account initialization step to update the blockchain
result = await awaitAccountInitializationChainState(driftClient, userIdToInit, authority);
if (!result) {
return 'failed_awaitAccountInitializationChainState';
}
await driftClient.switchActiveUser(userIdToInit, authority);
// Do the subscription step
// Run the success handler
result = callbacks.handleSuccessStep
? await callbacks.handleSuccessStep(accountAlreadyExisted)
: result;
if (!result) {
return 'failed_handleSuccessStep';
}
return 'ok';
};
async function updateUserAccount(user) {
const publicKey = user.userAccountPublicKey;
try {
const dataAndContext = await user.driftClient.program.account.user.fetchAndContext(publicKey, 'processed');
user.accountSubscriber.updateData(dataAndContext.data, dataAndContext.context.slot);
}
catch (e) {
// noop
}
}
const getMarketKey = (marketIndex, marketType) => `${utils_1.ENUM_UTILS.toStr(marketType)}_${marketIndex}`;
/**
* @deprecated Use createPlaceholderIWallet instead, since this is poorly named.
*/
const createThrowawayIWallet = (walletPubKey) => {
const newKeypair = walletPubKey
? new web3_js_1.Keypair({
publicKey: walletPubKey.toBytes(),
secretKey: new web3_js_1.Keypair().publicKey.toBytes(),
})
: new web3_js_1.Keypair();
const newWallet = {
publicKey: newKeypair.publicKey,
//@ts-ignore
signTransaction: () => {
return Promise.resolve();
},
//@ts-ignore
signAllTransactions: () => {
return Promise.resolve();
},
};
return newWallet;
};
/**
* Creates an IWallet wrapper, with redundant methods. If a `walletPubKey` is passed in,
* the `publicKey` will be based on that.
*/
const createPlaceholderIWallet = (walletPubKey) => {
const newKeypair = walletPubKey
? new web3_js_1.Keypair({
publicKey: walletPubKey.toBytes(),
secretKey: new web3_js_1.Keypair().publicKey.toBytes(),
})
: new web3_js_1.Keypair();
const newWallet = {
publicKey: newKeypair.publicKey,
//@ts-ignore
signTransaction: () => {
return Promise.resolve();
},
//@ts-ignore
signAllTransactions: () => {
return Promise.resolve();
},
};
return newWallet;
};
const getSignatureVerificationMessageForSettings = (authority, signTs) => {
return new TextEncoder().encode(`Verify you are the owner of this wallet to update trade settings: \n${authority.toBase58()}\n\nThis signature will be valid for the next 30 minutes.\n\nTS: ${signTs.toString()}`);
};
const verifySignature = (signature, message, pubKey) => {
return tweetnacl_1.sign.detached.verify(message, signature, pubKey.toBytes());
};
const hashSignature = async (signature) => {
bcryptjs_react_1.default.setRandomFallback((num) => {
return Array.from(tweetnacl_1.default.randomBytes(num));
});
const hashedSignature = await bcryptjs_react_1.default.hash(signature, bcryptjs_react_1.default.genSaltSync(10));
return hashedSignature;
};
const compareSignatures = async (original, hashed) => {
const signaturesMatch = await bcryptjs_react_1.default.compare(original, hashed);
return signaturesMatch;
};
/* Trading-related helper functions */
const calculateAverageEntryPrice = (quoteAssetAmount, baseAssetAmount) => {
if (baseAssetAmount.eqZero())
return sdk_1.BigNum.zero();
return sdk_1.BigNum.from(quoteAssetAmount.val
.mul(sdk_1.PRICE_PRECISION)
.mul(sdk_1.AMM_TO_QUOTE_PRECISION_RATIO)
.div(baseAssetAmount.shiftTo(sdk_1.BASE_PRECISION_EXP).val)
.abs(), sdk_1.PRICE_PRECISION_EXP);
};
const getMarketOrderLimitPrice = ({ direction, baselinePrice, slippageTolerance, }) => {
let limitPrice;
if (!baselinePrice)
return sdk_1.ZERO;
if (slippageTolerance === 0)
return baselinePrice;
// infinite slippage capped at 15% currently
if (slippageTolerance == undefined)
slippageTolerance = 15;
// if manually entered, cap at 99%
if (slippageTolerance > 99)
slippageTolerance = 99;
let limitPricePctDiff;
if ((0, sdk_1.isVariant)(direction, 'long')) {
limitPricePctDiff = sdk_1.PRICE_PRECISION.add(new sdk_1.BN(slippageTolerance * sdk_1.PRICE_PRECISION.toNumber()).div(new sdk_1.BN(100)));
limitPrice = baselinePrice.mul(limitPricePctDiff).div(sdk_1.PRICE_PRECISION);
}
else {
limitPricePctDiff = sdk_1.PRICE_PRECISION.sub(new sdk_1.BN(slippageTolerance * sdk_1.PRICE_PRECISION.toNumber()).div(new sdk_1.BN(100)));
limitPrice = baselinePrice.mul(limitPricePctDiff).div(sdk_1.PRICE_PRECISION);
}
return limitPrice;
};
const getMarketAuctionParams = ({ direction, startPriceFromSettings, endPriceFromSettings, limitPrice, duration, auctionStartPriceOffset, auctionEndPriceOffset, additionalEndPriceBuffer, forceUpToSlippage, }) => {
let auctionStartPrice;
let auctionEndPrice;
let constrainedBySlippage;
const auctionEndPriceBuffer = sdk_1.BigNum.from(sdk_1.PRICE_PRECISION).scale(Math.abs(auctionEndPriceOffset * 100), 10000).val;
const auctionStartPriceBuffer = sdk_1.BigNum.from(startPriceFromSettings).scale(Math.abs(auctionStartPriceOffset * 100), 10000).val;
if ((0, sdk_1.isVariant)(direction, 'long')) {
auctionStartPrice = startPriceFromSettings.sub(auctionStartPriceBuffer);
const worstPriceToUse = sdk_1.BN.max(endPriceFromSettings, startPriceFromSettings); // Handles edge cases like if the worst price on the book was better than the oracle price, and the settings are asking to be relative to the oracle price
auctionEndPrice = sdk_1.PRICE_PRECISION.add(auctionEndPriceBuffer)
.mul(worstPriceToUse)
.div(sdk_1.PRICE_PRECISION);
constrainedBySlippage = limitPrice.lt(auctionEndPrice);
// if forceUpToSlippage is passed, use max slippage price as end price
if (forceUpToSlippage) {
auctionEndPrice = limitPrice;
constrainedBySlippage = false;
}
else {
// use BEST (limit price, auction end price) as end price
auctionEndPrice = sdk_1.BN.min(limitPrice, auctionEndPrice);
}
// apply additional buffer if provided
if (additionalEndPriceBuffer) {
auctionEndPrice = auctionEndPrice.add(additionalEndPriceBuffer);
constrainedBySlippage = limitPrice.lt(auctionEndPrice);
}
auctionStartPrice = sdk_1.BN.min(auctionStartPrice, auctionEndPrice);
}
else {
auctionStartPrice = startPriceFromSettings.add(auctionStartPriceBuffer);
const worstPriceToUse = sdk_1.BN.min(endPriceFromSettings, startPriceFromSettings); // Handles edge cases like if the worst price on the book was better than the oracle price, and the settings are asking to be relative to the oracle price
auctionEndPrice = sdk_1.PRICE_PRECISION.sub(auctionEndPriceBuffer)
.mul(worstPriceToUse)
.div(sdk_1.PRICE_PRECISION);
constrainedBySlippage = limitPrice.gt(auctionEndPrice);
// if forceUpToSlippage is passed, use max slippage price as end price
if (forceUpToSlippage) {
auctionEndPrice = limitPrice;
constrainedBySlippage = false;
}
else {
// use BEST (limit price, auction end price) as end price
auctionEndPrice = sdk_1.BN.max(limitPrice, auctionEndPrice);
}
// apply additional buffer if provided
if (additionalEndPriceBuffer) {
auctionEndPrice = auctionEndPrice.sub(additionalEndPriceBuffer);
constrainedBySlippage = limitPrice.gt(auctionEndPrice);
}
auctionStartPrice = sdk_1.BN.max(auctionStartPrice, auctionEndPrice);
}
return {
auctionStartPrice,
auctionEndPrice,
auctionDuration: duration,
constrainedBySlippage,
};
};
/**
* Helper function which derived market order params from the CORE data that is used to create them.
* @param param0
* @returns
*/
const deriveMarketOrderParams = ({ marketType, marketIndex, direction, maxLeverageSelected, maxLeverageOrderSize, baseAmount, reduceOnly, allowInfSlippage, oraclePrice, bestPrice, entryPrice, worstPrice, markPrice, auctionDuration, auctionStartPriceOffset, auctionEndPriceOffset, auctionStartPriceOffsetFrom, auctionEndPriceOffsetFrom, auctionPriceCaps, slippageTolerance, isOracleOrder, additionalEndPriceBuffer, forceUpToSlippage, }) => {
const priceObject = getPriceObject({
oraclePrice,
markPrice,
bestOffer: bestPrice,
entryPrice,
worstPrice,
direction,
});
// max slippage price
let limitPrice = getMarketOrderLimitPrice({
direction,
baselinePrice: priceObject[auctionStartPriceOffsetFrom],
slippageTolerance: allowInfSlippage ? undefined : slippageTolerance,
});
if (additionalEndPriceBuffer) {
limitPrice = (0, sdk_1.isVariant)(direction, 'long')
? limitPrice.add(additionalEndPriceBuffer)
: limitPrice.sub(additionalEndPriceBuffer);
}
const auctionParams = getMarketAuctionParams({
direction,
startPriceFromSettings: priceObject[auctionStartPriceOffsetFrom],
endPriceFromSettings: priceObject[auctionEndPriceOffsetFrom],
limitPrice,
duration: auctionDuration,
auctionStartPriceOffset: auctionStartPriceOffset,
auctionEndPriceOffset: auctionEndPriceOffset,
additionalEndPriceBuffer,
forceUpToSlippage,
});
let orderParams = (0, sdk_1.getMarketOrderParams)({
marketType,
marketIndex,
direction,
baseAssetAmount: maxLeverageSelected ? maxLeverageOrderSize : baseAmount,
reduceOnly,
price: allowInfSlippage ? undefined : limitPrice,
...auctionParams,
});
if (isOracleOrder) {
// wont work if oracle is zero
if (!oraclePrice.eq(sdk_1.ZERO)) {
// BEST (slippageLimitPrice, auctionEndPrice)
const oracleAuctionEndPrice = (0, sdk_1.isVariant)(direction, 'long')
? sdk_1.BN.min(limitPrice, auctionParams.auctionEndPrice)
: sdk_1.BN.max(limitPrice, auctionParams.auctionEndPrice);
const oracleAuctionParams = (0, sdk_1.deriveOracleAuctionParams)({
direction: direction,
oraclePrice,
auctionStartPrice: auctionParams.auctionStartPrice,
auctionEndPrice: oracleAuctionEndPrice,
limitPrice: oracleAuctionEndPrice,
auctionPriceCaps: auctionPriceCaps,
});
orderParams = {
...orderParams,
...oracleAuctionParams,
price: undefined,
orderType: sdk_1.OrderType.ORACLE,
};
}
}
return orderParams;
};
const getLimitAuctionParams = ({ direction, inputPrice, startPriceFromSettings, duration, auctionStartPriceOffset, oraclePriceBands, }) => {
let limitAuctionParams = trade_1.EMPTY_AUCTION_PARAMS;
const auctionStartPriceBuffer = inputPrice.scale(Math.abs(auctionStartPriceOffset * 100), 10000).val;
if ((0, sdk_1.isVariant)(direction, 'long') &&
startPriceFromSettings &&
startPriceFromSettings.lt(inputPrice.val) &&
startPriceFromSettings.gt(sdk_1.ZERO)) {
limitAuctionParams = {
auctionStartPrice: startPriceFromSettings.sub(auctionStartPriceBuffer),
auctionEndPrice: inputPrice.val,
auctionDuration: duration,
};
}
else if ((0, sdk_1.isVariant)(direction, 'short') &&
startPriceFromSettings &&
startPriceFromSettings.gt(sdk_1.ZERO) &&
startPriceFromSettings.gt(inputPrice.val)) {
limitAuctionParams = {
auctionStartPrice: startPriceFromSettings.add(auctionStartPriceBuffer),
auctionEndPrice: inputPrice.val,
auctionDuration: duration,
};
}
if (oraclePriceBands && limitAuctionParams.auctionDuration) {
const [minPrice, maxPrice] = oraclePriceBands;
// start and end price cant be outside of the oracle price bands
limitAuctionParams.auctionStartPrice = sdk_1.BN.max(sdk_1.BN.min(limitAuctionParams.auctionStartPrice, maxPrice), minPrice);
limitAuctionParams.auctionEndPrice = sdk_1.BN.max(sdk_1.BN.min(limitAuctionParams.auctionEndPrice, maxPrice), minPrice);
}
return limitAuctionParams;
};
const getPriceObject = ({ oraclePrice, bestOffer, entryPrice, worstPrice, markPrice, direction, }) => {
let best;
const nonZeroOptions = [oraclePrice, bestOffer, markPrice].filter((price) => price !== undefined && (price === null || price === void 0 ? void 0 : price.gt(sdk_1.ZERO)));
if (nonZeroOptions.length === 0) {
// console.error('Unable to create valid auction params');
return {
oracle: sdk_1.ZERO,
bestOffer: sdk_1.ZERO,
entry: sdk_1.ZERO,
best: sdk_1.ZERO,
worst: sdk_1.ZERO,
mark: sdk_1.ZERO,
};
}
if ((0, sdk_1.isVariant)(direction, 'long')) {
best = nonZeroOptions.reduce((a, b) => (a.lt(b) ? a : b)); // lowest price
}
else {
best = nonZeroOptions.reduce((a, b) => (a.gt(b) ? a : b)); // highest price
}
// if zero values come through, fallback to nonzero value
return {
oracle: (oraclePrice === null || oraclePrice === void 0 ? void 0 : oraclePrice.gt(sdk_1.ZERO)) ? oraclePrice : best,
bestOffer: (bestOffer === null || bestOffer === void 0 ? void 0 : bestOffer.gt(sdk_1.ZERO)) ? bestOffer : best,
entry: entryPrice,
best,
worst: worstPrice,
mark: (markPrice === null || markPrice === void 0 ? void 0 : markPrice.gt(sdk_1.ZERO)) ? markPrice : best,
};
};
/* LP Utils */
const getLpSharesAmountForQuote = (driftClient, marketIndex, quoteAmount) => {
const tenMillionBigNum = sdk_1.BigNum.fromPrint('10000000', sdk_1.QUOTE_PRECISION_EXP);
const pricePerLpShare = sdk_1.BigNum.from(driftClient.getQuoteValuePerLpShare(marketIndex), sdk_1.QUOTE_PRECISION_EXP);
return sdk_1.BigNum.from(quoteAmount, sdk_1.QUOTE_PRECISION_EXP)
.scale(tenMillionBigNum.toNum(), pricePerLpShare.mul(tenMillionBigNum).toNum())
.shiftTo(sdk_1.AMM_RESERVE_PRECISION_EXP);
};
const getQuoteValueForLpShares = (driftClient, marketIndex, sharesAmount) => {
const pricePerLpShare = sdk_1.BigNum.from(driftClient.getQuoteValuePerLpShare(marketIndex), sdk_1.QUOTE_PRECISION_EXP).shiftTo(sdk_1.AMM_RESERVE_PRECISION_EXP);
const lpSharesBigNum = sdk_1.BigNum.from(sharesAmount, sdk_1.AMM_RESERVE_PRECISION_EXP);
return lpSharesBigNum.mul(pricePerLpShare).shiftTo(sdk_1.QUOTE_PRECISION_EXP);
};
const getTokenAddress = (mintAddress, userPubKey) => {
return (0, spl_token_1.getAssociatedTokenAddress)(mintAddress, userPubKey, true);
};
const getBalanceFromTokenAccountResult = (account) => {
var _a, _b, _c, _d;
return (_d = (_c = (_b = (_a = account === null || account === void 0 ? void 0 : account.account.data) === null || _a === void 0 ? void 0 : _a.parsed) === null || _b === void 0 ? void 0 : _b.info) === null || _c === void 0 ? void 0 : _c.tokenAmount) === null || _d === void 0 ? void 0 : _d.uiAmount;
};
const getTokenAccount = async (connection, mintAddress, userPubKey) => {
const tokenAccounts = await connection.getParsedTokenAccountsByOwner(userPubKey, { mint: mintAddress });
const associatedAddress = await (0, spl_token_1.getAssociatedTokenAddress)(mintAddress, userPubKey, true);
const targetAccount = tokenAccounts.value.filter((account) => account.pubkey.equals(associatedAddress))[0] || tokenAccounts.value[0];
const anotherBalanceExists = tokenAccounts.value.find((account) => {
return (!!getBalanceFromTokenAccountResult(account) &&
!account.pubkey.equals(targetAccount.pubkey));
});
let tokenAccountWarning = false;
if (anotherBalanceExists) {
tokenAccountWarning = true;
}
return {
tokenAccount: targetAccount,
tokenAccountWarning,
};
};
const getMultipleAccounts = async (connection, keys, commitment) => {
const result = await Promise.all(chunks(keys, 99).map((chunk) => getMultipleAccountsCore(connection, chunk, commitment)));
const array = result
.map((a) => a.array
.map((acc) => {
if (!acc) {
return undefined;
}
const { data, ...rest } = acc;
const obj = {
...rest,
data: Buffer.from(data[0], 'base64'),
};
return obj;
})
.filter((_) => _))
.flat();
return { keys, array };
};
const getMultipleAccountsCore = async (connection, keys, commitment) => {
const args = connection._buildArgs([keys], commitment, 'base64');
const unsafeRes = await connection._rpcRequest('getMultipleAccounts', args);
if (unsafeRes.error) {
throw new Error('failed to get info about account ' + unsafeRes.error.message);
}
if (unsafeRes.result.value) {
const array = unsafeRes.result.value;
return { keys, array };
}
// TODO: fix
throw new Error();
};
const userExists = async (driftClient, userId, authority) => {
let userAccountExists = false;
try {
const user = driftClient.getUser(userId, authority);
userAccountExists = await user.exists();
}
catch (e) {
// user account does not exist so we leave userAccountExists false
}
return userAccountExists;
};
function chunks(array, size) {
return Array.apply(0, new Array(Math.ceil(array.length / size))).map((_, index) => array.slice(index * size, (index + 1) * size));
}
/**
* Trim trailing zeros from a numerical string
* @param str - numerical string to format
* @param zerosToShow - max number of zeros to show after the decimal. Similar to number.toFixed() but won't trim non-zero values. Optional, default value is 1
*/
const trimTrailingZeros = (str, zerosToShow = 1) => {
// Ignore strings with no decimal point
if (!str.includes('.'))
return str;
const sides = str.split('.');
sides[1] = sides[1].replace(/0+$/, '');
if (sides[1].length < zerosToShow) {
const zerosToAdd = zerosToShow - sides[1].length;
sides[1] = `${sides[1]}${Array(zerosToAdd).fill('0').join('')}`;
}
if (sides[1].length === 0) {
return sides[0];
}
else {
return sides.join('.');
}
};
const formatTokenInputCurried = (setAmount, spotMarketConfig) => (newAmount) => {
var _a, _b;
if (isNaN(+newAmount))
return;
if (newAmount === '') {
setAmount('');
return;
}
const lastChar = newAmount[newAmount.length - 1];
// if last char of string is a decimal point, don't format
if (lastChar === '.') {
setAmount(newAmount);
return;
}
if (lastChar === '0') {
// if last char of string is a zero in the decimal places, cut it off if it exceeds precision
const numOfDigitsAfterDecimal = (_b = (_a = newAmount.split('.')[1]) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
if (numOfDigitsAfterDecimal > spotMarketConfig.precisionExp.toNumber()) {
setAmount(newAmount.slice(0, -1));
}
else {
setAmount(newAmount);
}
return;
}
const formattedAmount = Number((+newAmount).toFixed(spotMarketConfig.precisionExp.toNumber()));
setAmount(formattedAmount.toString());
};
// --- Export The Utils
exports.COMMON_UI_UTILS = {
abbreviateAddress,
calculateAverageEntryPrice,
chunks,
compareSignatures,
createThrowawayIWallet,
createPlaceholderIWallet,
deriveMarketOrderParams,
fetchCurrentSubaccounts,
fetchUserClientsAndAccounts,
formatTokenInputCurried,
getBalanceFromTokenAccountResult,
getIdAndAuthorityFromKey,
getLimitAuctionParams,
getLpSharesAmountForQuote,
getMarketAuctionParams,
getMarketKey,
getMarketOrderLimitPrice,
getMultipleAccounts,
getMultipleAccountsCore,
getPriceObject,
getQuoteValueForLpShares,
getSignatureVerificationMessageForSettings,
getTokenAccount,
getTokenAddress,
getUserKey,
hashSignature,
initializeAndSubscribeToNewUserAccount,
userExists,
verifySignature,
trimTrailingZeros,
...user_1.USER_UTILS,
...trading_1.TRADING_UTILS,
...market_1.MARKET_UTILS,
...order_1.ORDER_COMMON_UTILS,
};
//# sourceMappingURL=commonUiUtils.js.map
;