@reyaxyz/common 0.332.0 → 0.333.2

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.
@@ -63,31 +63,136 @@ export const calculateMaxExposure = ({
63
63
  riskFactor: number;
64
64
  }) => poolBalance / (poolIMR * Math.sqrt(riskFactor));
65
65
 
66
+ /**
67
+ * Splits a trade exposure into rebalancing and unbalancing portions relative to pool net exposure.
68
+ * Rebalancing reduces pool's absolute exposure; unbalancing increases it.
69
+ */
70
+ export const splitTradeExposure = (
71
+ tradeExposure: number,
72
+ poolNetExposure: number,
73
+ ): { rebalancing: number; unbalancing: number } => {
74
+ if (tradeExposure === 0) {
75
+ return { rebalancing: 0, unbalancing: 0 };
76
+ }
77
+
78
+ // If pool has no exposure, entire trade is unbalancing
79
+ if (poolNetExposure === 0) {
80
+ return { rebalancing: 0, unbalancing: tradeExposure };
81
+ }
82
+
83
+ // Opposite sign means trade increases pool exposure → pure unbalancing
84
+ const sameSign =
85
+ (poolNetExposure > 0 && tradeExposure > 0) ||
86
+ (poolNetExposure < 0 && tradeExposure < 0);
87
+ if (!sameSign) {
88
+ return { rebalancing: 0, unbalancing: tradeExposure };
89
+ }
90
+
91
+ // Same sign: trade reduces pool exposure (pool takes opposite side)
92
+ const absPool = Math.abs(poolNetExposure);
93
+ const absTrade = Math.abs(tradeExposure);
94
+
95
+ if (absTrade <= absPool) {
96
+ // Pure rebalancing — trade doesn't cross zero
97
+ return { rebalancing: tradeExposure, unbalancing: 0 };
98
+ }
99
+
100
+ // Mixed: rebalance up to pool exposure, then unbalance the rest
101
+ const rebalancing = poolNetExposure;
102
+ const unbalancing = tradeExposure - rebalancing;
103
+ return { rebalancing, unbalancing };
104
+ };
105
+
106
+ /**
107
+ * Computes the new log price multiplier (logF) after a trade, handling both
108
+ * rebalancing and unbalancing portions.
109
+ *
110
+ * Mirrors on-chain logic in PriceMultiplier.sol:computeNewLogPriceMultiplier.
111
+ */
112
+ export const computeNewLogF = ({
113
+ currentLogF,
114
+ tradeExposure,
115
+ poolNetExposure,
116
+ depthFactor,
117
+ maxExposure,
118
+ }: {
119
+ currentLogF: number;
120
+ tradeExposure: number;
121
+ poolNetExposure: number;
122
+ depthFactor: number;
123
+ maxExposure: number;
124
+ }): number => {
125
+ if (tradeExposure === 0) return currentLogF;
126
+
127
+ const { rebalancing, unbalancing } = splitTradeExposure(
128
+ tradeExposure,
129
+ poolNetExposure,
130
+ );
131
+
132
+ // Case 1: Has unbalancing portion (includes mixed trades)
133
+ if (unbalancing !== 0) {
134
+ // If also rebalancing, it brings logF to 0 by definition (trade crosses zero)
135
+ const startLogF = rebalancing === 0 ? currentLogF : 0;
136
+ return startLogF + unbalancing / (depthFactor * maxExposure);
137
+ }
138
+
139
+ // Case 2: Only rebalancing
140
+ // Both rebalancing and poolNetExposure have the same sign, so ratio is in [0, 1]
141
+ if (rebalancing !== 0 && poolNetExposure !== 0) {
142
+ const ratio = rebalancing / poolNetExposure;
143
+ return currentLogF * (1 - ratio);
144
+ }
145
+
146
+ return currentLogF;
147
+ };
148
+
149
+ /**
150
+ * Estimates the execution price for a trade using the exponential logPriceMultiplier formula.
151
+ *
152
+ * Mirrors on-chain logic:
153
+ * 1. computeNewLogPriceMultiplier (PriceMultiplier.sol)
154
+ * 2. computeAmmPrice: ammPrice = oraclePrice * exp(logF)
155
+ * 3. computeSignedSpread: executionPrice = ammPrice * (1 + signedSpread)
156
+ *
157
+ * @audit spreadDiscount not applied — known limitation, to be addressed later
158
+ * @audit priceSpacing rounding not applied
159
+ */
66
160
  export const calculateEstimatedExecutionPrice = ({
67
- poolPrice,
68
161
  oraclePrice,
69
- tradeBaseSize,
70
- poolMaxExposure,
71
- marketDepthFactor,
72
- marketSpread,
162
+ currentLogF,
163
+ tradeExposure,
164
+ poolNetExposure,
165
+ depthFactor,
166
+ maxExposure,
167
+ priceSpread,
73
168
  }: {
74
- poolPrice: number;
75
169
  oraclePrice: number;
76
- tradeBaseSize: number;
77
- poolMaxExposure: number;
78
- marketDepthFactor: number;
79
- marketSpread: number;
170
+ currentLogF: number;
171
+ tradeExposure: number;
172
+ poolNetExposure: number;
173
+ depthFactor: number;
174
+ maxExposure: number;
175
+ priceSpread: number;
80
176
  }): number => {
81
- if (tradeBaseSize === 0) {
82
- return poolPrice;
177
+ if (tradeExposure === 0) {
178
+ return oraclePrice * Math.exp(currentLogF);
83
179
  }
84
180
 
85
- const poolDeviation =
86
- (tradeBaseSize * oraclePrice) / (marketDepthFactor * poolMaxExposure);
87
- const appliedSpread = tradeBaseSize > 0 ? marketSpread : -marketSpread;
88
- const estExecutionPrice = poolPrice * (1 + poolDeviation + appliedSpread);
181
+ // 1. Compute new logF after trade impact
182
+ const newLogF = computeNewLogF({
183
+ currentLogF,
184
+ tradeExposure,
185
+ poolNetExposure,
186
+ depthFactor,
187
+ maxExposure,
188
+ });
189
+
190
+ // 2. Compute AMM price: oraclePrice * exp(newLogF)
191
+ const ammPrice = oraclePrice * Math.exp(newLogF);
89
192
 
90
- return Math.max(0, estExecutionPrice);
193
+ // 3. Apply signed spread
194
+ const signedSpread = tradeExposure > 0 ? priceSpread : -priceSpread;
195
+ return Math.max(0, ammPrice * (1 + signedSpread));
91
196
  };
92
197
 
93
198
  export const calculateTradeFee = ({
@@ -122,7 +122,9 @@ export const TOKEN_INFO: Record<ReyaChainId | MoneyInOutChainId, TokenInfo[]> =
122
122
  address: '0xa6cf523f856f4a0aab78848e251c1b042e6406d5',
123
123
  decimals: 8,
124
124
  isRUSDUnderlying: false,
125
- ...defaultTokenThresholds,
125
+ minDepositAmount: 0.0001,
126
+ minWithdrawAmount: 0.0001,
127
+ minTransferAmount: 0.0001,
126
128
  },
127
129
  ],
128
130
  [MoneyInOutChainId.ethereumMainnet]: [