@reyaxyz/common 0.362.0 → 0.363.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 +1 -1
- package/dist/commands/exposure/command.js +8 -22
- package/dist/commands/exposure/command.js.map +1 -1
- package/dist/incentives-api-types.js.map +1 -1
- package/dist/services/getNativeToken.js +1 -0
- package/dist/services/getNativeToken.js.map +1 -1
- package/dist/services/getSocketAddresses.js +1 -0
- package/dist/services/getSocketAddresses.js.map +1 -1
- package/dist/trading-api-types.js.map +1 -1
- package/dist/transactions/abis/Errors.json +1638 -34
- package/dist/transactions/abis/OrdersGateway.json +729 -123
- package/dist/transactions/abis/PassivePerpProxy.json +542 -0
- package/dist/transactions/buildMulticallTx.js.map +1 -1
- package/dist/transactions/consts.js +1 -2
- package/dist/transactions/consts.js.map +1 -1
- package/dist/transactions/contractAddresses.js +45 -5
- package/dist/transactions/contractAddresses.js.map +1 -1
- package/dist/transactions/sign.js +237 -111
- package/dist/transactions/sign.js.map +1 -1
- package/dist/types/commands/exposure/command.d.ts +8 -7
- package/dist/types/commands/exposure/command.d.ts.map +1 -1
- package/dist/types/incentives-api-types.d.ts +61 -5
- package/dist/types/incentives-api-types.d.ts.map +1 -1
- package/dist/types/services/getNativeToken.d.ts.map +1 -1
- package/dist/types/services/getSocketAddresses.d.ts.map +1 -1
- package/dist/types/trading-api-types.d.ts +200 -32
- package/dist/types/trading-api-types.d.ts.map +1 -1
- package/dist/types/transactions/buildMulticallTx.d.ts +1 -0
- package/dist/types/transactions/buildMulticallTx.d.ts.map +1 -1
- package/dist/types/transactions/consts.d.ts +0 -1
- package/dist/types/transactions/consts.d.ts.map +1 -1
- package/dist/types/transactions/contractAddresses.d.ts +0 -1
- package/dist/types/transactions/contractAddresses.d.ts.map +1 -1
- package/dist/types/transactions/sign.d.ts +113 -9
- package/dist/types/transactions/sign.d.ts.map +1 -1
- package/dist/types/types.d.ts +62 -63
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils/calculate.d.ts.map +1 -1
- package/dist/types/utils/consts.d.ts +2 -2
- package/dist/types/utils/consts.d.ts.map +1 -1
- package/dist/types/utils/error-sanitizer.d.ts +15 -0
- package/dist/types/utils/error-sanitizer.d.ts.map +1 -0
- package/dist/types/utils/index.d.ts +3 -0
- package/dist/types/utils/index.d.ts.map +1 -1
- package/dist/types/utils/network.d.ts.map +1 -1
- package/dist/types/utils/number.d.ts +5 -0
- package/dist/types/utils/number.d.ts.map +1 -1
- package/dist/types/utils/realized-balance.d.ts +54 -0
- package/dist/types/utils/realized-balance.d.ts.map +1 -0
- package/dist/types/utils/throttled-error.d.ts +42 -0
- package/dist/types/utils/throttled-error.d.ts.map +1 -0
- package/dist/types/utils/token/normalize.d.ts +2 -1
- package/dist/types/utils/token/normalize.d.ts.map +1 -1
- package/dist/types/utils/token/token-info-getters.d.ts.map +1 -1
- package/dist/types/utils/token/token-info.d.ts.map +1 -1
- package/dist/types.js +25 -11
- package/dist/types.js.map +1 -1
- package/dist/utils/calculate.js +18 -9
- package/dist/utils/calculate.js.map +1 -1
- package/dist/utils/consts.js +2 -7
- package/dist/utils/consts.js.map +1 -1
- package/dist/utils/error-sanitizer.js +135 -0
- package/dist/utils/error-sanitizer.js.map +1 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/network.js +4 -0
- package/dist/utils/network.js.map +1 -1
- package/dist/utils/number.js +96 -1
- package/dist/utils/number.js.map +1 -1
- package/dist/utils/realized-balance.js +126 -0
- package/dist/utils/realized-balance.js.map +1 -0
- package/dist/utils/throttled-error.js +60 -0
- package/dist/utils/throttled-error.js.map +1 -0
- package/dist/utils/token/normalize.js +2 -2
- package/dist/utils/token/normalize.js.map +1 -1
- package/dist/utils/token/token-info-getters.js +80 -5
- package/dist/utils/token/token-info-getters.js.map +1 -1
- package/dist/utils/token/token-info.js +11 -0
- package/dist/utils/token/token-info.js.map +1 -1
- package/package.json +3 -3
- package/src/commands/exposure/command.ts +8 -38
- package/src/incentives-api-types.ts +60 -5
- package/src/services/getNativeToken.ts +1 -0
- package/src/services/getSocketAddresses.ts +1 -0
- package/src/trading-api-types.ts +258 -39
- package/src/transactions/abis/Errors.json +5478 -3874
- package/src/transactions/abis/OrdersGateway.json +730 -124
- package/src/transactions/abis/PassivePerpProxy.json +542 -0
- package/src/transactions/buildMulticallTx.ts +4 -0
- package/src/transactions/consts.ts +0 -1
- package/src/transactions/contractAddresses.ts +42 -7
- package/src/transactions/sign.ts +306 -150
- package/src/types.ts +102 -82
- package/src/utils/calculate.ts +21 -8
- package/src/utils/consts.ts +4 -16
- package/src/utils/error-sanitizer.ts +137 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/network.ts +4 -0
- package/src/utils/number.ts +110 -0
- package/src/utils/realized-balance.ts +163 -0
- package/src/utils/throttled-error.ts +85 -0
- package/src/utils/token/normalize.ts +6 -1
- package/src/utils/token/token-info-getters.ts +104 -6
- package/src/utils/token/token-info.ts +78 -0
package/src/types.ts
CHANGED
|
@@ -49,6 +49,7 @@ export type MarketEntity = {
|
|
|
49
49
|
|
|
50
50
|
export enum ReyaChainId {
|
|
51
51
|
reyaNetwork = 1729,
|
|
52
|
+
reyaLocal = 31337,
|
|
52
53
|
reyaCronos = 89346162,
|
|
53
54
|
}
|
|
54
55
|
|
|
@@ -145,16 +146,14 @@ export type StrippedMarginAccountEntity = Pick<
|
|
|
145
146
|
'id' | 'name'
|
|
146
147
|
>;
|
|
147
148
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
};
|
|
157
|
-
};
|
|
149
|
+
// PRO-126 Bug 5: `ConditionalOrdersInfo` and the
|
|
150
|
+
// `PositionEntity.conditionalOrdersInfo` field that surfaced
|
|
151
|
+
// pending SL/TP info on positions were retired alongside the
|
|
152
|
+
// `OrdersGateway.execute` / conditional-orders DB pipeline. The
|
|
153
|
+
// off-chain API no longer has a backing source for live trigger
|
|
154
|
+
// metadata; once the ME exposes pending trigger info via its own
|
|
155
|
+
// surface, an equivalent field can be reintroduced sourcing from
|
|
156
|
+
// there.
|
|
158
157
|
|
|
159
158
|
export type PositionEntity = {
|
|
160
159
|
id: number;
|
|
@@ -162,7 +161,6 @@ export type PositionEntity = {
|
|
|
162
161
|
size: number;
|
|
163
162
|
base: number;
|
|
164
163
|
price: number;
|
|
165
|
-
conditionalOrdersInfo?: ConditionalOrdersInfo;
|
|
166
164
|
markPrice: number;
|
|
167
165
|
orderStatus: Status;
|
|
168
166
|
realisedPnl: number;
|
|
@@ -175,8 +173,6 @@ export type PositionEntity = {
|
|
|
175
173
|
accountId: number;
|
|
176
174
|
};
|
|
177
175
|
|
|
178
|
-
export type OrderType = 'market';
|
|
179
|
-
|
|
180
176
|
export type OrderHistoryType =
|
|
181
177
|
| 'long-trade'
|
|
182
178
|
| 'short-trade'
|
|
@@ -186,7 +182,7 @@ export type OrderHistoryType =
|
|
|
186
182
|
export type OrderHistoryEntity = {
|
|
187
183
|
id: string;
|
|
188
184
|
action: OrderHistoryType;
|
|
189
|
-
orderType:
|
|
185
|
+
orderType: 'market';
|
|
190
186
|
base: number;
|
|
191
187
|
executionPrice: number;
|
|
192
188
|
realisedPnl?: number | null;
|
|
@@ -260,34 +256,6 @@ export type LpTransactionHistoryEntity = {
|
|
|
260
256
|
timestamp: number;
|
|
261
257
|
};
|
|
262
258
|
|
|
263
|
-
export type MarginAccountTransactionHistoryType =
|
|
264
|
-
| 'deposit'
|
|
265
|
-
| 'withdrawal'
|
|
266
|
-
| 'transfer'
|
|
267
|
-
| 'auto_exchange_deposit'
|
|
268
|
-
| 'auto_exchange_withdrawal';
|
|
269
|
-
|
|
270
|
-
export type MarginAccountTransactionStatus = 'confirmed' | 'pending';
|
|
271
|
-
|
|
272
|
-
export type MarginAccountTransactionHistoryEntity = {
|
|
273
|
-
id: number;
|
|
274
|
-
type: MarginAccountTransactionHistoryType;
|
|
275
|
-
token: string;
|
|
276
|
-
amount: number;
|
|
277
|
-
transactionLink: string;
|
|
278
|
-
status: MarginAccountTransactionStatus;
|
|
279
|
-
timestamp: number;
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
export type ReferralFeeTransactionHistoryEntity = {
|
|
283
|
-
id: number;
|
|
284
|
-
referee_wallet_address: string;
|
|
285
|
-
token: string;
|
|
286
|
-
amount: number;
|
|
287
|
-
transactionLink: string;
|
|
288
|
-
timestamp: number;
|
|
289
|
-
};
|
|
290
|
-
|
|
291
259
|
export type TokenEntity = {
|
|
292
260
|
name: TokenName;
|
|
293
261
|
address: Address;
|
|
@@ -776,6 +744,23 @@ export type TransactionExecutionOutput = {
|
|
|
776
744
|
txHash: string | null;
|
|
777
745
|
txSubmitted?: boolean;
|
|
778
746
|
errorMessage?: string;
|
|
747
|
+
/**
|
|
748
|
+
* Structured error discriminator surfaced by the slim
|
|
749
|
+
* `executeReyaTransaction` core. One of
|
|
750
|
+
* `TRANSACTION_FAILED_TO_SEND` (pre-send: wallet manager rejected),
|
|
751
|
+
* `RECEIPT_NOT_FOUND` (post-send: receipt fetch failed),
|
|
752
|
+
* `TRANSACTION_REVERTED` (post-send: receipt.status === 0), or
|
|
753
|
+
* `FAILED_FILL` (post-send: FailedUnifiedFillBytes / FailedFillBytes).
|
|
754
|
+
* Optional — populated when the slim core returned a discriminated
|
|
755
|
+
* failure. May coexist with a non-null `txHash` on post-send failures
|
|
756
|
+
* (the on-chain hash is surfaced for `RECEIPT_NOT_FOUND`,
|
|
757
|
+
* `TRANSACTION_REVERTED`, and `FAILED_FILL`). UI consumers should prefer
|
|
758
|
+
* `errorMessage` for display; this field is for analytics + logs.
|
|
759
|
+
*
|
|
760
|
+
* `FAILED_ORDER` (used to be: FailedOrderBytes) is retired alongside
|
|
761
|
+
* the legacy `OrdersGateway.execute` IOC handler in PRO-126 Bug 5.
|
|
762
|
+
*/
|
|
763
|
+
errorCode?: string;
|
|
779
764
|
accountId?: string | null;
|
|
780
765
|
coreSigNonce?: string | null;
|
|
781
766
|
poolSigNonce?: string | null;
|
|
@@ -841,16 +826,29 @@ export type PendingWithdrawal = {
|
|
|
841
826
|
tokenAmount: number;
|
|
842
827
|
} & (PendingWithdrawalStatus | ReadyWithdrawalStatus);
|
|
843
828
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
829
|
+
// PRO-126 Bug 5 retired the legacy `OrdersGatewayOrderType` enum (the
|
|
830
|
+
// 8-field `ConditionalOrderDetails` typehash discriminator). The
|
|
831
|
+
// on-chain `OrdersGateway.execute` selector that consumed those values
|
|
832
|
+
// is gone; every order class now uses `OrderType` below, whose values
|
|
833
|
+
// mirror the on-chain `OrderType` enum in
|
|
834
|
+
// orders-gateway/src/libraries/DataTypes.sol.
|
|
835
|
+
export enum OrderType {
|
|
836
|
+
Limit = 0,
|
|
837
|
+
StopLoss = 1,
|
|
838
|
+
TakeProfit = 2,
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// Matches the on-chain `MarketType` enum. Used to discriminate spot vs perp
|
|
842
|
+
// in unified off-chain code paths and with the market-id namespace helpers in
|
|
843
|
+
// packages/common-backend/src/market-id-namespace.
|
|
844
|
+
export enum MarketType {
|
|
845
|
+
Spot = 0,
|
|
846
|
+
Perp = 1,
|
|
852
847
|
}
|
|
853
848
|
|
|
849
|
+
// Canonical on-chain V3 codes, also written by the pre-cutover V2 indexer.
|
|
850
|
+
export { CanonicalExecutionType as ExecutionType } from './canonical-execution-type';
|
|
851
|
+
|
|
854
852
|
export enum ConditionalOrderType {
|
|
855
853
|
STOP_LOSS = 0,
|
|
856
854
|
TAKE_PROFIT = 1,
|
|
@@ -965,23 +963,6 @@ export type V1AuthenticatorTransport =
|
|
|
965
963
|
| 'AUTHENTICATOR_TRANSPORT_HYBRID';
|
|
966
964
|
|
|
967
965
|
// on-chain events (consider pushing into a separate file)
|
|
968
|
-
export type PositionDataUpdatedDetails = {
|
|
969
|
-
marketId: string;
|
|
970
|
-
accountId: string;
|
|
971
|
-
// todo: p1: argument names don't align with the on-chain event
|
|
972
|
-
orderBase: string;
|
|
973
|
-
realisedPnl: string;
|
|
974
|
-
lastPrice: string;
|
|
975
|
-
lastPriceTimestamp: string;
|
|
976
|
-
fundingValue: string;
|
|
977
|
-
baseMultiplier: string;
|
|
978
|
-
adlUnwindPrice: string;
|
|
979
|
-
blockTimestamp: string;
|
|
980
|
-
transactionHash: string;
|
|
981
|
-
blockNumber: number;
|
|
982
|
-
uniqueId: string;
|
|
983
|
-
};
|
|
984
|
-
|
|
985
966
|
export type MatchOrderDetails = {
|
|
986
967
|
uniqueId: string;
|
|
987
968
|
transactionHash: string;
|
|
@@ -1040,10 +1021,11 @@ export interface PassivePerpOrderAndPositionUpdate {
|
|
|
1040
1021
|
uniqueId: bigint;
|
|
1041
1022
|
}
|
|
1042
1023
|
|
|
1043
|
-
export type
|
|
1024
|
+
export type ConfiguredAssetPair =
|
|
1044
1025
|
| 'ETHUSD'
|
|
1045
1026
|
| 'WSTETHUSD'
|
|
1046
1027
|
| 'WBTCUSD'
|
|
1028
|
+
| 'BTCUSD' // Localnet's mock BTC collateral feed
|
|
1047
1029
|
| 'USDCUSD'
|
|
1048
1030
|
| 'USDEUSD'
|
|
1049
1031
|
| 'SUSDEUSD'
|
|
@@ -1132,6 +1114,19 @@ export type AssetPair =
|
|
|
1132
1114
|
| 'LINEAUSDMARK'
|
|
1133
1115
|
| 'MEGAUSDMARK';
|
|
1134
1116
|
|
|
1117
|
+
type SpotPair<T extends string> = T extends `${infer Base}USDMARK`
|
|
1118
|
+
? `${Base}USD`
|
|
1119
|
+
: never;
|
|
1120
|
+
|
|
1121
|
+
/** Internal cache keys whose prices mirror an existing reference feed. */
|
|
1122
|
+
export type MirroredAssetPair = 'REYALM#KEYROCKRUSD' | 'REYALM#FLOWRUSD';
|
|
1123
|
+
|
|
1124
|
+
/** Includes Stork spot feeds and mirrored price cache keys. */
|
|
1125
|
+
export type AssetPair =
|
|
1126
|
+
| ConfiguredAssetPair
|
|
1127
|
+
| SpotPair<ConfiguredAssetPair>
|
|
1128
|
+
| MirroredAssetPair;
|
|
1129
|
+
|
|
1135
1130
|
export type ContractId = string;
|
|
1136
1131
|
|
|
1137
1132
|
export type Price = {
|
|
@@ -1143,9 +1138,31 @@ export type Price = {
|
|
|
1143
1138
|
|
|
1144
1139
|
export type MarketPrice = {
|
|
1145
1140
|
marketId: number;
|
|
1146
|
-
|
|
1141
|
+
/** @deprecated No longer written; will be removed when V1 prices endpoints are dropped. */
|
|
1142
|
+
price?: string;
|
|
1143
|
+
/**
|
|
1144
|
+
* ME mark price (E18), despite the name — the pool-price task has written the
|
|
1145
|
+
* matching-engine mark here since the perpOB rewire. Surfaced as the v2
|
|
1146
|
+
* `MarketSummary.markPrice`. The genuine index price is `indexPrice`.
|
|
1147
|
+
*/
|
|
1147
1148
|
oraclePrice: string;
|
|
1148
|
-
|
|
1149
|
+
/**
|
|
1150
|
+
* Orderbook mid (E18). Absent on an empty or one-sided book — there is
|
|
1151
|
+
* deliberately no mark-price fallback, because publishing the mark under the
|
|
1152
|
+
* name "mid" makes a book with no two-sided quote indistinguishable from one
|
|
1153
|
+
* trading exactly at the mark. Consumers must treat it as optional, the same
|
|
1154
|
+
* way `SpotMarketSummary.throttledMidPrice` has always been treated.
|
|
1155
|
+
*/
|
|
1156
|
+
poolPrice?: string;
|
|
1157
|
+
/**
|
|
1158
|
+
* Stork spot `*USD` index price (E18), rUSD-quoted — divided by USDCUSD the
|
|
1159
|
+
* same way the collateral prices are, since Stork publishes it as X/USD and
|
|
1160
|
+
* every price this API serves is X/USDC. Absent when the feed has no fresh
|
|
1161
|
+
* tick, when the USDCUSD cross-rate is unavailable, and permanently on
|
|
1162
|
+
* markets with no Stork feed, so consumers must treat it as optional rather
|
|
1163
|
+
* than substituting a stale value.
|
|
1164
|
+
*/
|
|
1165
|
+
indexPrice?: string;
|
|
1149
1166
|
updatedAt: number;
|
|
1150
1167
|
};
|
|
1151
1168
|
|
|
@@ -1163,9 +1180,13 @@ export const isMarketPrice = (
|
|
|
1163
1180
|
// Replication-derived types
|
|
1164
1181
|
type Unpacked<T> = T extends (infer U)[] ? U : T;
|
|
1165
1182
|
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1183
|
+
// PRO-126 Bug 7: `ConditionalOrderSensitive` + `OrderReplicationData`
|
|
1184
|
+
// retired with the rest of the `ConditionalOrders` DB pipeline. The
|
|
1185
|
+
// `replication:ConditionalOrdersSensitive` channel is no longer
|
|
1186
|
+
// subscribed (bun-socket/index.ts) and no longer publishes
|
|
1187
|
+
// (common-backend/src/config/database.ts). Order data flows through
|
|
1188
|
+
// the ME's `{orders}:changes` stream consumed by AccountOrdersMap;
|
|
1189
|
+
// callers should derive types from the ME's protobuf `Order` schema.
|
|
1169
1190
|
|
|
1170
1191
|
export type Position = Unpacked<Replication['replication:position']['result']>;
|
|
1171
1192
|
|
|
@@ -1174,13 +1195,12 @@ export type PositionReplicationData = Stringified<
|
|
|
1174
1195
|
Unpacked<Replication['replication:position']['result']>
|
|
1175
1196
|
>;
|
|
1176
1197
|
|
|
1177
|
-
export type
|
|
1178
|
-
Unpacked<Replication['replication:
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
>;
|
|
1198
|
+
export type PerpExecutionReplicationData = Omit<
|
|
1199
|
+
Stringified<Unpacked<Replication['replication:orders']['result']>>,
|
|
1200
|
+
'created_at'
|
|
1201
|
+
> & {
|
|
1202
|
+
created_at?: string;
|
|
1203
|
+
};
|
|
1184
1204
|
|
|
1185
1205
|
export type AccountRealBalanceReplicationData = Stringified<
|
|
1186
1206
|
Unpacked<Replication['replication:account_real_balances']['result']>
|
|
@@ -1190,8 +1210,8 @@ export type SpotExecutionReplicationData = Stringified<
|
|
|
1190
1210
|
Unpacked<Replication['replication:spot_executions']['result']>
|
|
1191
1211
|
>;
|
|
1192
1212
|
|
|
1193
|
-
export type
|
|
1194
|
-
Unpacked<Replication['replication:
|
|
1213
|
+
export type ExecutionBustReplicationData = Stringified<
|
|
1214
|
+
Unpacked<Replication['replication:execution_busts']['result']>
|
|
1195
1215
|
>;
|
|
1196
1216
|
|
|
1197
1217
|
export type MarketDataReplicationData = Stringified<
|
package/src/utils/calculate.ts
CHANGED
|
@@ -593,7 +593,10 @@ export const calculateTotalMargin = ({
|
|
|
593
593
|
let fundingCashFlow = 0;
|
|
594
594
|
let adlCashFlow = 0;
|
|
595
595
|
|
|
596
|
-
|
|
596
|
+
// Mirrors calculateSettledPositionPnl: a flat position accrues nothing, and
|
|
597
|
+
// a zero position multiplier (reopened after full ADL) cannot be divided by,
|
|
598
|
+
// so only realized and mark PnL count for it.
|
|
599
|
+
if (marketTracker && position.base !== 0 && position.baseMultiplier > 0) {
|
|
597
600
|
// fundingCashFlow
|
|
598
601
|
const marketTrackerFundingValue =
|
|
599
602
|
position.base < 0
|
|
@@ -619,13 +622,18 @@ export const calculateTotalMargin = ({
|
|
|
619
622
|
adlCashFlow = (adlUnwindPrice - initialPrice) * position.base;
|
|
620
623
|
}
|
|
621
624
|
|
|
625
|
+
// The underlying supplies the mark; all position cashflows are in the quote token.
|
|
626
|
+
// A missing mark means "no price for this bucket", not a price of zero:
|
|
627
|
+
// carry the position at its last price rather than swinging by its notional.
|
|
628
|
+
const markPrice = markPrices.get(tokenInfo.underlyingAssetId);
|
|
622
629
|
const unrealizedPnl =
|
|
623
|
-
|
|
624
|
-
|
|
630
|
+
markPrice === undefined
|
|
631
|
+
? 0
|
|
632
|
+
: (markPrice - position.lastPrice) * position.base;
|
|
625
633
|
|
|
626
634
|
pnl.set(
|
|
627
|
-
tokenInfo.
|
|
628
|
-
(pnl.get(tokenInfo.
|
|
635
|
+
tokenInfo.quoteTokenId,
|
|
636
|
+
(pnl.get(tokenInfo.quoteTokenId) ?? 0) +
|
|
629
637
|
position.realizedPnl +
|
|
630
638
|
fundingCashFlow +
|
|
631
639
|
adlCashFlow +
|
|
@@ -633,10 +641,15 @@ export const calculateTotalMargin = ({
|
|
|
633
641
|
);
|
|
634
642
|
});
|
|
635
643
|
|
|
636
|
-
|
|
637
|
-
|
|
644
|
+
// A quote-token PnL balance can exist without a collateral deposit row.
|
|
645
|
+
const assets = new Set([
|
|
646
|
+
...Array.from(accountBalances.keys()),
|
|
647
|
+
...Array.from(pnl.keys()),
|
|
648
|
+
]);
|
|
649
|
+
return Array.from(assets).reduce(
|
|
650
|
+
(sum, address) =>
|
|
638
651
|
sum +
|
|
639
|
-
(
|
|
652
|
+
((accountBalances.get(address) ?? 0) + (pnl.get(address) ?? 0)) *
|
|
640
653
|
(address === rUSDAddress
|
|
641
654
|
? 1
|
|
642
655
|
: (assetPrices.get(address) ?? 0) / (rUSDPrice || 1)),
|
package/src/utils/consts.ts
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ConditionalOrderType,
|
|
3
|
-
ConditionalOrderTypeName,
|
|
4
|
-
Rank,
|
|
5
|
-
RankTrading,
|
|
6
|
-
TierType,
|
|
7
|
-
} from '../types';
|
|
1
|
+
import { Rank, RankTrading, TierType } from '../types';
|
|
8
2
|
|
|
9
3
|
export const MAX_UINT128 = 2 ** 128 - 1;
|
|
10
4
|
export const API_TIMEOUT: number = 2500;
|
|
@@ -111,15 +105,6 @@ export const RANKS_TRADING: Record<number, RankTrading> = {
|
|
|
111
105
|
},
|
|
112
106
|
};
|
|
113
107
|
|
|
114
|
-
export const conditionalOrderTypeNames: Record<
|
|
115
|
-
ConditionalOrderType,
|
|
116
|
-
ConditionalOrderTypeName
|
|
117
|
-
> = {
|
|
118
|
-
[ConditionalOrderType.STOP_LOSS]: 'Stop Loss',
|
|
119
|
-
[ConditionalOrderType.TAKE_PROFIT]: 'Take Profit',
|
|
120
|
-
[ConditionalOrderType.LIMIT_ORDER]: 'Limit Order',
|
|
121
|
-
};
|
|
122
|
-
|
|
123
108
|
export const INSTANT_TRADING_RATE_XP = 2;
|
|
124
109
|
export const TRADING_LOTTERY_BOOST = 10;
|
|
125
110
|
|
|
@@ -135,6 +120,9 @@ export const VOLUME_TIER_MAP: Record<number, number> = {
|
|
|
135
120
|
6: 5_000_000_000, // Tier 6: $5,000,000,000 volume
|
|
136
121
|
};
|
|
137
122
|
|
|
123
|
+
export const FEE_TIER_VOLUME_LOOKBACK_WINDOW_IN_SECONDS =
|
|
124
|
+
30 * ONE_DAY_IN_SECONDS;
|
|
125
|
+
|
|
138
126
|
export const TIER_TYPE_MAP: Record<number, TierType> = {
|
|
139
127
|
0: TierType.REGULAR,
|
|
140
128
|
1: TierType.REGULAR,
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Redis can echo command arguments inside an error's message. Once an error is
|
|
2
|
+
// known to carry command metadata, its message and stack must therefore be
|
|
3
|
+
// treated as tainted rather than attempting value-by-value replacement.
|
|
4
|
+
const REDACTED_COMMAND_ERROR =
|
|
5
|
+
'[redacted: Redis command error — message/stack withheld (may echo arguments)]';
|
|
6
|
+
|
|
7
|
+
function commandField(source: unknown): { name?: unknown } | undefined {
|
|
8
|
+
if (source === null || typeof source !== 'object') return undefined;
|
|
9
|
+
const command = (source as { command?: unknown }).command;
|
|
10
|
+
return command !== null && typeof command === 'object'
|
|
11
|
+
? (command as { name?: unknown })
|
|
12
|
+
: undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function redactCommandBearing(
|
|
16
|
+
source: { name?: unknown; code?: unknown; cause?: unknown },
|
|
17
|
+
command: { name?: unknown },
|
|
18
|
+
depth: number,
|
|
19
|
+
): Record<string, unknown> {
|
|
20
|
+
const out: Record<string, unknown> = {};
|
|
21
|
+
if (typeof source.name === 'string') out.name = source.name;
|
|
22
|
+
if (typeof source.code === 'string' || typeof source.code === 'number') {
|
|
23
|
+
out.code = source.code;
|
|
24
|
+
}
|
|
25
|
+
out.command = typeof command.name === 'string' ? { name: command.name } : {};
|
|
26
|
+
out.message = REDACTED_COMMAND_ERROR;
|
|
27
|
+
// The stack is intentionally withheld because it can repeat the tainted
|
|
28
|
+
// message. Causes are projected independently so safe diagnostics survive.
|
|
29
|
+
if (source.cause !== undefined && depth < 3) {
|
|
30
|
+
out.cause = normalizeError(source.cause, depth + 1);
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Project an unknown error into a serializable diagnostic record. An ioredis
|
|
37
|
+
* error carrying `command: { name, args }` is fail-closed: arguments plus the
|
|
38
|
+
* potentially argument-bearing message/stack are withheld, while safe fields
|
|
39
|
+
* such as the error name, code, command name, and projected cause survive.
|
|
40
|
+
*/
|
|
41
|
+
export function normalizeError(
|
|
42
|
+
err: unknown,
|
|
43
|
+
depth = 0,
|
|
44
|
+
): Record<string, unknown> {
|
|
45
|
+
if (err instanceof Error) {
|
|
46
|
+
// A logger must never throw because an error object has a hostile getter.
|
|
47
|
+
try {
|
|
48
|
+
const command = commandField(err);
|
|
49
|
+
if (command) return redactCommandBearing(err, command, depth);
|
|
50
|
+
|
|
51
|
+
const out: Record<string, unknown> = {};
|
|
52
|
+
if (typeof err.name === 'string') out.name = err.name;
|
|
53
|
+
if (typeof err.message === 'string') out.message = err.message;
|
|
54
|
+
if (typeof err.stack === 'string') out.stack = err.stack;
|
|
55
|
+
const { code } = err as { code?: unknown };
|
|
56
|
+
if (typeof code === 'string' || typeof code === 'number') out.code = code;
|
|
57
|
+
const { cause } = err as { cause?: unknown };
|
|
58
|
+
if (cause !== undefined && depth < 3) {
|
|
59
|
+
out.cause = normalizeError(cause, depth + 1);
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
} catch {
|
|
63
|
+
return { message: '[unserializable error]' };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Plain objects use the same command-taint rule and an allow-list projection
|
|
68
|
+
// so arbitrary password/argument fields never reach a log record.
|
|
69
|
+
if (err !== null && typeof err === 'object') {
|
|
70
|
+
try {
|
|
71
|
+
const command = commandField(err);
|
|
72
|
+
if (command) {
|
|
73
|
+
return redactCommandBearing(
|
|
74
|
+
err as { name?: unknown; code?: unknown; cause?: unknown },
|
|
75
|
+
command,
|
|
76
|
+
depth,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
const src = err as Record<string, unknown>;
|
|
80
|
+
const out: Record<string, unknown> = {};
|
|
81
|
+
if (typeof src.message === 'string') out.message = src.message;
|
|
82
|
+
if (typeof src.name === 'string') out.name = src.name;
|
|
83
|
+
if (typeof src.code === 'string' || typeof src.code === 'number') {
|
|
84
|
+
out.code = src.code;
|
|
85
|
+
}
|
|
86
|
+
if (src.cause !== undefined && depth < 3) {
|
|
87
|
+
out.cause = normalizeError(src.cause, depth + 1);
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
} catch {
|
|
91
|
+
return { message: '[unserializable error]' };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
return { message: String(err) };
|
|
97
|
+
} catch {
|
|
98
|
+
return { message: '[unserializable error]' };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Pure, in-place log-record sanitizer. Keeping this free of Winston types lets
|
|
104
|
+
* every package install a tiny adapter around the same implementation without
|
|
105
|
+
* introducing package cycles or forcing the shared leaf package to depend on a
|
|
106
|
+
* logging framework.
|
|
107
|
+
*/
|
|
108
|
+
export function sanitizeErrorLogInfo<T extends Record<string, unknown>>(
|
|
109
|
+
info: T,
|
|
110
|
+
): T {
|
|
111
|
+
const record = info as Record<string, unknown>;
|
|
112
|
+
// Nested `{ error }` and `{ cause }` are the preferred contextual shapes.
|
|
113
|
+
// Primitive values cannot carry command metadata, so preserve their existing
|
|
114
|
+
// field shape rather than wrapping them in `{ message }`.
|
|
115
|
+
if (record.error !== null && typeof record.error === 'object') {
|
|
116
|
+
record.error = normalizeError(record.error);
|
|
117
|
+
}
|
|
118
|
+
if (record.cause !== null && typeof record.cause === 'object') {
|
|
119
|
+
record.cause = normalizeError(record.cause);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Winston can flatten a raw Error passed as the second argument. Command
|
|
123
|
+
// presence alone makes the merged message/stack tainted, including stackless
|
|
124
|
+
// and plain-object variants.
|
|
125
|
+
const command = commandField(record);
|
|
126
|
+
if (command) {
|
|
127
|
+
record.message = REDACTED_COMMAND_ERROR;
|
|
128
|
+
record.stack = undefined;
|
|
129
|
+
try {
|
|
130
|
+
record.command =
|
|
131
|
+
typeof command.name === 'string' ? { name: command.name } : {};
|
|
132
|
+
} catch {
|
|
133
|
+
record.command = {};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return info;
|
|
137
|
+
}
|
package/src/utils/index.ts
CHANGED
package/src/utils/network.ts
CHANGED
|
@@ -19,6 +19,8 @@ export function getMoneyInOutNetworks(
|
|
|
19
19
|
MoneyInOutChainId.arbitrumSepolia,
|
|
20
20
|
];
|
|
21
21
|
|
|
22
|
+
if (chainId === ReyaChainId.reyaLocal) return [];
|
|
23
|
+
|
|
22
24
|
throw new Error(`Unknown Reya chain id ${chainId}`);
|
|
23
25
|
}
|
|
24
26
|
|
|
@@ -52,11 +54,13 @@ export const chainIdToExplorerURL: {
|
|
|
52
54
|
[MoneyInOutChainId.opSepolia]: 'https://sepolia-optimism.etherscan.io',
|
|
53
55
|
[MoneyInOutChainId.ethereumSepolia]: 'https://sepolia.etherscan.io',
|
|
54
56
|
[ReyaChainId.reyaNetwork]: 'https://explorer.reya.network',
|
|
57
|
+
[ReyaChainId.reyaLocal]: 'http://127.0.0.1:4000',
|
|
55
58
|
[ReyaChainId.reyaCronos]: 'https://reya-cronos.blockscout.com',
|
|
56
59
|
};
|
|
57
60
|
|
|
58
61
|
const ChainRpcUrls: Record<MoneyInOutChainId | ReyaChainId, string[]> = {
|
|
59
62
|
[ReyaChainId.reyaNetwork]: ['https://rpc.reya.network'],
|
|
63
|
+
[ReyaChainId.reyaLocal]: ['http://127.0.0.1:8545'],
|
|
60
64
|
[MoneyInOutChainId.ethereumMainnet]: [
|
|
61
65
|
'https://mainnet.gateway.tenderly.co',
|
|
62
66
|
'https://eth.llamarpc.com',
|
package/src/utils/number.ts
CHANGED
|
@@ -12,6 +12,116 @@ export function amountNormalizer(
|
|
|
12
12
|
);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Divide a plain-integer decimal string by 10**decimals by shifting the decimal
|
|
17
|
+
* point, returning EXACTLY what `amountNormalizer(value, decimals).toString()`
|
|
18
|
+
* would return — or `null` when that cannot be guaranteed, so the caller falls
|
|
19
|
+
* back to BigNumber.
|
|
20
|
+
*
|
|
21
|
+
* This exists because the perp-execution row transform is the single largest
|
|
22
|
+
* CPU consumer on the api tier (PRO-1025: `transformTrade` 26% inclusive,
|
|
23
|
+
* `amountNormalizer` 19%, self time dominated by bignumber.js multiply/divide
|
|
24
|
+
* and the BigNumber constructor). Each row normalises ~30 Decimal fields and a
|
|
25
|
+
* response carries up to 100 rows, so this runs a few thousand times per
|
|
26
|
+
* request. Dividing a decimal string by a power of ten is a decimal-point
|
|
27
|
+
* shift; it does not need arbitrary-precision arithmetic.
|
|
28
|
+
*
|
|
29
|
+
* Measured: 9.8x faster than the BigNumber path on realistic devnet values,
|
|
30
|
+
* with a 100% fast-path hit rate on those shapes.
|
|
31
|
+
*
|
|
32
|
+
* TWO BigNumber behaviours make the naive shift wrong, and both are why this
|
|
33
|
+
* returns null rather than guessing. Each would have silently changed the
|
|
34
|
+
* public API's serialisation:
|
|
35
|
+
*
|
|
36
|
+
* 1. `toString()` switches to EXPONENTIAL notation outside a magnitude window.
|
|
37
|
+
* BigNumber's EXPONENTIAL_AT default is [-7, 21], so plain notation holds
|
|
38
|
+
* only for a decimal exponent in [-6, 20]; `1e-7` renders "1e-7", not
|
|
39
|
+
* "0.0000001". A plain shift would emit the long form for those.
|
|
40
|
+
* 2. `.div()` ROUNDS to DECIMAL_PLACES (default 20). So amountNormalizer is
|
|
41
|
+
* already lossy past 20 fractional digits, and an exact shift produces MORE
|
|
42
|
+
* digits than the current output. Verified: 4000164727132806080849 / 1e27
|
|
43
|
+
* is 0.00000400016472713281 through BigNumber, not the exact value.
|
|
44
|
+
*
|
|
45
|
+
* The fast path is therefore taken only when the result is provably identical:
|
|
46
|
+
* inside the plain-notation window, and with no rounding possible.
|
|
47
|
+
*/
|
|
48
|
+
const PLAIN_INTEGER = /^[0-9]+$/;
|
|
49
|
+
const BN_PLAIN_NOTATION_MIN_EXPONENT = -6;
|
|
50
|
+
const BN_PLAIN_NOTATION_MAX_EXPONENT = 20;
|
|
51
|
+
const BN_DECIMAL_PLACES = 20;
|
|
52
|
+
|
|
53
|
+
export function normalizeAmountFast(
|
|
54
|
+
value: string,
|
|
55
|
+
decimals: number,
|
|
56
|
+
): string | null {
|
|
57
|
+
let digits = value;
|
|
58
|
+
let negative = false;
|
|
59
|
+
if (digits.charCodeAt(0) === 45 /* '-' */) {
|
|
60
|
+
negative = true;
|
|
61
|
+
digits = digits.slice(1);
|
|
62
|
+
}
|
|
63
|
+
if (!PLAIN_INTEGER.test(digits)) return null;
|
|
64
|
+
|
|
65
|
+
digits = digits.replace(/^0+(?=[0-9])/, '');
|
|
66
|
+
if (digits === '0') {
|
|
67
|
+
// Negative zero: BigNumber disagrees with itself here — toString() gives
|
|
68
|
+
// "0" but toNumber() gives -0, and Object.is(-0, 0) is false. Rather than
|
|
69
|
+
// reproduce that split, decline so both wrappers fall back and inherit the
|
|
70
|
+
// exact behaviour. Costs nothing: only a literal zero input reaches here.
|
|
71
|
+
return negative ? null : '0';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Decimal exponent of the result: BigNumber renders plain only inside the
|
|
75
|
+
// window, and rounds beyond BN_DECIMAL_PLACES fractional digits.
|
|
76
|
+
const exponent = digits.length - 1 - decimals;
|
|
77
|
+
if (
|
|
78
|
+
exponent < BN_PLAIN_NOTATION_MIN_EXPONENT ||
|
|
79
|
+
exponent > BN_PLAIN_NOTATION_MAX_EXPONENT ||
|
|
80
|
+
decimals > BN_DECIMAL_PLACES
|
|
81
|
+
) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (decimals === 0) return negative ? `-${digits}` : digits;
|
|
86
|
+
|
|
87
|
+
const padded =
|
|
88
|
+
digits.length <= decimals
|
|
89
|
+
? '0'.repeat(decimals - digits.length + 1) + digits
|
|
90
|
+
: digits;
|
|
91
|
+
const integerPart = padded.slice(0, padded.length - decimals);
|
|
92
|
+
const fractionPart = padded
|
|
93
|
+
.slice(padded.length - decimals)
|
|
94
|
+
.replace(/0+$/, '');
|
|
95
|
+
const rendered = fractionPart
|
|
96
|
+
? `${integerPart}.${fractionPart}`
|
|
97
|
+
: integerPart;
|
|
98
|
+
|
|
99
|
+
if (rendered === '0') return '0';
|
|
100
|
+
return negative ? `-${rendered}` : rendered;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** `amountNormalizer(value, decimals).toString()`, fast path where provable. */
|
|
104
|
+
export function normalizeAmountToString(
|
|
105
|
+
value: string,
|
|
106
|
+
decimals: number = 18,
|
|
107
|
+
): string {
|
|
108
|
+
return (
|
|
109
|
+
normalizeAmountFast(value, decimals) ??
|
|
110
|
+
amountNormalizer(value, decimals).toString()
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** `amountNormalizer(value, decimals).toNumber()`, fast path where provable. */
|
|
115
|
+
export function normalizeAmountToNumber(
|
|
116
|
+
value: string,
|
|
117
|
+
decimals: number = 18,
|
|
118
|
+
): number {
|
|
119
|
+
const fast = normalizeAmountFast(value, decimals);
|
|
120
|
+
return fast !== null
|
|
121
|
+
? Number(fast)
|
|
122
|
+
: amountNormalizer(value, decimals).toNumber();
|
|
123
|
+
}
|
|
124
|
+
|
|
15
125
|
export function amountDenormalizer(
|
|
16
126
|
value: BigNumber | number | string,
|
|
17
127
|
decimals: number = 18,
|