fix signal and get position during closing

This commit is contained in:
2025-05-14 14:48:01 +07:00
parent 15190a0516
commit 456867c352
3 changed files with 420 additions and 419 deletions

View File

@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Globalization;
using Managing.Core; using Managing.Core;
using Managing.Domain.Candles; using Managing.Domain.Candles;
using Managing.Domain.Users; using Managing.Domain.Users;
@@ -34,7 +35,7 @@ namespace Managing.Domain.Strategies
StrategyType = strategyType; StrategyType = strategyType;
User = user; User = user;
Identifier = $"{StrategyType}-{direction}-{ticker}-{candle?.Close}-{date:yyyyMMdd-HHmmss}"; Identifier = $"{StrategyType}-{direction}-{ticker}-{candle?.Close.ToString(CultureInfo.InvariantCulture)}-{date:yyyyMMdd-HHmmss}";
SignalType = signalType; SignalType = signalType;
} }

View File

@@ -458,10 +458,10 @@ public class EvmManager : IEvmManager
{ {
// Log the error // Log the error
Console.Error.WriteLine($"Error initializing address: {ex.Message}"); Console.Error.WriteLine($"Error initializing address: {ex.Message}");
return new PrivyInitAddressResponse return new PrivyInitAddressResponse
{ {
Success = false, Success = false,
Error = ex.Message Error = ex.Message
}; };
} }
} }
@@ -660,7 +660,9 @@ public class EvmManager : IEvmManager
{ {
account = account.Key, account = account.Key,
ticker = ticker.ToString(), ticker = ticker.ToString(),
direction = direction.ToString(), direction = direction == TradeDirection.Long
? TradeDirection.Short.ToString()
: TradeDirection.Long.ToString(),
}); });
trade = new Trade( trade = new Trade(
DateTime.UtcNow, DateTime.UtcNow,

View File

@@ -15,483 +15,481 @@ import {EntryField, SidecarSlTpOrderEntryValid} from "../../types/sidecarOrders.
/** Base Optional params for helpers, allows to avoid calling markets, tokens and uiFeeFactor methods if they are already passed */ /** Base Optional params for helpers, allows to avoid calling markets, tokens and uiFeeFactor methods if they are already passed */
interface BaseOptionalParams { interface BaseOptionalParams {
marketsInfoData?: MarketsInfoData; marketsInfoData?: MarketsInfoData;
tokensData?: TokensData; tokensData?: TokensData;
uiFeeFactor?: bigint; uiFeeFactor?: bigint;
} }
export type PositionIncreaseParams = ( export type PositionIncreaseParams = (
| { | {
/** Increase amounts will be calculated based on collateral amount */ /** Increase amounts will be calculated based on collateral amount */
payAmount: bigint; payAmount: bigint;
} }
| { | {
/** Increase amounts will be calculated based on position size amount */ /** Increase amounts will be calculated based on position size amount */
sizeAmount: bigint; sizeAmount: bigint;
} }
) & { ) & {
marketAddress: string; marketAddress: string;
payTokenAddress: string; payTokenAddress: string;
collateralTokenAddress: string; collateralTokenAddress: string;
/** @default 100 */ /** @default 100 */
allowedSlippageBps?: number; allowedSlippageBps?: number;
referralCodeForTxn?: string; referralCodeForTxn?: string;
leverage?: bigint; leverage?: bigint;
/** If presented, then it's limit order */ /** If presented, then it's limit order */
limitPrice?: bigint; limitPrice?: bigint;
acceptablePriceImpactBuffer?: number; acceptablePriceImpactBuffer?: number;
fixedAcceptablePriceImpactBps?: bigint; fixedAcceptablePriceImpactBps?: bigint;
skipSimulation?: boolean; skipSimulation?: boolean;
stopLossPrice?: bigint; stopLossPrice?: bigint;
takeProfitPrice?: bigint; takeProfitPrice?: bigint;
} & BaseOptionalParams; } & BaseOptionalParams;
async function getAndValidateBaseParams(sdk: GmxSdk, params: BaseOptionalParams) { async function getAndValidateBaseParams(sdk: GmxSdk, params: BaseOptionalParams) {
let tokensData: TokensData | undefined = params.tokensData; let tokensData: TokensData | undefined = params.tokensData;
let marketsInfoData: MarketsInfoData | undefined = params.marketsInfoData; let marketsInfoData: MarketsInfoData | undefined = params.marketsInfoData;
if (!params.marketsInfoData && !params.tokensData) { if (!params.marketsInfoData && !params.tokensData) {
const result = await sdk.markets.getMarketsInfo(); const result = await sdk.markets.getMarketsInfo();
marketsInfoData = result.marketsInfoData; marketsInfoData = result.marketsInfoData;
tokensData = result.tokensData; tokensData = result.tokensData;
} }
if (!tokensData) { if (!tokensData) {
throw new Error("Tokens data is not available"); throw new Error("Tokens data is not available");
} }
if (!marketsInfoData) { if (!marketsInfoData) {
throw new Error("Markets info data is not available"); throw new Error("Markets info data is not available");
} }
let uiFeeFactor = params.uiFeeFactor; let uiFeeFactor = params.uiFeeFactor;
if (!uiFeeFactor) { if (!uiFeeFactor) {
uiFeeFactor = await sdk.utils.getUiFeeFactor(); uiFeeFactor = await sdk.utils.getUiFeeFactor();
} }
return { return {
tokensData, tokensData,
marketsInfoData, marketsInfoData,
uiFeeFactor, uiFeeFactor,
}; };
} }
export async function increaseOrderHelper( export async function increaseOrderHelper(
sdk: GmxSdk, sdk: GmxSdk,
params: PositionIncreaseParams & { params: PositionIncreaseParams & {
isLong: boolean; isLong: boolean;
} }
) { ) {
const { tokensData, marketsInfoData, uiFeeFactor } = await getAndValidateBaseParams(sdk, params); const {tokensData, marketsInfoData, uiFeeFactor} = await getAndValidateBaseParams(sdk, params);
const isLimit = Boolean(params.limitPrice); const isLimit = Boolean(params.limitPrice);
const fromToken = tokensData[params.payTokenAddress]; const fromToken = tokensData[params.payTokenAddress];
const collateralToken = tokensData[params.collateralTokenAddress]; const collateralToken = tokensData[params.collateralTokenAddress];
if (!fromToken) { if (!fromToken) {
throw new Error("From token is not available"); throw new Error("From token is not available");
} }
if (!collateralToken) { if (!collateralToken) {
throw new Error("Collateral token is not available"); throw new Error("Collateral token is not available");
} }
const marketInfo = getByKey(marketsInfoData, params.marketAddress); const marketInfo = getByKey(marketsInfoData, params.marketAddress);
if (!marketInfo) { if (!marketInfo) {
throw new Error("Market info is not available"); throw new Error("Market info is not available");
} }
const collateralTokenAddress = collateralToken.address; const collateralTokenAddress = collateralToken.address;
const allowedSlippage = params.allowedSlippageBps ?? 100; const allowedSlippage = params.allowedSlippageBps ?? 100;
const graph = getMarketsGraph(Object.values(marketsInfoData)); const graph = getMarketsGraph(Object.values(marketsInfoData));
const wrappedFromAddress = getWrappedAddress(sdk.chainId, params.payTokenAddress); const wrappedFromAddress = getWrappedAddress(sdk.chainId, params.payTokenAddress);
const wrappedToAddress = getWrappedAddress(sdk.chainId, collateralTokenAddress); const wrappedToAddress = getWrappedAddress(sdk.chainId, collateralTokenAddress);
const allPaths = findAllSwapPaths({ const allPaths = findAllSwapPaths({
chainId: sdk.chainId, chainId: sdk.chainId,
fromTokenAddress: params.payTokenAddress, fromTokenAddress: params.payTokenAddress,
toTokenAddress: collateralTokenAddress, toTokenAddress: collateralTokenAddress,
marketsInfoData, marketsInfoData,
graph, graph,
wrappedFromAddress, wrappedFromAddress,
wrappedToAddress, wrappedToAddress,
});
const estimator = createSwapEstimator(marketsInfoData);
const findSwapPath = createFindSwapPath({
chainId: sdk.chainId,
fromTokenAddress: params.payTokenAddress,
toTokenAddress: collateralTokenAddress,
marketsInfoData,
estimator,
allPaths,
});
const payOrSizeAmount = "payAmount" in params ? params.payAmount : params.sizeAmount;
const increaseAmounts = getIncreasePositionAmounts({
marketInfo,
indexToken: marketInfo.indexToken,
initialCollateralToken: fromToken,
collateralToken,
isLong: params.isLong,
initialCollateralAmount: payOrSizeAmount,
position: undefined,
indexTokenAmount: payOrSizeAmount,
leverage: params.leverage,
triggerPrice: params.limitPrice,
limitOrderType: params.limitPrice ? OrderType.LimitIncrease : undefined,
userReferralInfo: undefined,
strategy: "payAmount" in params ? "leverageByCollateral" : "leverageBySize",
findSwapPath: findSwapPath,
uiFeeFactor,
acceptablePriceImpactBuffer: params.acceptablePriceImpactBuffer,
fixedAcceptablePriceImpactBps: params.fixedAcceptablePriceImpactBps,
externalSwapQuote: undefined,
});
const createSltpEntries: SidecarSlTpOrderEntryValid[] = [
]
let stopLossDecreaseAmounts: DecreasePositionAmounts | undefined;
if (params.stopLossPrice) {
const stopLossCollateralDeltaUsd = convertToUsd(increaseAmounts.collateralDeltaAmount, collateralToken.decimals, params.stopLossPrice);
const acceptablePriceInfo = getAcceptablePriceInfo({
marketInfo,
isIncrease: false,
isLong: params.isLong,
indexPrice: params.stopLossPrice,
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
maxNegativePriceImpactBps: marketInfo.maxPositionImpactFactorForLiquidations,
}); });
stopLossDecreaseAmounts = { const estimator = createSwapEstimator(marketsInfoData);
isFullClose: true,
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
sizeDeltaInTokens: increaseAmounts.sizeDeltaInTokens,
collateralDeltaUsd: stopLossCollateralDeltaUsd,
collateralDeltaAmount: increaseAmounts.collateralDeltaAmount,
indexPrice: params.stopLossPrice,
collateralPrice: 0n,
acceptablePrice: params.isLong ? 2n ** 256n - 1n : 0n,
acceptablePriceDeltaBps: acceptablePriceInfo.acceptablePriceDeltaBps,
recommendedAcceptablePriceDeltaBps: 0n,
estimatedPnl: 0n,
estimatedPnlPercentage: 0n,
realizedPnl: 0n,
realizedPnlPercentage: 0n,
positionFeeUsd: 0n,
uiFeeUsd: 0n,
swapUiFeeUsd: 0n,
feeDiscountUsd: 0n,
borrowingFeeUsd: 0n,
fundingFeeUsd: 0n,
swapProfitFeeUsd: 0n,
positionPriceImpactDeltaUsd: 0n,
priceImpactDiffUsd: 0n,
payedRemainingCollateralAmount: 0n,
payedOutputUsd: 0n,
payedRemainingCollateralUsd: 0n,
receiveTokenAmount: 0n,
receiveUsd: 0n,
decreaseSwapType: DecreasePositionSwapType.SwapPnlTokenToCollateralToken,
triggerOrderType: OrderType.StopLossDecrease,
triggerPrice: params.stopLossPrice,
}
const stopLossEntry: SidecarSlTpOrderEntryValid = { const findSwapPath = createFindSwapPath({
decreaseAmounts: stopLossDecreaseAmounts, chainId: sdk.chainId,
id: "sl-order", fromTokenAddress: params.payTokenAddress,
price: { toTokenAddress: collateralTokenAddress,
input: params.stopLossPrice?.toString() ?? "", marketsInfoData,
value: params.stopLossPrice, estimator,
error: null, allPaths,
} as EntryField,
sizeUsd: {
input: increaseAmounts.sizeDeltaUsd.toString(),
value: increaseAmounts.sizeDeltaUsd,
error: null,
} as EntryField,
percentage: {
input: "100",
value: 100n,
error: null,
} as EntryField,
txnType: "create",
mode: "keepSize",
order: null,
increaseAmounts: undefined
}
createSltpEntries.push(stopLossEntry)
}
let takeProfitDecreaseAmounts: DecreasePositionAmounts | undefined;
if (params.takeProfitPrice) {
const takeProfitCollateralDeltaUsd = convertToUsd(increaseAmounts.collateralDeltaAmount, collateralToken.decimals, params.takeProfitPrice);
const acceptablePriceInfo = getAcceptablePriceInfo({
marketInfo,
isIncrease: false,
isLong: params.isLong,
indexPrice: params.takeProfitPrice,
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
maxNegativePriceImpactBps: marketInfo.maxPositionImpactFactorForLiquidations,
}); });
takeProfitDecreaseAmounts = { const payOrSizeAmount = "payAmount" in params ? params.payAmount : params.sizeAmount;
isFullClose: true,
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd, const increaseAmounts = getIncreasePositionAmounts({
sizeDeltaInTokens: increaseAmounts.sizeDeltaInTokens, marketInfo,
collateralDeltaUsd: takeProfitCollateralDeltaUsd, indexToken: marketInfo.indexToken,
collateralDeltaAmount: increaseAmounts.collateralDeltaAmount, initialCollateralToken: fromToken,
indexPrice: params.takeProfitPrice, // Keep original trigger price for indexPrice collateralToken,
collateralPrice: 0n, // Consider if this needs calculation isLong: params.isLong,
acceptablePrice: acceptablePriceInfo.acceptablePrice, initialCollateralAmount: payOrSizeAmount,
acceptablePriceDeltaBps: acceptablePriceInfo.acceptablePriceDeltaBps, position: undefined,
recommendedAcceptablePriceDeltaBps: 0n, indexTokenAmount: payOrSizeAmount,
estimatedPnl: 0n, leverage: params.leverage,
estimatedPnlPercentage: 0n, triggerPrice: params.limitPrice,
realizedPnl: 0n, limitOrderType: params.limitPrice ? OrderType.LimitIncrease : undefined,
realizedPnlPercentage: 0n, userReferralInfo: undefined,
positionFeeUsd: 0n, strategy: "payAmount" in params ? "leverageByCollateral" : "leverageBySize",
uiFeeUsd: 0n, findSwapPath: findSwapPath,
swapUiFeeUsd: 0n, uiFeeFactor,
feeDiscountUsd: 0n, acceptablePriceImpactBuffer: params.acceptablePriceImpactBuffer,
borrowingFeeUsd: 0n, fixedAcceptablePriceImpactBps: params.fixedAcceptablePriceImpactBps,
fundingFeeUsd: 0n, externalSwapQuote: undefined,
swapProfitFeeUsd: 0n, });
positionPriceImpactDeltaUsd: 0n,
priceImpactDiffUsd: 0n, const createSltpEntries: SidecarSlTpOrderEntryValid[] = []
payedRemainingCollateralAmount: 0n,
payedOutputUsd: 0n, let stopLossDecreaseAmounts: DecreasePositionAmounts | undefined;
payedRemainingCollateralUsd: 0n, if (params.stopLossPrice) {
receiveTokenAmount: 0n, const stopLossCollateralDeltaUsd = convertToUsd(increaseAmounts.collateralDeltaAmount, collateralToken.decimals, params.stopLossPrice);
receiveUsd: 0n,
decreaseSwapType: DecreasePositionSwapType.SwapPnlTokenToCollateralToken, const acceptablePriceInfo = getAcceptablePriceInfo({
triggerOrderType: OrderType.LimitDecrease, marketInfo,
triggerPrice: params.takeProfitPrice, isIncrease: false,
isLong: params.isLong,
indexPrice: params.stopLossPrice,
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
maxNegativePriceImpactBps: marketInfo.maxPositionImpactFactorForLiquidations,
});
stopLossDecreaseAmounts = {
isFullClose: true,
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
sizeDeltaInTokens: increaseAmounts.sizeDeltaInTokens,
collateralDeltaUsd: stopLossCollateralDeltaUsd,
collateralDeltaAmount: increaseAmounts.collateralDeltaAmount,
indexPrice: params.stopLossPrice,
collateralPrice: 0n,
acceptablePrice: params.isLong ? 2n ** 256n - 1n : 0n,
acceptablePriceDeltaBps: acceptablePriceInfo.acceptablePriceDeltaBps + 50n,
recommendedAcceptablePriceDeltaBps: 50n,
estimatedPnl: 0n,
estimatedPnlPercentage: 0n,
realizedPnl: 0n,
realizedPnlPercentage: 0n,
positionFeeUsd: 0n,
uiFeeUsd: 0n,
swapUiFeeUsd: 0n,
feeDiscountUsd: 0n,
borrowingFeeUsd: 0n,
fundingFeeUsd: 0n,
swapProfitFeeUsd: 0n,
positionPriceImpactDeltaUsd: 0n,
priceImpactDiffUsd: 0n,
payedRemainingCollateralAmount: 0n,
payedOutputUsd: 0n,
payedRemainingCollateralUsd: 0n,
receiveTokenAmount: 0n,
receiveUsd: 0n,
decreaseSwapType: DecreasePositionSwapType.SwapPnlTokenToCollateralToken,
triggerOrderType: OrderType.StopLossDecrease,
triggerPrice: params.stopLossPrice,
}
const stopLossEntry: SidecarSlTpOrderEntryValid = {
decreaseAmounts: stopLossDecreaseAmounts,
id: "sl-order",
price: {
input: params.stopLossPrice?.toString() ?? "",
value: params.stopLossPrice,
error: null,
} as EntryField,
sizeUsd: {
input: increaseAmounts.sizeDeltaUsd.toString(),
value: increaseAmounts.sizeDeltaUsd,
error: null,
} as EntryField,
percentage: {
input: "100",
value: 100n,
error: null,
} as EntryField,
txnType: "create",
mode: "keepSize",
order: null,
increaseAmounts: undefined
}
createSltpEntries.push(stopLossEntry)
} }
const takeProfitEntry: SidecarSlTpOrderEntryValid = { let takeProfitDecreaseAmounts: DecreasePositionAmounts | undefined;
decreaseAmounts: takeProfitDecreaseAmounts, if (params.takeProfitPrice) {
id: "tp-order", const takeProfitCollateralDeltaUsd = convertToUsd(increaseAmounts.collateralDeltaAmount, collateralToken.decimals, params.takeProfitPrice);
price: {
input: params.takeProfitPrice?.toString() ?? "", const acceptablePriceInfo = getAcceptablePriceInfo({
value: params.takeProfitPrice, marketInfo,
error: null, isIncrease: false,
} as EntryField, isLong: params.isLong,
sizeUsd: { indexPrice: params.takeProfitPrice,
input: increaseAmounts.sizeDeltaUsd.toString(), sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
value: increaseAmounts.sizeDeltaUsd, maxNegativePriceImpactBps: marketInfo.maxPositionImpactFactorForLiquidations,
error: null, });
} as EntryField,
percentage: { takeProfitDecreaseAmounts = {
input: "100", isFullClose: true,
value: 100n, sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
error: null, sizeDeltaInTokens: increaseAmounts.sizeDeltaInTokens,
} as EntryField, collateralDeltaUsd: takeProfitCollateralDeltaUsd,
txnType: "create", collateralDeltaAmount: increaseAmounts.collateralDeltaAmount,
mode: "keepSize", indexPrice: params.takeProfitPrice, // Keep original trigger price for indexPrice
order: null, collateralPrice: 0n, // Consider if this needs calculation
increaseAmounts: undefined acceptablePrice: acceptablePriceInfo.acceptablePrice,
acceptablePriceDeltaBps: acceptablePriceInfo.acceptablePriceDeltaBps + 50n, // Add 0.5% buffer to acceptable price impact
recommendedAcceptablePriceDeltaBps: 50n, // Set recommended buffer to 0.5%
estimatedPnl: 0n,
estimatedPnlPercentage: 0n,
realizedPnl: 0n,
realizedPnlPercentage: 0n,
positionFeeUsd: 0n,
uiFeeUsd: 0n,
swapUiFeeUsd: 0n,
feeDiscountUsd: 0n,
borrowingFeeUsd: 0n,
fundingFeeUsd: 0n,
swapProfitFeeUsd: 0n,
positionPriceImpactDeltaUsd: 0n,
priceImpactDiffUsd: 0n,
payedRemainingCollateralAmount: 0n,
payedOutputUsd: 0n,
payedRemainingCollateralUsd: 0n,
receiveTokenAmount: 0n,
receiveUsd: 0n,
decreaseSwapType: DecreasePositionSwapType.SwapPnlTokenToCollateralToken,
triggerOrderType: OrderType.LimitDecrease,
triggerPrice: params.takeProfitPrice,
}
const takeProfitEntry: SidecarSlTpOrderEntryValid = {
decreaseAmounts: takeProfitDecreaseAmounts,
id: "tp-order",
price: {
input: params.takeProfitPrice?.toString() ?? "",
value: params.takeProfitPrice,
error: null,
} as EntryField,
sizeUsd: {
input: increaseAmounts.sizeDeltaUsd.toString(),
value: increaseAmounts.sizeDeltaUsd,
error: null,
} as EntryField,
percentage: {
input: "100",
value: 100n,
error: null,
} as EntryField,
txnType: "create",
mode: "keepSize",
order: null,
increaseAmounts: undefined
}
createSltpEntries.push(takeProfitEntry)
} }
createSltpEntries.push(takeProfitEntry) const createIncreaseOrderParams: Parameters<typeof sdk.orders.createIncreaseOrder>[0] = {
} marketsInfoData,
tokensData,
isLimit,
marketAddress: params.marketAddress,
fromToken: tokensData[params.payTokenAddress],
allowedSlippage,
collateralToken,
referralCodeForTxn: params.referralCodeForTxn,
triggerPrice: params.limitPrice,
collateralTokenAddress: collateralToken.address,
isLong: params.isLong,
receiveTokenAddress: collateralTokenAddress,
indexToken: marketInfo.indexToken,
marketInfo,
skipSimulation: params.skipSimulation,
increaseAmounts,
createSltpEntries: createSltpEntries.length > 0 ? createSltpEntries : undefined,
};
const createIncreaseOrderParams: Parameters<typeof sdk.orders.createIncreaseOrder>[0] = { return sdk.orders.createIncreaseOrder(createIncreaseOrderParams);
marketsInfoData,
tokensData,
isLimit,
marketAddress: params.marketAddress,
fromToken: tokensData[params.payTokenAddress],
allowedSlippage,
collateralToken,
referralCodeForTxn: params.referralCodeForTxn,
triggerPrice: params.limitPrice,
collateralTokenAddress: collateralToken.address,
isLong: params.isLong,
receiveTokenAddress: collateralTokenAddress,
indexToken: marketInfo.indexToken,
marketInfo,
skipSimulation: params.skipSimulation,
increaseAmounts,
createSltpEntries: createSltpEntries.length > 0 ? createSltpEntries : undefined,
};
return sdk.orders.createIncreaseOrder(createIncreaseOrderParams);
} }
function getTriggerRatio({ function getTriggerRatio({
toToken, toToken,
fromToken, fromToken,
triggerPrice, triggerPrice,
}: { }: {
toToken: TokenData; toToken: TokenData;
fromToken: TokenData; fromToken: TokenData;
triggerPrice: bigint; triggerPrice: bigint;
}) { }) {
const fromTokenPrice = fromToken?.prices.minPrice; const fromTokenPrice = fromToken?.prices.minPrice;
const markPrice = toToken.prices.minPrice; const markPrice = toToken.prices.minPrice;
const markRatio = getTokensRatioByPrice({ const markRatio = getTokensRatioByPrice({
fromToken, fromToken,
toToken, toToken,
fromPrice: fromTokenPrice, fromPrice: fromTokenPrice,
toPrice: markPrice, toPrice: markPrice,
}); });
const triggerRatio: TokensRatio = { const triggerRatio: TokensRatio = {
ratio: triggerPrice > 0 ? triggerPrice : markRatio.ratio, ratio: triggerPrice > 0 ? triggerPrice : markRatio.ratio,
largestToken: markRatio.largestToken, largestToken: markRatio.largestToken,
smallestToken: markRatio.smallestToken, smallestToken: markRatio.smallestToken,
}; };
return triggerRatio; return triggerRatio;
} }
export type SwapParams = ( export type SwapParams = (
| { | {
fromAmount: bigint; fromAmount: bigint;
} }
| { | {
toAmount: bigint; toAmount: bigint;
} }
) & { ) & {
fromTokenAddress: string; fromTokenAddress: string;
toTokenAddress: string; toTokenAddress: string;
allowedSlippageBps?: number; allowedSlippageBps?: number;
referralCodeForTxn?: string; referralCodeForTxn?: string;
/** If presented, then it's limit swap order */ /** If presented, then it's limit swap order */
triggerPrice?: bigint; triggerPrice?: bigint;
} & BaseOptionalParams; } & BaseOptionalParams;
export async function swap(sdk: GmxSdk, params: SwapParams) { export async function swap(sdk: GmxSdk, params: SwapParams) {
const { tokensData, marketsInfoData, uiFeeFactor } = await getAndValidateBaseParams(sdk, params); const {tokensData, marketsInfoData, uiFeeFactor} = await getAndValidateBaseParams(sdk, params);
const fromToken = tokensData[params.fromTokenAddress]; const fromToken = tokensData[params.fromTokenAddress];
const toToken = tokensData[params.toTokenAddress]; const toToken = tokensData[params.toTokenAddress];
if (!fromToken || !toToken) { if (!fromToken || !toToken) {
throw new Error("From or to token is not available"); throw new Error("From or to token is not available");
} }
const isLimit = Boolean(params.triggerPrice); const isLimit = Boolean(params.triggerPrice);
if (!fromToken || !toToken) { if (!fromToken || !toToken) {
return undefined; return undefined;
} }
const graph = getMarketsGraph(Object.values(marketsInfoData)); const graph = getMarketsGraph(Object.values(marketsInfoData));
const wrappedFromAddress = getWrappedAddress(sdk.chainId, params.fromTokenAddress); const wrappedFromAddress = getWrappedAddress(sdk.chainId, params.fromTokenAddress);
const wrappedToAddress = getWrappedAddress(sdk.chainId, params.toTokenAddress); const wrappedToAddress = getWrappedAddress(sdk.chainId, params.toTokenAddress);
const allPaths = findAllSwapPaths({ const allPaths = findAllSwapPaths({
chainId: sdk.chainId, chainId: sdk.chainId,
fromTokenAddress: params.fromTokenAddress, fromTokenAddress: params.fromTokenAddress,
toTokenAddress: params.toTokenAddress, toTokenAddress: params.toTokenAddress,
marketsInfoData, marketsInfoData,
graph, graph,
wrappedFromAddress, wrappedFromAddress,
wrappedToAddress, wrappedToAddress,
}); });
const estimator = createSwapEstimator(marketsInfoData); const estimator = createSwapEstimator(marketsInfoData);
const findSwapPath = createFindSwapPath({ const findSwapPath = createFindSwapPath({
chainId: sdk.chainId, chainId: sdk.chainId,
fromTokenAddress: params.fromTokenAddress, fromTokenAddress: params.fromTokenAddress,
toTokenAddress: params.toTokenAddress, toTokenAddress: params.toTokenAddress,
marketsInfoData, marketsInfoData,
estimator, estimator,
allPaths, allPaths,
}); });
const isWrapOrUnwrap = Boolean( const isWrapOrUnwrap = Boolean(
fromToken && toToken && (getIsWrap(fromToken, toToken) || getIsUnwrap(fromToken, toToken)) fromToken && toToken && (getIsWrap(fromToken, toToken) || getIsUnwrap(fromToken, toToken))
); );
const swapOptimizationOrder: Parameters<FindSwapPath>[1]["order"] = isLimit ? ["length", "liquidity"] : undefined; const swapOptimizationOrder: Parameters<FindSwapPath>[1]["order"] = isLimit ? ["length", "liquidity"] : undefined;
let swapAmounts: SwapAmounts | undefined; let swapAmounts: SwapAmounts | undefined;
const fromTokenPrice = fromToken.prices.minPrice; const fromTokenPrice = fromToken.prices.minPrice;
const triggerRatio = params.triggerPrice const triggerRatio = params.triggerPrice
? getTriggerRatio({ ? getTriggerRatio({
fromToken, fromToken,
toToken, toToken,
triggerPrice: params.triggerPrice,
})
: undefined;
if (isWrapOrUnwrap) {
const tokenAmount = "fromAmount" in params ? params.fromAmount : params.toAmount;
const usdAmount = convertToUsd(tokenAmount, fromToken.decimals, fromTokenPrice)!;
const price = fromTokenPrice;
swapAmounts = {
amountIn: tokenAmount,
usdIn: usdAmount!,
amountOut: tokenAmount,
usdOut: usdAmount!,
swapPathStats: undefined,
priceIn: price,
priceOut: price,
minOutputAmount: tokenAmount,
};
return swapAmounts;
} else if ("fromAmount" in params) {
swapAmounts = getSwapAmountsByFromValue({
tokenIn: fromToken,
tokenOut: toToken,
amountIn: params.fromAmount,
triggerRatio,
isLimit,
findSwapPath: findSwapPath,
uiFeeFactor,
swapOptimizationOrder,
allowedSwapSlippageBps: isLimit ? BigInt(params.allowedSlippageBps ?? 100) : undefined,
});
} else {
swapAmounts = getSwapAmountsByToValue({
tokenIn: fromToken,
tokenOut: toToken,
amountOut: params.toAmount,
triggerRatio,
isLimit: isLimit,
findSwapPath: findSwapPath,
uiFeeFactor,
swapOptimizationOrder,
allowedSwapSlippageBps: isLimit ? BigInt(params.allowedSlippageBps ?? 100) : undefined,
});
}
if (!swapAmounts) {
return undefined;
}
const createSwapOrderParams: Parameters<typeof sdk.orders.createSwapOrder>[0] = {
tokensData,
fromToken: tokensData[params.fromTokenAddress],
toToken: tokensData[params.toTokenAddress],
swapAmounts,
isLimit,
allowedSlippage: params.allowedSlippageBps ?? 100,
referralCodeForTxn: params.referralCodeForTxn,
triggerPrice: params.triggerPrice, triggerPrice: params.triggerPrice,
})
: undefined;
if (isWrapOrUnwrap) {
const tokenAmount = "fromAmount" in params ? params.fromAmount : params.toAmount;
const usdAmount = convertToUsd(tokenAmount, fromToken.decimals, fromTokenPrice)!;
const price = fromTokenPrice;
swapAmounts = {
amountIn: tokenAmount,
usdIn: usdAmount!,
amountOut: tokenAmount,
usdOut: usdAmount!,
swapPathStats: undefined,
priceIn: price,
priceOut: price,
minOutputAmount: tokenAmount,
}; };
return swapAmounts; return sdk.orders.createSwapOrder(createSwapOrderParams);
} else if ("fromAmount" in params) {
swapAmounts = getSwapAmountsByFromValue({
tokenIn: fromToken,
tokenOut: toToken,
amountIn: params.fromAmount,
triggerRatio,
isLimit,
findSwapPath: findSwapPath,
uiFeeFactor,
swapOptimizationOrder,
allowedSwapSlippageBps: isLimit ? BigInt(params.allowedSlippageBps ?? 100) : undefined,
});
} else {
swapAmounts = getSwapAmountsByToValue({
tokenIn: fromToken,
tokenOut: toToken,
amountOut: params.toAmount,
triggerRatio,
isLimit: isLimit,
findSwapPath: findSwapPath,
uiFeeFactor,
swapOptimizationOrder,
allowedSwapSlippageBps: isLimit ? BigInt(params.allowedSlippageBps ?? 100) : undefined,
});
}
if (!swapAmounts) {
return undefined;
}
const createSwapOrderParams: Parameters<typeof sdk.orders.createSwapOrder>[0] = {
tokensData,
fromToken: tokensData[params.fromTokenAddress],
toToken: tokensData[params.toTokenAddress],
swapAmounts,
isLimit,
allowedSlippage: params.allowedSlippageBps ?? 100,
referralCodeForTxn: params.referralCodeForTxn,
triggerPrice: params.triggerPrice,
};
return sdk.orders.createSwapOrder(createSwapOrderParams);
} }