Skip to content
This repository was archived by the owner on Dec 5, 2025. It is now read-only.

Commit b97ed86

Browse files
authored
Merge pull request #61 from Parifi/fix/liq-price
Fix liquidation price logic [PFT-2466]
2 parents 0df9443 + 785795e commit b97ed86

File tree

4 files changed

+43
-43
lines changed

4 files changed

+43
-43
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@parifi/synthetix-sdk-ts",
3-
"version": "0.4.33",
3+
"version": "0.4.34",
44
"description": "A Typescript SDK for interactions with the Synthetix protocol",
55
"files": [
66
"dist",

src/constants/protocol.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export const DEFAULT_NETWORK_ID = 8453;
22

3+
export const DEFAULT_DECIMALS = 18;
34
export const DEFAULT_SLIPPAGE = 2.0;
45
export const DEFAULT_GAS_MULTIPLIER = 2.0;
56

src/perps/index.ts

Lines changed: 39 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { encodeFunctionData, erc20Abi, formatEther, getAbiItem, Hex } from 'viem';
1+
import { encodeFunctionData, erc20Abi, formatEther, formatUnits, getAbiItem, Hex } from 'viem';
22
import { SynthetixSdk } from '..';
33
import {
44
CollateralData,
@@ -47,6 +47,7 @@ import { MetadataResponse, PythPriceId } from '../interface/Markets';
4747
import { erc20StateOverrideBalanceAndAllowance } from '../utils/override';
4848
import { SYNTHETIX_ZAP_ABI } from '../contracts/abis/zap';
4949
import { SYNTHETIX_ZAP } from '../contracts/addreses/zap';
50+
import { DEFAULT_DECIMALS } from '../constants';
5051

5152
/**
5253
* Class for interacting with Synthetix Perps V3 contracts
@@ -1156,6 +1157,13 @@ export class Perps extends Market<MarketData> implements PerpsRepository {
11561157
return convertWeiToEther(debt);
11571158
}
11581159

1160+
/**
1161+
* Fetch Position and related margin details from contracts
1162+
* @param accountId The id of the account to get the debt for
1163+
* @param marketIdOrName Market
1164+
* @param override override params for the call
1165+
* @returns Formatted values in decimal numbers for position and margin data
1166+
*/
11591167
protected async _getPositionData(accountId: bigint, marketIdOrName: MarketIdOrName, override?: OverrideParamsRead) {
11601168
if (!accountId) throw new Error('Account ID is required');
11611169
const marketProxy = await this.sdk.contracts.getPerpsMarketProxyInstance();
@@ -1190,39 +1198,21 @@ export class Perps extends Market<MarketData> implements PerpsRepository {
11901198
override,
11911199
);
11921200

1201+
const availableMargin = multicallResponse.at(0) as bigint;
1202+
const requiredMaintenanceMargin = (multicallResponse.at(1) as bigint[]).at(1) as bigint;
1203+
const requiredInitialMargin = (multicallResponse.at(1) as bigint[]).at(0) as bigint;
1204+
const positionSize = multicallResponse.at(2) as bigint;
1205+
const indexPrice = multicallResponse.at(3) as bigint;
1206+
11931207
return {
1194-
availableMargin: multicallResponse.at(0) as bigint,
1195-
requiredMaintenanceMargin: (multicallResponse.at(1) as bigint[]).at(1) as bigint,
1196-
requiredInitialMargin: (multicallResponse.at(1) as bigint[]).at(0) as bigint,
1197-
positionSize: multicallResponse.at(2) as bigint,
1198-
indexPrice: multicallResponse.at(3) as bigint,
1208+
availableMargin: Number(formatUnits(availableMargin, DEFAULT_DECIMALS)),
1209+
requiredMaintenanceMargin: Number(formatUnits(requiredMaintenanceMargin, DEFAULT_DECIMALS)),
1210+
requiredInitialMargin: Number(formatUnits(requiredInitialMargin, DEFAULT_DECIMALS)),
1211+
positionSize: Number(formatUnits(positionSize, DEFAULT_DECIMALS)),
1212+
indexPrice: Number(formatUnits(indexPrice, DEFAULT_DECIMALS)),
11991213
};
12001214
}
12011215

1202-
public async calculateApproxLiquidationPrice({
1203-
collateralAmount,
1204-
marketIdOrName,
1205-
sizeAmount,
1206-
accountId,
1207-
collateralIdOrName,
1208-
}: {
1209-
collateralAmount: number;
1210-
marketIdOrName: MarketIdOrName;
1211-
sizeAmount: number;
1212-
accountId: bigint;
1213-
collateralIdOrName: MarketIdOrName;
1214-
}) {
1215-
const { requiredMaintenanceMargin, indexPrice } = await this._getPositionData(accountId, marketIdOrName);
1216-
1217-
const amount = await this.formatSize(collateralAmount, marketIdOrName);
1218-
const size = await this.sdk.spot.formatSize(sizeAmount, collateralIdOrName);
1219-
1220-
const healthFactor = (amount * 100n) / size;
1221-
const liquidationPrice = requiredMaintenanceMargin - amount / size + indexPrice;
1222-
1223-
return { healthFactor, liquidationPrice };
1224-
}
1225-
12261216
/**
12271217
* Calculate the approximate liquidation price for an account with single position
12281218
* Provide either a ``marketId`` or a ``marketName``
@@ -1235,22 +1225,29 @@ export class Perps extends Market<MarketData> implements PerpsRepository {
12351225
marketIdOrName: MarketIdOrName,
12361226
accountId = this.defaultAccountId,
12371227
override?: OverrideParamsRead,
1238-
): Promise<{ healthFactor: bigint; liquidationPrice: bigint }> {
1228+
): Promise<{ healthFactor: number; liquidationPrice: number }> {
12391229
if (!accountId) throw new Error('Account ID is required');
12401230

1241-
const { availableMargin, requiredMaintenanceMargin, positionSize, indexPrice } = await this._getPositionData(
1231+
const { positionSize, availableMargin, requiredMaintenanceMargin, indexPrice } = await this._getPositionData(
12421232
accountId,
12431233
marketIdOrName,
12441234
override,
12451235
);
12461236

1247-
const healthFactor = (availableMargin * 100n) / (requiredMaintenanceMargin + 1n);
1237+
const healthFactor = (availableMargin * 100) / (requiredMaintenanceMargin + 1);
1238+
1239+
// For invalid position, return current market price
1240+
if (positionSize == 0) return { healthFactor, liquidationPrice: indexPrice };
12481241

1249-
if (positionSize == 0n) {
1250-
return { healthFactor, liquidationPrice: 0n };
1242+
const lossPerToken = (requiredMaintenanceMargin - availableMargin) / positionSize;
1243+
1244+
let liquidationPrice;
1245+
if (positionSize > 0) {
1246+
liquidationPrice = indexPrice - lossPerToken;
1247+
} else {
1248+
liquidationPrice = indexPrice + lossPerToken;
12511249
}
12521250

1253-
const liquidationPrice = (requiredMaintenanceMargin - availableMargin) / positionSize + indexPrice;
12541251
return { healthFactor, liquidationPrice };
12551252
}
12561253

@@ -1259,12 +1256,14 @@ export class Perps extends Market<MarketData> implements PerpsRepository {
12591256
* @param {bigint} accountId The id of the account to calculate health factor.
12601257
* If not provided, the default account is used.
12611258
* @returns healthFactor percentage;
1259+
* @returns availableMargin Formatted available margin in USD;
1260+
* @returns requiredMaintenanceMargin Formatted required maintenance margin in USD
12621261
* healthFactor = (available margin * 100) / maintenance margin
12631262
*/
12641263
public async getHealthFactor(
12651264
accountId = this.defaultAccountId,
12661265
override?: OverrideParamsRead,
1267-
): Promise<{ healthFactor: bigint; availableMargin: bigint; requiredMaintenanceMargin: bigint }> {
1266+
): Promise<{ healthFactor: number; availableMargin: number; requiredMaintenanceMargin: number }> {
12681267
if (!accountId) throw new Error('Account ID is required');
12691268
const marketProxy = await this.sdk.contracts.getPerpsMarketProxyInstance();
12701269

@@ -1290,14 +1289,14 @@ export class Perps extends Market<MarketData> implements PerpsRepository {
12901289
);
12911290

12921291
// 0. Available Margin
1293-
const availableMargin = multicallResponse.at(0) as bigint;
1292+
const availableMargin = Number(formatUnits(multicallResponse.at(0) as bigint, DEFAULT_DECIMALS));
12941293

12951294
// 1. Required margin
12961295
// returns (uint256 requiredInitialMargin,uint256 requiredMaintenanceMargin,uint256 maxLiquidationReward)
12971296
const requiredMarginsResponse = multicallResponse.at(1) as bigint[];
1298-
const requiredMaintenanceMargin = requiredMarginsResponse.at(1) as bigint;
1297+
const requiredMaintenanceMargin = Number(formatUnits(requiredMarginsResponse.at(1) as bigint, DEFAULT_DECIMALS));
12991298

1300-
const healthFactor = (availableMargin * 100n) / (requiredMaintenanceMargin + 1n);
1299+
const healthFactor = (availableMargin * 100) / (requiredMaintenanceMargin + 1);
13011300
return { healthFactor, availableMargin, requiredMaintenanceMargin };
13021301
}
13031302

0 commit comments

Comments
 (0)