@reyaxyz/common 0.373.1 → 0.374.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,5 +6,5 @@
6
6
 
7
7
  | Statements | Branches | Functions | Lines |
8
8
  | --------------------------- | ----------------------- | ------------------------- | ----------------- |
9
- | ![Statements](https://img.shields.io/badge/statements-56.9%25-red.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-57.43%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-44.35%25-red.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-60.52%25-red.svg?style=flat) |
9
+ | ![Statements](https://img.shields.io/badge/statements-57.08%25-red.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-57.75%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-44.81%25-red.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-60.7%25-red.svg?style=flat) |
10
10
 
@@ -43,6 +43,17 @@ export declare class SettlementTrackerError extends Error {
43
43
  readonly reason: 'missing' | 'inconsistent';
44
44
  constructor(marketId: string, reason: 'missing' | 'inconsistent');
45
45
  }
46
+ /**
47
+ * Market trackers older than the position's own snapshot carry nothing the
48
+ * stored position does not already reflect. Off-chain tracker feeds can lag
49
+ * the position stream.
50
+ */
51
+ export declare function trackersBehindPosition(position: Pick<RealizedBalancePosition, 'block_number'>, trackers: Pick<RealizedBalanceTrackers, 'blockNumber'>): boolean;
52
+ /**
53
+ * The trackers to ADL-adjust a position's quantity with, guarded the way its
54
+ * settlement is: behind the position, its own multiplier and unwind price stand in.
55
+ */
56
+ export declare function positionAdjustmentTrackers<T extends Pick<RealizedBalanceTrackers, 'longTrackersBaseMultiplier' | 'shortTrackersBaseMultiplier' | 'longTrackersAdlUnwindPrice' | 'shortTrackersAdlUnwindPrice' | 'blockNumber'>>(position: Pick<RealizedBalancePosition, 'block_number' | 'base_multiplier' | 'adl_unwind_price'>, trackers: T): T;
46
57
  /** Mirrors perpOB updatePerpPosition, including PRBMath truncation at each step. */
47
58
  export declare function calculateSettledPositionPnl(position: RealizedBalancePosition, trackers: RealizedBalanceTrackers | undefined, onGuarded?: () => void): BigNumber;
48
59
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"realized-balance.d.ts","sourceRoot":"/","sources":["utils/realized-balance.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,cAAc,CAAC;AAErC,kEAAkE;AAClE,MAAM,MAAM,uBAAuB,GAAG;IACpC,SAAS,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,CAAC;IAClC,IAAI,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACpC,YAAY,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5C,aAAa,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC7C,eAAe,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC/C,gBAAgB,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAChD,UAAU,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC1C,wFAAwF;IACxF,YAAY,CAAC,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,wBAAwB,EAAE,MAAM,CAAC;IACjC,yBAAyB,EAAE,MAAM,CAAC;IAClC,0BAA0B,EAAE,MAAM,CAAC;IACnC,2BAA2B,EAAE,MAAM,CAAC;IACpC,0BAA0B,EAAE,MAAM,CAAC;IACnC,2BAA2B,EAAE,MAAM,CAAC;IACpC,0FAA0F;IAC1F,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,4EAA4E;AAC5E,qBAAa,sBAAuB,SAAQ,KAAK;aAE7B,QAAQ,EAAE,MAAM;aAChB,MAAM,EAAE,SAAS,GAAG,cAAc;gBADlC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,SAAS,GAAG,cAAc;CAWrD;AAkBD,oFAAoF;AACpF,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,uBAAuB,EACjC,QAAQ,EAAE,uBAAuB,GAAG,SAAS,EAC7C,SAAS,CAAC,EAAE,MAAM,IAAI,GACrB,SAAS,CAyEX;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,uBAAuB,EAAE,EACpC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC,EAC1D,kBAAkB,EAAE,MAAM,EAC1B,SAAS,CAAC,EAAE,MAAM,IAAI,GACrB,SAAS,CAaX"}
1
+ {"version":3,"file":"realized-balance.d.ts","sourceRoot":"/","sources":["utils/realized-balance.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,cAAc,CAAC;AAErC,kEAAkE;AAClE,MAAM,MAAM,uBAAuB,GAAG;IACpC,SAAS,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,CAAC;IAClC,IAAI,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACpC,YAAY,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5C,aAAa,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC7C,eAAe,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC/C,gBAAgB,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAChD,UAAU,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC1C,wFAAwF;IACxF,YAAY,CAAC,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,wBAAwB,EAAE,MAAM,CAAC;IACjC,yBAAyB,EAAE,MAAM,CAAC;IAClC,0BAA0B,EAAE,MAAM,CAAC;IACnC,2BAA2B,EAAE,MAAM,CAAC;IACpC,0BAA0B,EAAE,MAAM,CAAC;IACnC,2BAA2B,EAAE,MAAM,CAAC;IACpC,0FAA0F;IAC1F,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,4EAA4E;AAC5E,qBAAa,sBAAuB,SAAQ,KAAK;aAE7B,QAAQ,EAAE,MAAM;aAChB,MAAM,EAAE,SAAS,GAAG,cAAc;gBADlC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,SAAS,GAAG,cAAc;CAWrD;AAaD;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,IAAI,CAAC,uBAAuB,EAAE,cAAc,CAAC,EACvD,QAAQ,EAAE,IAAI,CAAC,uBAAuB,EAAE,aAAa,CAAC,GACrD,OAAO,CAIT;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,CAAC,SAAS,IAAI,CACZ,uBAAuB,EACrB,4BAA4B,GAC5B,6BAA6B,GAC7B,4BAA4B,GAC5B,6BAA6B,GAC7B,aAAa,CAChB,EAED,QAAQ,EAAE,IAAI,CACZ,uBAAuB,EACvB,cAAc,GAAG,iBAAiB,GAAG,kBAAkB,CACxD,EACD,QAAQ,EAAE,CAAC,GACV,CAAC,CAgBH;AAOD,oFAAoF;AACpF,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,uBAAuB,EACjC,QAAQ,EAAE,uBAAuB,GAAG,SAAS,EAC7C,SAAS,CAAC,EAAE,MAAM,IAAI,GACrB,SAAS,CAuEX;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,uBAAuB,EAAE,EACpC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC,EAC1D,kBAAkB,EAAE,MAAM,EAC1B,SAAS,CAAC,EAAE,MAAM,IAAI,GACrB,SAAS,CAaX"}
@@ -14,11 +14,22 @@ var __extends = (this && this.__extends) || (function () {
14
14
  d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
15
  };
16
16
  })();
17
+ var __assign = (this && this.__assign) || function () {
18
+ __assign = Object.assign || function(t) {
19
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
20
+ s = arguments[i];
21
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
22
+ t[p] = s[p];
23
+ }
24
+ return t;
25
+ };
26
+ return __assign.apply(this, arguments);
27
+ };
17
28
  var __importDefault = (this && this.__importDefault) || function (mod) {
18
29
  return (mod && mod.__esModule) ? mod : { "default": mod };
19
30
  };
20
31
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.calculateSettledAccountPnl = exports.calculateSettledPositionPnl = exports.SettlementTrackerError = void 0;
32
+ exports.calculateSettledAccountPnl = exports.calculateSettledPositionPnl = exports.positionAdjustmentTrackers = exports.trackersBehindPosition = exports.SettlementTrackerError = void 0;
22
33
  var bignumber_js_1 = __importDefault(require("bignumber.js"));
23
34
  /** Structured cause for service metrics; calculation still fails closed. */
24
35
  var SettlementTrackerError = /** @class */ (function (_super) {
@@ -52,6 +63,31 @@ var optionalBlock = function (value) {
52
63
  var result = new bignumber_js_1.default(value.toString());
53
64
  return result.isInteger() ? result : undefined;
54
65
  };
66
+ /**
67
+ * Market trackers older than the position's own snapshot carry nothing the
68
+ * stored position does not already reflect. Off-chain tracker feeds can lag
69
+ * the position stream.
70
+ */
71
+ function trackersBehindPosition(position, trackers) {
72
+ var positionBlock = optionalBlock(position.block_number);
73
+ var trackerBlock = optionalBlock(trackers.blockNumber);
74
+ return !!positionBlock && !!trackerBlock && positionBlock.gt(trackerBlock);
75
+ }
76
+ exports.trackersBehindPosition = trackersBehindPosition;
77
+ /**
78
+ * The trackers to ADL-adjust a position's quantity with, guarded the way its
79
+ * settlement is: behind the position, its own multiplier and unwind price stand in.
80
+ */
81
+ function positionAdjustmentTrackers(position, trackers) {
82
+ if (!trackersBehindPosition(position, trackers) ||
83
+ position.base_multiplier === null ||
84
+ position.adl_unwind_price === null)
85
+ return trackers;
86
+ var multiplier = position.base_multiplier.toString();
87
+ var unwind = position.adl_unwind_price.toString();
88
+ return __assign(__assign({}, trackers), { longTrackersBaseMultiplier: multiplier, shortTrackersBaseMultiplier: multiplier, longTrackersAdlUnwindPrice: unwind, shortTrackersAdlUnwindPrice: unwind });
89
+ }
90
+ exports.positionAdjustmentTrackers = positionAdjustmentTrackers;
55
91
  var mulWad = function (a, b) {
56
92
  return a.times(b).dividedToIntegerBy(WAD);
57
93
  };
@@ -85,12 +121,10 @@ function calculateSettledPositionPnl(position, trackers, onGuarded) {
85
121
  return realized;
86
122
  // Market trackers older than the position's own snapshot carry no new
87
123
  // cashflow: the stored realized PnL already settled everything up to the
88
- // position's block. Off-chain tracker feeds can lag the position stream, so
89
- // treat that window as "nothing accrued" instead of projecting a delta
90
- // backwards (wrong-sign funding, or an ADL guard failure below).
91
- var positionBlock = optionalBlock(position.block_number);
92
- var trackerBlock = optionalBlock(trackers.blockNumber);
93
- if (positionBlock && trackerBlock && positionBlock.gt(trackerBlock)) {
124
+ // position's block. Treat that window as "nothing accrued" instead of
125
+ // projecting a delta backwards (wrong-sign funding, or an ADL guard failure
126
+ // below).
127
+ if (trackersBehindPosition(position, trackers)) {
94
128
  onGuarded === null || onGuarded === void 0 ? void 0 : onGuarded();
95
129
  return realized;
96
130
  }
@@ -1 +1 @@
1
- {"version":3,"file":"realized-balance.js","sourceRoot":"/","sources":["utils/realized-balance.ts"],"names":[],"mappings":"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","sourcesContent":["import BigNumber from 'bignumber.js';\n\n/** Raw E18 position values, from either Prisma or replication. */\nexport type RealizedBalancePosition = {\n market_id: { toString(): string };\n base: { toString(): string } | null;\n realized_pnl: { toString(): string } | null;\n funding_value: { toString(): string } | null;\n base_multiplier: { toString(): string } | null;\n adl_unwind_price: { toString(): string } | null;\n last_price: { toString(): string } | null;\n /** Block the stored trackers were written at; lets stale market trackers be ignored. */\n block_number?: { toString(): string } | null;\n};\n\nexport type RealizedBalanceTrackers = {\n longTrackersFundingValue: string;\n shortTrackersFundingValue: string;\n longTrackersBaseMultiplier: string;\n shortTrackersBaseMultiplier: string;\n longTrackersAdlUnwindPrice: string;\n shortTrackersAdlUnwindPrice: string;\n /** Block the market trackers were observed at (optional; enables the staleness guard). */\n blockNumber?: string | null;\n};\n\n/** Structured cause for service metrics; calculation still fails closed. */\nexport class SettlementTrackerError extends Error {\n constructor(\n public readonly marketId: string,\n public readonly reason: 'missing' | 'inconsistent',\n ) {\n super(\n reason === 'missing'\n ? `Missing market trackers for ${marketId}`\n : `Inconsistent ADL trackers for ${marketId}`,\n );\n // common targets ES5; restore Error subclass identity after downleveling.\n Object.setPrototypeOf(this, new.target.prototype);\n this.name = 'SettlementTrackerError';\n }\n}\n\nconst WAD = new BigNumber('1000000000000000000');\nconst integer = (value: { toString(): string } | null, field: string) => {\n const result = new BigNumber(value?.toString() ?? NaN);\n if (!result.isInteger()) throw new Error(`Missing or invalid ${field}`);\n return result;\n};\nconst optionalBlock = (value: { toString(): string } | null | undefined) => {\n if (value === null || value === undefined) return undefined;\n const result = new BigNumber(value.toString());\n return result.isInteger() ? result : undefined;\n};\nconst mulWad = (a: BigNumber, b: BigNumber) =>\n a.times(b).dividedToIntegerBy(WAD);\nconst divWad = (a: BigNumber, b: BigNumber) =>\n a.times(WAD).dividedToIntegerBy(b);\n\n/** Mirrors perpOB updatePerpPosition, including PRBMath truncation at each step. */\nexport function calculateSettledPositionPnl(\n position: RealizedBalancePosition,\n trackers: RealizedBalanceTrackers | undefined,\n onGuarded?: () => void,\n): BigNumber {\n const base = integer(position.base, 'position base');\n const realized = integer(position.realized_pnl, 'realized PnL');\n if (base.isZero()) return realized;\n if (!trackers)\n throw new SettlementTrackerError(position.market_id.toString(), 'missing');\n\n const long = base.isPositive();\n const funding = integer(\n long\n ? trackers.longTrackersFundingValue\n : trackers.shortTrackersFundingValue,\n 'market funding',\n );\n const multiplier = integer(\n long\n ? trackers.longTrackersBaseMultiplier\n : trackers.shortTrackersBaseMultiplier,\n 'market multiplier',\n );\n const unwind = integer(\n long\n ? trackers.longTrackersAdlUnwindPrice\n : trackers.shortTrackersAdlUnwindPrice,\n 'market unwind price',\n );\n const oldMultiplier = integer(\n position.base_multiplier,\n 'position multiplier',\n );\n const oldUnwind = integer(position.adl_unwind_price, 'position unwind price');\n const oldFunding = integer(position.funding_value, 'position funding');\n if (\n funding.eq(oldFunding) &&\n multiplier.eq(oldMultiplier) &&\n unwind.eq(oldUnwind)\n )\n return realized;\n\n // Market trackers older than the position's own snapshot carry no new\n // cashflow: the stored realized PnL already settled everything up to the\n // position's block. Off-chain tracker feeds can lag the position stream, so\n // treat that window as \"nothing accrued\" instead of projecting a delta\n // backwards (wrong-sign funding, or an ADL guard failure below).\n const positionBlock = optionalBlock(position.block_number);\n const trackerBlock = optionalBlock(trackers.blockNumber);\n if (positionBlock && trackerBlock && positionBlock.gt(trackerBlock)) {\n onGuarded?.();\n return realized;\n }\n\n if (\n oldMultiplier.lte(0) ||\n multiplier.lt(0) ||\n multiplier.gt(oldMultiplier) ||\n unwind.lt(oldUnwind)\n ) {\n throw new SettlementTrackerError(\n position.market_id.toString(),\n 'inconsistent',\n );\n }\n const fundingCashflow = mulWad(\n divWad(funding.minus(oldFunding), oldMultiplier),\n base.negated(),\n );\n const unwindDelta = divWad(unwind.minus(oldUnwind), oldMultiplier);\n const initialPrice = mulWad(\n WAD.minus(divWad(multiplier, oldMultiplier)),\n integer(position.last_price, 'position price'),\n );\n const adlCashflow = mulWad(base, unwindDelta.minus(initialPrice));\n return realized.plus(fundingCashflow).plus(adlCashflow);\n}\n\n/**\n * Returns raw E18 PnL aligned to collateral units. The instrument floors EACH\n * market's PnL to quote-token precision before Core aggregates collateral info.\n * Funding is the stored market tracker delta; do not extrapolate it with time.\n */\nexport function calculateSettledAccountPnl(\n positions: RealizedBalancePosition[],\n trackers: Partial<Record<string, RealizedBalanceTrackers>>,\n collateralDecimals: number,\n onGuarded?: () => void,\n): BigNumber {\n return positions.reduce((sum, position) => {\n const pnl = calculateSettledPositionPnl(\n position,\n trackers[position.market_id.toString()],\n onGuarded,\n );\n const rounded = pnl\n .shiftedBy(collateralDecimals - 18)\n .integerValue(BigNumber.ROUND_FLOOR)\n .shiftedBy(18 - collateralDecimals);\n return sum.plus(rounded);\n }, new BigNumber(0));\n}\n"]}
1
+ {"version":3,"file":"realized-balance.js","sourceRoot":"/","sources":["utils/realized-balance.ts"],"names":[],"mappings":"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","sourcesContent":["import BigNumber from 'bignumber.js';\n\n/** Raw E18 position values, from either Prisma or replication. */\nexport type RealizedBalancePosition = {\n market_id: { toString(): string };\n base: { toString(): string } | null;\n realized_pnl: { toString(): string } | null;\n funding_value: { toString(): string } | null;\n base_multiplier: { toString(): string } | null;\n adl_unwind_price: { toString(): string } | null;\n last_price: { toString(): string } | null;\n /** Block the stored trackers were written at; lets stale market trackers be ignored. */\n block_number?: { toString(): string } | null;\n};\n\nexport type RealizedBalanceTrackers = {\n longTrackersFundingValue: string;\n shortTrackersFundingValue: string;\n longTrackersBaseMultiplier: string;\n shortTrackersBaseMultiplier: string;\n longTrackersAdlUnwindPrice: string;\n shortTrackersAdlUnwindPrice: string;\n /** Block the market trackers were observed at (optional; enables the staleness guard). */\n blockNumber?: string | null;\n};\n\n/** Structured cause for service metrics; calculation still fails closed. */\nexport class SettlementTrackerError extends Error {\n constructor(\n public readonly marketId: string,\n public readonly reason: 'missing' | 'inconsistent',\n ) {\n super(\n reason === 'missing'\n ? `Missing market trackers for ${marketId}`\n : `Inconsistent ADL trackers for ${marketId}`,\n );\n // common targets ES5; restore Error subclass identity after downleveling.\n Object.setPrototypeOf(this, new.target.prototype);\n this.name = 'SettlementTrackerError';\n }\n}\n\nconst WAD = new BigNumber('1000000000000000000');\nconst integer = (value: { toString(): string } | null, field: string) => {\n const result = new BigNumber(value?.toString() ?? NaN);\n if (!result.isInteger()) throw new Error(`Missing or invalid ${field}`);\n return result;\n};\nconst optionalBlock = (value: { toString(): string } | null | undefined) => {\n if (value === null || value === undefined) return undefined;\n const result = new BigNumber(value.toString());\n return result.isInteger() ? result : undefined;\n};\n/**\n * Market trackers older than the position's own snapshot carry nothing the\n * stored position does not already reflect. Off-chain tracker feeds can lag\n * the position stream.\n */\nexport function trackersBehindPosition(\n position: Pick<RealizedBalancePosition, 'block_number'>,\n trackers: Pick<RealizedBalanceTrackers, 'blockNumber'>,\n): boolean {\n const positionBlock = optionalBlock(position.block_number);\n const trackerBlock = optionalBlock(trackers.blockNumber);\n return !!positionBlock && !!trackerBlock && positionBlock.gt(trackerBlock);\n}\n\n/**\n * The trackers to ADL-adjust a position's quantity with, guarded the way its\n * settlement is: behind the position, its own multiplier and unwind price stand in.\n */\nexport function positionAdjustmentTrackers<\n T extends Pick<\n RealizedBalanceTrackers,\n | 'longTrackersBaseMultiplier'\n | 'shortTrackersBaseMultiplier'\n | 'longTrackersAdlUnwindPrice'\n | 'shortTrackersAdlUnwindPrice'\n | 'blockNumber'\n >,\n>(\n position: Pick<\n RealizedBalancePosition,\n 'block_number' | 'base_multiplier' | 'adl_unwind_price'\n >,\n trackers: T,\n): T {\n if (\n !trackersBehindPosition(position, trackers) ||\n position.base_multiplier === null ||\n position.adl_unwind_price === null\n )\n return trackers;\n const multiplier = position.base_multiplier.toString();\n const unwind = position.adl_unwind_price.toString();\n return {\n ...trackers,\n longTrackersBaseMultiplier: multiplier,\n shortTrackersBaseMultiplier: multiplier,\n longTrackersAdlUnwindPrice: unwind,\n shortTrackersAdlUnwindPrice: unwind,\n };\n}\n\nconst mulWad = (a: BigNumber, b: BigNumber) =>\n a.times(b).dividedToIntegerBy(WAD);\nconst divWad = (a: BigNumber, b: BigNumber) =>\n a.times(WAD).dividedToIntegerBy(b);\n\n/** Mirrors perpOB updatePerpPosition, including PRBMath truncation at each step. */\nexport function calculateSettledPositionPnl(\n position: RealizedBalancePosition,\n trackers: RealizedBalanceTrackers | undefined,\n onGuarded?: () => void,\n): BigNumber {\n const base = integer(position.base, 'position base');\n const realized = integer(position.realized_pnl, 'realized PnL');\n if (base.isZero()) return realized;\n if (!trackers)\n throw new SettlementTrackerError(position.market_id.toString(), 'missing');\n\n const long = base.isPositive();\n const funding = integer(\n long\n ? trackers.longTrackersFundingValue\n : trackers.shortTrackersFundingValue,\n 'market funding',\n );\n const multiplier = integer(\n long\n ? trackers.longTrackersBaseMultiplier\n : trackers.shortTrackersBaseMultiplier,\n 'market multiplier',\n );\n const unwind = integer(\n long\n ? trackers.longTrackersAdlUnwindPrice\n : trackers.shortTrackersAdlUnwindPrice,\n 'market unwind price',\n );\n const oldMultiplier = integer(\n position.base_multiplier,\n 'position multiplier',\n );\n const oldUnwind = integer(position.adl_unwind_price, 'position unwind price');\n const oldFunding = integer(position.funding_value, 'position funding');\n if (\n funding.eq(oldFunding) &&\n multiplier.eq(oldMultiplier) &&\n unwind.eq(oldUnwind)\n )\n return realized;\n\n // Market trackers older than the position's own snapshot carry no new\n // cashflow: the stored realized PnL already settled everything up to the\n // position's block. Treat that window as \"nothing accrued\" instead of\n // projecting a delta backwards (wrong-sign funding, or an ADL guard failure\n // below).\n if (trackersBehindPosition(position, trackers)) {\n onGuarded?.();\n return realized;\n }\n\n if (\n oldMultiplier.lte(0) ||\n multiplier.lt(0) ||\n multiplier.gt(oldMultiplier) ||\n unwind.lt(oldUnwind)\n ) {\n throw new SettlementTrackerError(\n position.market_id.toString(),\n 'inconsistent',\n );\n }\n const fundingCashflow = mulWad(\n divWad(funding.minus(oldFunding), oldMultiplier),\n base.negated(),\n );\n const unwindDelta = divWad(unwind.minus(oldUnwind), oldMultiplier);\n const initialPrice = mulWad(\n WAD.minus(divWad(multiplier, oldMultiplier)),\n integer(position.last_price, 'position price'),\n );\n const adlCashflow = mulWad(base, unwindDelta.minus(initialPrice));\n return realized.plus(fundingCashflow).plus(adlCashflow);\n}\n\n/**\n * Returns raw E18 PnL aligned to collateral units. The instrument floors EACH\n * market's PnL to quote-token precision before Core aggregates collateral info.\n * Funding is the stored market tracker delta; do not extrapolate it with time.\n */\nexport function calculateSettledAccountPnl(\n positions: RealizedBalancePosition[],\n trackers: Partial<Record<string, RealizedBalanceTrackers>>,\n collateralDecimals: number,\n onGuarded?: () => void,\n): BigNumber {\n return positions.reduce((sum, position) => {\n const pnl = calculateSettledPositionPnl(\n position,\n trackers[position.market_id.toString()],\n onGuarded,\n );\n const rounded = pnl\n .shiftedBy(collateralDecimals - 18)\n .integerValue(BigNumber.ROUND_FLOOR)\n .shiftedBy(18 - collateralDecimals);\n return sum.plus(rounded);\n }, new BigNumber(0));\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reyaxyz/common",
3
- "version": "0.373.1",
3
+ "version": "0.374.0",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "registry": "https://registry.npmjs.org"
@@ -41,5 +41,5 @@
41
41
  "test:coverage": "npx jest test --coverage",
42
42
  "generate:coverage-badges": "npx istanbul-badges-readme --silent"
43
43
  },
44
- "gitHead": "c4733b7a18ca9e87db1d10e750da92340d59a384"
44
+ "gitHead": "c79e1c00f842259ddcb7c435ed799ea0b49e9939"
45
45
  }
@@ -52,6 +52,57 @@ const optionalBlock = (value: { toString(): string } | null | undefined) => {
52
52
  const result = new BigNumber(value.toString());
53
53
  return result.isInteger() ? result : undefined;
54
54
  };
55
+ /**
56
+ * Market trackers older than the position's own snapshot carry nothing the
57
+ * stored position does not already reflect. Off-chain tracker feeds can lag
58
+ * the position stream.
59
+ */
60
+ export function trackersBehindPosition(
61
+ position: Pick<RealizedBalancePosition, 'block_number'>,
62
+ trackers: Pick<RealizedBalanceTrackers, 'blockNumber'>,
63
+ ): boolean {
64
+ const positionBlock = optionalBlock(position.block_number);
65
+ const trackerBlock = optionalBlock(trackers.blockNumber);
66
+ return !!positionBlock && !!trackerBlock && positionBlock.gt(trackerBlock);
67
+ }
68
+
69
+ /**
70
+ * The trackers to ADL-adjust a position's quantity with, guarded the way its
71
+ * settlement is: behind the position, its own multiplier and unwind price stand in.
72
+ */
73
+ export function positionAdjustmentTrackers<
74
+ T extends Pick<
75
+ RealizedBalanceTrackers,
76
+ | 'longTrackersBaseMultiplier'
77
+ | 'shortTrackersBaseMultiplier'
78
+ | 'longTrackersAdlUnwindPrice'
79
+ | 'shortTrackersAdlUnwindPrice'
80
+ | 'blockNumber'
81
+ >,
82
+ >(
83
+ position: Pick<
84
+ RealizedBalancePosition,
85
+ 'block_number' | 'base_multiplier' | 'adl_unwind_price'
86
+ >,
87
+ trackers: T,
88
+ ): T {
89
+ if (
90
+ !trackersBehindPosition(position, trackers) ||
91
+ position.base_multiplier === null ||
92
+ position.adl_unwind_price === null
93
+ )
94
+ return trackers;
95
+ const multiplier = position.base_multiplier.toString();
96
+ const unwind = position.adl_unwind_price.toString();
97
+ return {
98
+ ...trackers,
99
+ longTrackersBaseMultiplier: multiplier,
100
+ shortTrackersBaseMultiplier: multiplier,
101
+ longTrackersAdlUnwindPrice: unwind,
102
+ shortTrackersAdlUnwindPrice: unwind,
103
+ };
104
+ }
105
+
55
106
  const mulWad = (a: BigNumber, b: BigNumber) =>
56
107
  a.times(b).dividedToIntegerBy(WAD);
57
108
  const divWad = (a: BigNumber, b: BigNumber) =>
@@ -103,12 +154,10 @@ export function calculateSettledPositionPnl(
103
154
 
104
155
  // Market trackers older than the position's own snapshot carry no new
105
156
  // cashflow: the stored realized PnL already settled everything up to the
106
- // position's block. Off-chain tracker feeds can lag the position stream, so
107
- // treat that window as "nothing accrued" instead of projecting a delta
108
- // backwards (wrong-sign funding, or an ADL guard failure below).
109
- const positionBlock = optionalBlock(position.block_number);
110
- const trackerBlock = optionalBlock(trackers.blockNumber);
111
- if (positionBlock && trackerBlock && positionBlock.gt(trackerBlock)) {
157
+ // position's block. Treat that window as "nothing accrued" instead of
158
+ // projecting a delta backwards (wrong-sign funding, or an ADL guard failure
159
+ // below).
160
+ if (trackersBehindPosition(position, trackers)) {
112
161
  onGuarded?.();
113
162
  return realized;
114
163
  }