@n1xyz/wallet-widget 0.0.22 → 0.0.25
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/dist/Modal/NordFlow/context/DepositContext.js +7 -3
- package/dist/Modal/NordFlow/context/DepositContext.js.map +1 -1
- package/dist/Modal/NordFlow/context/WalletConnectContext.js +4 -3
- package/dist/Modal/NordFlow/context/WalletConnectContext.js.map +1 -1
- package/dist/Modal/NordFlow/screens/04-AmountInputScreen.js +19 -14
- package/dist/Modal/NordFlow/screens/04-AmountInputScreen.js.map +1 -1
- package/dist/Modal/Sidebar/N1Sidebar.js +6 -2
- package/dist/Modal/Sidebar/N1Sidebar.js.map +1 -1
- package/dist/Modal/Sidebar/NordTradingView/MarketOverview/MarketOverview.d.ts +7 -2
- package/dist/Modal/Sidebar/NordTradingView/MarketOverview/MarketOverview.js +11 -8
- package/dist/Modal/Sidebar/NordTradingView/MarketOverview/MarketOverview.js.map +1 -1
- package/dist/Modal/Sidebar/NordTradingView/MarketOverview/MarketSelector.d.ts +2 -2
- package/dist/Modal/Sidebar/NordTradingView/MarketOverview/MarketSelector.js +3 -3
- package/dist/Modal/Sidebar/NordTradingView/MarketOverview/MarketSelector.js.map +1 -1
- package/dist/Modal/Sidebar/NordTradingView/MarketOverview/MarketStats.d.ts +4 -0
- package/dist/Modal/Sidebar/NordTradingView/MarketOverview/MarketStats.js +4 -0
- package/dist/Modal/Sidebar/NordTradingView/MarketOverview/MarketStats.js.map +1 -1
- package/dist/Modal/Sidebar/NordTradingView/NordTradingView.d.ts +1 -1
- package/dist/Modal/Sidebar/NordTradingView/NordTradingView.js +129 -41
- package/dist/Modal/Sidebar/NordTradingView/NordTradingView.js.map +1 -1
- package/dist/Modal/Sidebar/NordTradingView/OrderBook/OrderBook.d.ts +0 -5
- package/dist/Modal/Sidebar/NordTradingView/OrderBook/OrderBook.js +234 -195
- package/dist/Modal/Sidebar/NordTradingView/OrderBook/OrderBook.js.map +1 -1
- package/dist/Modal/Sidebar/NordTradingView/TradeForm/TradeForm.d.ts +6 -1
- package/dist/Modal/Sidebar/NordTradingView/TradeForm/TradeForm.js +11 -7
- package/dist/Modal/Sidebar/NordTradingView/TradeForm/TradeForm.js.map +1 -1
- package/dist/Modal/Sidebar/NordTradingView/UserBalances/UserBalances.d.ts +4 -0
- package/dist/Modal/Sidebar/NordTradingView/UserBalances/UserBalances.js +4 -0
- package/dist/Modal/Sidebar/NordTradingView/UserBalances/UserBalances.js.map +1 -1
- package/dist/Modal/Sidebar/NordTradingView/UserPositions/UserPositions.d.ts +0 -1
- package/dist/Modal/Sidebar/NordTradingView/UserPositions/UserPositions.js +271 -189
- package/dist/Modal/Sidebar/NordTradingView/UserPositions/UserPositions.js.map +1 -1
- package/dist/Provider/hooks/useNordUserInitialization.js +7 -4
- package/dist/Provider/hooks/useNordUserInitialization.js.map +1 -1
- package/dist/embedded-main-css.d.ts +1 -1
- package/dist/embedded-main-css.js +1 -1
- package/dist/embedded-main-css.js.map +1 -1
- package/dist/main.css +1 -1
- package/package.json +2 -3
|
@@ -1,196 +1,278 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
12
|
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
-
function step(op) {
|
|
15
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
17
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
-
switch (op[0]) {
|
|
20
|
-
case 0: case 1: t = op; break;
|
|
21
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
-
default:
|
|
25
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
-
if (t[2]) _.ops.pop();
|
|
30
|
-
_.trys.pop(); continue;
|
|
31
|
-
}
|
|
32
|
-
op = body.call(thisArg, _);
|
|
33
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
"use strict";
|
|
2
|
+
// DEPRECATED: This entire component has been deprecated due to errors
|
|
3
|
+
// All code below has been commented out to prevent issues
|
|
4
|
+
/*
|
|
5
|
+
|
|
38
6
|
import { useState, useEffect } from 'react';
|
|
39
7
|
import { useN1WalletContext } from '../../../../Provider/hooks';
|
|
40
|
-
import { RefreshCw } from 'lucide-react';
|
|
8
|
+
import { RefreshCw, X } from 'lucide-react';
|
|
9
|
+
|
|
10
|
+
interface Position {
|
|
11
|
+
marketId: number;
|
|
12
|
+
symbol: string;
|
|
13
|
+
size: number;
|
|
14
|
+
entryPrice: number;
|
|
15
|
+
markPrice: number;
|
|
16
|
+
pnl: number;
|
|
17
|
+
pnlPercentage: number;
|
|
18
|
+
liquidationPrice?: number;
|
|
19
|
+
accountId: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Interface for market stats based on the Nord API
|
|
23
|
+
interface MarketStats {
|
|
24
|
+
market_id: number;
|
|
25
|
+
index_price: [number, number];
|
|
26
|
+
volume_24h: number;
|
|
27
|
+
high_24h: number;
|
|
28
|
+
low_24h: number;
|
|
29
|
+
perp_stats?: {
|
|
30
|
+
mark_price?: number;
|
|
31
|
+
funding_rate?: number;
|
|
32
|
+
next_funding_time?: Date;
|
|
33
|
+
open_interest?: number;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
41
37
|
export default function UserPositions() {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
for (_b = 0, accountPositions_1 = accountPositions; _b < accountPositions_1.length; _b++) {
|
|
120
|
-
position = accountPositions_1[_b];
|
|
121
|
-
_loop_1(position);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
setPositions(realPositions);
|
|
127
|
-
setLoading(false); // Explicitly set loading to false on success
|
|
128
|
-
return [3 /*break*/, 8];
|
|
129
|
-
case 7:
|
|
130
|
-
err_1 = _d.sent();
|
|
131
|
-
console.error('Error fetching positions:', err_1);
|
|
132
|
-
setError('Failed to load positions');
|
|
133
|
-
setLoading(false); // Ensure loading is set to false on error
|
|
134
|
-
return [3 /*break*/, 8];
|
|
135
|
-
case 8: return [2 /*return*/];
|
|
38
|
+
const { nord, nordUser } = useN1WalletContext();
|
|
39
|
+
const [positions, setPositions] = useState<Position[]>([]);
|
|
40
|
+
const [loading, setLoading] = useState(false);
|
|
41
|
+
const [error, setError] = useState<string | null>(null);
|
|
42
|
+
const [marketStats, setMarketStats] = useState<MarketStats[]>([]);
|
|
43
|
+
|
|
44
|
+
// Fetch user positions
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (!nordUser || !nord) return;
|
|
47
|
+
|
|
48
|
+
const fetchPositions = async () => {
|
|
49
|
+
setLoading(true);
|
|
50
|
+
setError(null);
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
// Refresh user info to get latest positions
|
|
54
|
+
await nordUser.fetchInfo();
|
|
55
|
+
|
|
56
|
+
// Get market stats for mark prices
|
|
57
|
+
try {
|
|
58
|
+
const stats = await nord.marketsStats();
|
|
59
|
+
setMarketStats(stats.markets);
|
|
60
|
+
} catch (statsErr) {
|
|
61
|
+
console.error('Error fetching market stats:', statsErr);
|
|
62
|
+
// Don't set loading to false here, as we still want to try to get positions
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Extract real positions from nordUser
|
|
66
|
+
const realPositions: Position[] = [];
|
|
67
|
+
|
|
68
|
+
// Loop through all accounts
|
|
69
|
+
if (nordUser.accountIds) {
|
|
70
|
+
for (const accountId of nordUser.accountIds) {
|
|
71
|
+
// Check if this account has positions
|
|
72
|
+
const accountPositions = nordUser.positions[accountId];
|
|
73
|
+
|
|
74
|
+
if (accountPositions && accountPositions.length > 0) {
|
|
75
|
+
// Process each position
|
|
76
|
+
for (const position of accountPositions) {
|
|
77
|
+
// Only include positions with perp data and non-zero size
|
|
78
|
+
if (position.perp && Math.abs(position.perp.baseSize) > 0) {
|
|
79
|
+
// Find market info to get symbol
|
|
80
|
+
const market = nord.markets.find(
|
|
81
|
+
(m) => m.marketId === position.marketId
|
|
82
|
+
);
|
|
83
|
+
const symbol = market
|
|
84
|
+
? market.symbol
|
|
85
|
+
: `Market-${position.marketId}`;
|
|
86
|
+
|
|
87
|
+
// Calculate PnL
|
|
88
|
+
const totalPnl =
|
|
89
|
+
position.perp.sizePricePnl +
|
|
90
|
+
position.perp.fundingPaymentPnl;
|
|
91
|
+
const entryValue =
|
|
92
|
+
Math.abs(position.perp.baseSize) * position.perp.price;
|
|
93
|
+
const pnlPercentage =
|
|
94
|
+
entryValue > 0 ? (totalPnl / entryValue) * 100 : 0;
|
|
95
|
+
|
|
96
|
+
// Get mark price from market stats or use entry price as fallback
|
|
97
|
+
const stats = marketStats.find(
|
|
98
|
+
(m) => m.market_id === position.marketId
|
|
99
|
+
);
|
|
100
|
+
const markPrice =
|
|
101
|
+
stats?.perp_stats?.mark_price || position.perp.price;
|
|
102
|
+
|
|
103
|
+
realPositions.push({
|
|
104
|
+
marketId: position.marketId,
|
|
105
|
+
symbol: symbol,
|
|
106
|
+
size: position.perp.isLong
|
|
107
|
+
? position.perp.baseSize
|
|
108
|
+
: -position.perp.baseSize,
|
|
109
|
+
entryPrice: position.perp.price,
|
|
110
|
+
markPrice: markPrice,
|
|
111
|
+
pnl: totalPnl,
|
|
112
|
+
pnlPercentage: pnlPercentage,
|
|
113
|
+
accountId: accountId,
|
|
114
|
+
});
|
|
136
115
|
}
|
|
137
|
-
|
|
138
|
-
}); };
|
|
139
|
-
fetchPositions();
|
|
140
|
-
// Set up polling for position updates
|
|
141
|
-
var intervalId = setInterval(fetchPositions, 10000); // Update every 10 seconds
|
|
142
|
-
return function () { return clearInterval(intervalId); };
|
|
143
|
-
}, [nordUser, nord]); // Remove marketStats from dependency array to prevent infinite loops
|
|
144
|
-
// Handle position close
|
|
145
|
-
var handleClosePosition = function (marketId, accountId) { return __awaiter(_this, void 0, void 0, function () {
|
|
146
|
-
var position, err_2;
|
|
147
|
-
return __generator(this, function (_a) {
|
|
148
|
-
switch (_a.label) {
|
|
149
|
-
case 0:
|
|
150
|
-
if (!nordUser)
|
|
151
|
-
return [2 /*return*/];
|
|
152
|
-
_a.label = 1;
|
|
153
|
-
case 1:
|
|
154
|
-
_a.trys.push([1, 4, , 5]);
|
|
155
|
-
position = positions.find(function (pos) { return pos.marketId === marketId && pos.accountId === accountId; });
|
|
156
|
-
if (!position) {
|
|
157
|
-
throw new Error('Position not found');
|
|
158
|
-
}
|
|
159
|
-
// Place an order to close the position (opposite side, same size)
|
|
160
|
-
return [4 /*yield*/, nordUser.placeOrder({
|
|
161
|
-
marketId: marketId,
|
|
162
|
-
side: position.size > 0 ? 'ask' : 'bid', // Opposite side to close
|
|
163
|
-
fillMode: 'market', // Market order to ensure it gets filled
|
|
164
|
-
isReduceOnly: true, // Reduce-only to ensure it only closes the position
|
|
165
|
-
size: Math.abs(position.size), // Absolute size value
|
|
166
|
-
accountId: accountId,
|
|
167
|
-
})];
|
|
168
|
-
case 2:
|
|
169
|
-
// Place an order to close the position (opposite side, same size)
|
|
170
|
-
_a.sent();
|
|
171
|
-
// Refresh positions after closing
|
|
172
|
-
return [4 /*yield*/, nordUser.fetchInfo()];
|
|
173
|
-
case 3:
|
|
174
|
-
// Refresh positions after closing
|
|
175
|
-
_a.sent();
|
|
176
|
-
// Remove the position from the local state
|
|
177
|
-
setPositions(positions.filter(function (pos) { return !(pos.marketId === marketId && pos.accountId === accountId); }));
|
|
178
|
-
return [3 /*break*/, 5];
|
|
179
|
-
case 4:
|
|
180
|
-
err_2 = _a.sent();
|
|
181
|
-
console.error('Error closing position:', err_2);
|
|
182
|
-
setError('Failed to close position');
|
|
183
|
-
return [3 /*break*/, 5];
|
|
184
|
-
case 5: return [2 /*return*/];
|
|
116
|
+
}
|
|
185
117
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
setPositions(realPositions);
|
|
122
|
+
setLoading(false); // Explicitly set loading to false on success
|
|
123
|
+
} catch (err) {
|
|
124
|
+
console.error('Error fetching positions:', err);
|
|
125
|
+
setError('Failed to load positions');
|
|
126
|
+
setLoading(false); // Ensure loading is set to false on error
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
fetchPositions();
|
|
131
|
+
|
|
132
|
+
// Set up polling for position updates
|
|
133
|
+
const intervalId = setInterval(fetchPositions, 10000); // Update every 10 seconds
|
|
134
|
+
|
|
135
|
+
return () => clearInterval(intervalId);
|
|
136
|
+
}, [nordUser, nord]); // Remove marketStats from dependency array to prevent infinite loops
|
|
137
|
+
|
|
138
|
+
// Handle position close
|
|
139
|
+
const handleClosePosition = async (marketId: number, accountId: number) => {
|
|
140
|
+
if (!nordUser) return;
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
// Find the position to close
|
|
144
|
+
const position = positions.find(
|
|
145
|
+
(pos) => pos.marketId === marketId && pos.accountId === accountId
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
if (!position) {
|
|
149
|
+
throw new Error('Position not found');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Place an order to close the position (opposite side, same size)
|
|
153
|
+
await nordUser.placeOrder({
|
|
154
|
+
marketId: marketId,
|
|
155
|
+
side: position.size > 0 ? 'ask' : 'bid', // Opposite side to close
|
|
156
|
+
fillMode: 'market', // Market order to ensure it gets filled
|
|
157
|
+
isReduceOnly: true, // Reduce-only to ensure it only closes the position
|
|
158
|
+
size: Math.abs(position.size), // Absolute size value
|
|
159
|
+
accountId: accountId,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Refresh positions after closing
|
|
163
|
+
await nordUser.fetchInfo();
|
|
164
|
+
|
|
165
|
+
// Remove the position from the local state
|
|
166
|
+
setPositions(
|
|
167
|
+
positions.filter(
|
|
168
|
+
(pos) => !(pos.marketId === marketId && pos.accountId === accountId)
|
|
169
|
+
)
|
|
170
|
+
);
|
|
171
|
+
} catch (err) {
|
|
172
|
+
console.error('Error closing position:', err);
|
|
173
|
+
setError('Failed to close position');
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
return (
|
|
178
|
+
<div className="bg-white dark:bg-n1-ww-gray-950 overflow-hidden">
|
|
179
|
+
<div className="p-4">
|
|
180
|
+
<h2 className="text-lg font-semibold text-n1-ww-gray-900 dark:text-n1-ww-gray-100 mb-4">
|
|
181
|
+
Positions
|
|
182
|
+
</h2>
|
|
183
|
+
|
|
184
|
+
{loading && positions.length === 0 ? (
|
|
185
|
+
<div className="flex items-center justify-center h-32">
|
|
186
|
+
<RefreshCw size={20} className="text-n1-ww-gray-400 n1-ww-animate-spin" />
|
|
187
|
+
</div>
|
|
188
|
+
) : error ? (
|
|
189
|
+
<div className="p-3 bg-red-50 dark:bg-red-900/20 rounded-lg text-red-600 dark:text-red-400 text-sm">
|
|
190
|
+
{error}
|
|
191
|
+
</div>
|
|
192
|
+
) : positions.length === 0 ? (
|
|
193
|
+
<div className="p-3 bg-n1-ww-gray-50 dark:bg-n1-ww-gray-950 rounded-lg text-n1-ww-gray-500 dark:text-n1-ww-gray-400 text-sm text-center">
|
|
194
|
+
No open positions
|
|
195
|
+
</div>
|
|
196
|
+
) : (
|
|
197
|
+
<div className="space-y-3">
|
|
198
|
+
{positions.map((position, index) => (
|
|
199
|
+
<div
|
|
200
|
+
key={index}
|
|
201
|
+
className="p-3 bg-n1-ww-gray-50 dark:bg-n1-ww-gray-950 rounded-lg"
|
|
202
|
+
>
|
|
203
|
+
<div className="flex items-center justify-between mb-2">
|
|
204
|
+
<div className="flex items-center">
|
|
205
|
+
<span className="font-medium text-n1-ww-gray-900 dark:text-n1-ww-gray-100 mr-2">
|
|
206
|
+
{position.symbol}
|
|
207
|
+
</span>
|
|
208
|
+
<span
|
|
209
|
+
className={`text-xs px-2 py-0.5 rounded ${
|
|
210
|
+
position.size > 0
|
|
211
|
+
? 'bg-green-100 dark:bg-green-900/20 text-green-600 dark:text-green-400'
|
|
212
|
+
: 'bg-red-100 dark:bg-red-900/20 text-red-600 dark:text-red-400'
|
|
213
|
+
}`}
|
|
214
|
+
>
|
|
215
|
+
{position.size > 0 ? 'Long' : 'Short'}{' '}
|
|
216
|
+
{Math.abs(position.size)}
|
|
217
|
+
</span>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
<div className="grid grid-cols-2 gap-2 text-sm">
|
|
222
|
+
<div>
|
|
223
|
+
<div className="text-n1-ww-gray-500 dark:text-n1-ww-gray-400">
|
|
224
|
+
Entry Price
|
|
225
|
+
</div>
|
|
226
|
+
<div className="font-medium text-n1-ww-gray-900 dark:text-n1-ww-gray-100 font-mono">
|
|
227
|
+
{position.entryPrice.toFixed(2)}
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
<div>
|
|
231
|
+
<div className="text-n1-ww-gray-500 dark:text-n1-ww-gray-400">
|
|
232
|
+
Mark Price
|
|
233
|
+
</div>
|
|
234
|
+
<div className="font-medium text-n1-ww-gray-900 dark:text-n1-ww-gray-100 font-mono">
|
|
235
|
+
{position.markPrice.toFixed(2)}
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
<div>
|
|
239
|
+
<div className="text-n1-ww-gray-500 dark:text-n1-ww-gray-400">PnL</div>
|
|
240
|
+
<div
|
|
241
|
+
className={`font-medium font-mono ${
|
|
242
|
+
position.pnl >= 0
|
|
243
|
+
? 'text-green-600 dark:text-green-400'
|
|
244
|
+
: 'text-red-600 dark:text-red-400'
|
|
245
|
+
}`}
|
|
246
|
+
>
|
|
247
|
+
{position.pnl >= 0 ? '+' : ''}
|
|
248
|
+
{position.pnl.toFixed(2)} (
|
|
249
|
+
{position.pnlPercentage.toFixed(2)}%)
|
|
250
|
+
</div>
|
|
251
|
+
</div>
|
|
252
|
+
<div>
|
|
253
|
+
<div className="text-n1-ww-gray-500 dark:text-n1-ww-gray-400">
|
|
254
|
+
Liquidation
|
|
255
|
+
</div>
|
|
256
|
+
<div className="font-medium text-n1-ww-gray-900 dark:text-n1-ww-gray-100 font-mono">
|
|
257
|
+
{position.liquidationPrice
|
|
258
|
+
? position.liquidationPrice.toFixed(2)
|
|
259
|
+
: 'N/A'}
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
))}
|
|
265
|
+
|
|
266
|
+
{loading && positions.length > 0 && (
|
|
267
|
+
<div className="flex justify-center mt-2">
|
|
268
|
+
<RefreshCw size={16} className="text-n1-ww-gray-400 n1-ww-animate-spin" />
|
|
269
|
+
</div>
|
|
270
|
+
)}
|
|
271
|
+
</div>
|
|
272
|
+
)}
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
);
|
|
195
276
|
}
|
|
277
|
+
*/
|
|
196
278
|
//# sourceMappingURL=UserPositions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserPositions.js","sourceRoot":"","sources":["../../../../../src/Modal/Sidebar/NordTradingView/UserPositions/UserPositions.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState, useEffect } from 'react';\nimport { useN1WalletContext } from '../../../../Provider/hooks';\nimport { RefreshCw, X } from 'lucide-react';\n\ninterface Position {\n marketId: number;\n symbol: string;\n size: number;\n entryPrice: number;\n markPrice: number;\n pnl: number;\n pnlPercentage: number;\n liquidationPrice?: number;\n accountId: number;\n}\n\n// Interface for market stats based on the Nord API\ninterface MarketStats {\n market_id: number;\n index_price: [number, number];\n volume_24h: number;\n high_24h: number;\n low_24h: number;\n perp_stats?: {\n mark_price?: number;\n funding_rate?: number;\n next_funding_time?: Date;\n open_interest?: number;\n };\n}\n\nexport default function UserPositions() {\n const { nord, nordUser } = useN1WalletContext();\n const [positions, setPositions] = useState<Position[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [marketStats, setMarketStats] = useState<MarketStats[]>([]);\n\n // Fetch user positions\n useEffect(() => {\n if (!nordUser || !nord) return;\n\n const fetchPositions = async () => {\n setLoading(true);\n setError(null);\n\n try {\n // Refresh user info to get latest positions\n await nordUser.fetchInfo();\n\n // Get market stats for mark prices\n try {\n const stats = await nord.marketsStats();\n setMarketStats(stats.markets);\n } catch (statsErr) {\n console.error('Error fetching market stats:', statsErr);\n // Don't set loading to false here, as we still want to try to get positions\n }\n\n // Extract real positions from nordUser\n const realPositions: Position[] = [];\n\n // Loop through all accounts\n if (nordUser.accountIds) {\n for (const accountId of nordUser.accountIds) {\n // Check if this account has positions\n const accountPositions = nordUser.positions[accountId];\n\n if (accountPositions && accountPositions.length > 0) {\n // Process each position\n for (const position of accountPositions) {\n // Only include positions with perp data and non-zero size\n if (position.perp && Math.abs(position.perp.baseSize) > 0) {\n // Find market info to get symbol\n const market = nord.markets.find(\n (m) => m.marketId === position.marketId\n );\n const symbol = market\n ? market.symbol\n : `Market-${position.marketId}`;\n\n // Calculate PnL\n const totalPnl =\n position.perp.sizePricePnl +\n position.perp.fundingPaymentPnl;\n const entryValue =\n Math.abs(position.perp.baseSize) * position.perp.price;\n const pnlPercentage =\n entryValue > 0 ? (totalPnl / entryValue) * 100 : 0;\n\n // Get mark price from market stats or use entry price as fallback\n const stats = marketStats.find(\n (m) => m.market_id === position.marketId\n );\n const markPrice =\n stats?.perp_stats?.mark_price || position.perp.price;\n\n realPositions.push({\n marketId: position.marketId,\n symbol: symbol,\n size: position.perp.isLong\n ? position.perp.baseSize\n : -position.perp.baseSize,\n entryPrice: position.perp.price,\n markPrice: markPrice,\n pnl: totalPnl,\n pnlPercentage: pnlPercentage,\n accountId: accountId,\n });\n }\n }\n }\n }\n }\n\n setPositions(realPositions);\n setLoading(false); // Explicitly set loading to false on success\n } catch (err) {\n console.error('Error fetching positions:', err);\n setError('Failed to load positions');\n setLoading(false); // Ensure loading is set to false on error\n }\n };\n\n fetchPositions();\n\n // Set up polling for position updates\n const intervalId = setInterval(fetchPositions, 10000); // Update every 10 seconds\n\n return () => clearInterval(intervalId);\n }, [nordUser, nord]); // Remove marketStats from dependency array to prevent infinite loops\n\n // Handle position close\n const handleClosePosition = async (marketId: number, accountId: number) => {\n if (!nordUser) return;\n\n try {\n // Find the position to close\n const position = positions.find(\n (pos) => pos.marketId === marketId && pos.accountId === accountId\n );\n\n if (!position) {\n throw new Error('Position not found');\n }\n\n // Place an order to close the position (opposite side, same size)\n await nordUser.placeOrder({\n marketId: marketId,\n side: position.size > 0 ? 'ask' : 'bid', // Opposite side to close\n fillMode: 'market', // Market order to ensure it gets filled\n isReduceOnly: true, // Reduce-only to ensure it only closes the position\n size: Math.abs(position.size), // Absolute size value\n accountId: accountId,\n });\n\n // Refresh positions after closing\n await nordUser.fetchInfo();\n\n // Remove the position from the local state\n setPositions(\n positions.filter(\n (pos) => !(pos.marketId === marketId && pos.accountId === accountId)\n )\n );\n } catch (err) {\n console.error('Error closing position:', err);\n setError('Failed to close position');\n }\n };\n\n return (\n <div className=\"bg-white dark:bg-n1-ww-gray-950 overflow-hidden\">\n <div className=\"p-4\">\n <h2 className=\"text-lg font-semibold text-n1-ww-gray-900 dark:text-n1-ww-gray-100 mb-4\">\n Positions\n </h2>\n\n {loading && positions.length === 0 ? (\n <div className=\"flex items-center justify-center h-32\">\n <RefreshCw size={20} className=\"text-n1-ww-gray-400 n1-ww-animate-spin\" />\n </div>\n ) : error ? (\n <div className=\"p-3 bg-red-50 dark:bg-red-900/20 rounded-lg text-red-600 dark:text-red-400 text-sm\">\n {error}\n </div>\n ) : positions.length === 0 ? (\n <div className=\"p-3 bg-n1-ww-gray-50 dark:bg-n1-ww-gray-950 rounded-lg text-n1-ww-gray-500 dark:text-n1-ww-gray-400 text-sm text-center\">\n No open positions\n </div>\n ) : (\n <div className=\"space-y-3\">\n {positions.map((position, index) => (\n <div\n key={index}\n className=\"p-3 bg-n1-ww-gray-50 dark:bg-n1-ww-gray-950 rounded-lg\"\n >\n <div className=\"flex items-center justify-between mb-2\">\n <div className=\"flex items-center\">\n <span className=\"font-medium text-n1-ww-gray-900 dark:text-n1-ww-gray-100 mr-2\">\n {position.symbol}\n </span>\n <span\n className={`text-xs px-2 py-0.5 rounded ${\n position.size > 0\n ? 'bg-green-100 dark:bg-green-900/20 text-green-600 dark:text-green-400'\n : 'bg-red-100 dark:bg-red-900/20 text-red-600 dark:text-red-400'\n }`}\n >\n {position.size > 0 ? 'Long' : 'Short'}{' '}\n {Math.abs(position.size)}\n </span>\n </div>\n </div>\n\n <div className=\"grid grid-cols-2 gap-2 text-sm\">\n <div>\n <div className=\"text-n1-ww-gray-500 dark:text-n1-ww-gray-400\">\n Entry Price\n </div>\n <div className=\"font-medium text-n1-ww-gray-900 dark:text-n1-ww-gray-100 font-mono\">\n {position.entryPrice.toFixed(2)}\n </div>\n </div>\n <div>\n <div className=\"text-n1-ww-gray-500 dark:text-n1-ww-gray-400\">\n Mark Price\n </div>\n <div className=\"font-medium text-n1-ww-gray-900 dark:text-n1-ww-gray-100 font-mono\">\n {position.markPrice.toFixed(2)}\n </div>\n </div>\n <div>\n <div className=\"text-n1-ww-gray-500 dark:text-n1-ww-gray-400\">PnL</div>\n <div\n className={`font-medium font-mono ${\n position.pnl >= 0\n ? 'text-green-600 dark:text-green-400'\n : 'text-red-600 dark:text-red-400'\n }`}\n >\n {position.pnl >= 0 ? '+' : ''}\n {position.pnl.toFixed(2)} (\n {position.pnlPercentage.toFixed(2)}%)\n </div>\n </div>\n <div>\n <div className=\"text-n1-ww-gray-500 dark:text-n1-ww-gray-400\">\n Liquidation\n </div>\n <div className=\"font-medium text-n1-ww-gray-900 dark:text-n1-ww-gray-100 font-mono\">\n {position.liquidationPrice\n ? position.liquidationPrice.toFixed(2)\n : 'N/A'}\n </div>\n </div>\n </div>\n </div>\n ))}\n\n {/* Loading indicator for refreshing */}\n {loading && positions.length > 0 && (\n <div className=\"flex justify-center mt-2\">\n <RefreshCw size={16} className=\"text-n1-ww-gray-400 n1-ww-animate-spin\" />\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"UserPositions.js","sourceRoot":"","sources":["../../../../../src/Modal/Sidebar/NordTradingView/UserPositions/UserPositions.tsx"],"names":[],"mappings":";AAAA,sEAAsE;AACtE,0DAA0D;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiRE","sourcesContent":["// DEPRECATED: This entire component has been deprecated due to errors\n// All code below has been commented out to prevent issues\n\n/*\n\nimport { useState, useEffect } from 'react';\nimport { useN1WalletContext } from '../../../../Provider/hooks';\nimport { RefreshCw, X } from 'lucide-react';\n\ninterface Position {\n marketId: number;\n symbol: string;\n size: number;\n entryPrice: number;\n markPrice: number;\n pnl: number;\n pnlPercentage: number;\n liquidationPrice?: number;\n accountId: number;\n}\n\n// Interface for market stats based on the Nord API\ninterface MarketStats {\n market_id: number;\n index_price: [number, number];\n volume_24h: number;\n high_24h: number;\n low_24h: number;\n perp_stats?: {\n mark_price?: number;\n funding_rate?: number;\n next_funding_time?: Date;\n open_interest?: number;\n };\n}\n\nexport default function UserPositions() {\n const { nord, nordUser } = useN1WalletContext();\n const [positions, setPositions] = useState<Position[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [marketStats, setMarketStats] = useState<MarketStats[]>([]);\n\n // Fetch user positions\n useEffect(() => {\n if (!nordUser || !nord) return;\n\n const fetchPositions = async () => {\n setLoading(true);\n setError(null);\n\n try {\n // Refresh user info to get latest positions\n await nordUser.fetchInfo();\n\n // Get market stats for mark prices\n try {\n const stats = await nord.marketsStats();\n setMarketStats(stats.markets);\n } catch (statsErr) {\n console.error('Error fetching market stats:', statsErr);\n // Don't set loading to false here, as we still want to try to get positions\n }\n\n // Extract real positions from nordUser\n const realPositions: Position[] = [];\n\n // Loop through all accounts\n if (nordUser.accountIds) {\n for (const accountId of nordUser.accountIds) {\n // Check if this account has positions\n const accountPositions = nordUser.positions[accountId];\n\n if (accountPositions && accountPositions.length > 0) {\n // Process each position\n for (const position of accountPositions) {\n // Only include positions with perp data and non-zero size\n if (position.perp && Math.abs(position.perp.baseSize) > 0) {\n // Find market info to get symbol\n const market = nord.markets.find(\n (m) => m.marketId === position.marketId\n );\n const symbol = market\n ? market.symbol\n : `Market-${position.marketId}`;\n\n // Calculate PnL\n const totalPnl =\n position.perp.sizePricePnl +\n position.perp.fundingPaymentPnl;\n const entryValue =\n Math.abs(position.perp.baseSize) * position.perp.price;\n const pnlPercentage =\n entryValue > 0 ? (totalPnl / entryValue) * 100 : 0;\n\n // Get mark price from market stats or use entry price as fallback\n const stats = marketStats.find(\n (m) => m.market_id === position.marketId\n );\n const markPrice =\n stats?.perp_stats?.mark_price || position.perp.price;\n\n realPositions.push({\n marketId: position.marketId,\n symbol: symbol,\n size: position.perp.isLong\n ? position.perp.baseSize\n : -position.perp.baseSize,\n entryPrice: position.perp.price,\n markPrice: markPrice,\n pnl: totalPnl,\n pnlPercentage: pnlPercentage,\n accountId: accountId,\n });\n }\n }\n }\n }\n }\n\n setPositions(realPositions);\n setLoading(false); // Explicitly set loading to false on success\n } catch (err) {\n console.error('Error fetching positions:', err);\n setError('Failed to load positions');\n setLoading(false); // Ensure loading is set to false on error\n }\n };\n\n fetchPositions();\n\n // Set up polling for position updates\n const intervalId = setInterval(fetchPositions, 10000); // Update every 10 seconds\n\n return () => clearInterval(intervalId);\n }, [nordUser, nord]); // Remove marketStats from dependency array to prevent infinite loops\n\n // Handle position close\n const handleClosePosition = async (marketId: number, accountId: number) => {\n if (!nordUser) return;\n\n try {\n // Find the position to close\n const position = positions.find(\n (pos) => pos.marketId === marketId && pos.accountId === accountId\n );\n\n if (!position) {\n throw new Error('Position not found');\n }\n\n // Place an order to close the position (opposite side, same size)\n await nordUser.placeOrder({\n marketId: marketId,\n side: position.size > 0 ? 'ask' : 'bid', // Opposite side to close\n fillMode: 'market', // Market order to ensure it gets filled\n isReduceOnly: true, // Reduce-only to ensure it only closes the position\n size: Math.abs(position.size), // Absolute size value\n accountId: accountId,\n });\n\n // Refresh positions after closing\n await nordUser.fetchInfo();\n\n // Remove the position from the local state\n setPositions(\n positions.filter(\n (pos) => !(pos.marketId === marketId && pos.accountId === accountId)\n )\n );\n } catch (err) {\n console.error('Error closing position:', err);\n setError('Failed to close position');\n }\n };\n\n return (\n <div className=\"bg-white dark:bg-n1-ww-gray-950 overflow-hidden\">\n <div className=\"p-4\">\n <h2 className=\"text-lg font-semibold text-n1-ww-gray-900 dark:text-n1-ww-gray-100 mb-4\">\n Positions\n </h2>\n\n {loading && positions.length === 0 ? (\n <div className=\"flex items-center justify-center h-32\">\n <RefreshCw size={20} className=\"text-n1-ww-gray-400 n1-ww-animate-spin\" />\n </div>\n ) : error ? (\n <div className=\"p-3 bg-red-50 dark:bg-red-900/20 rounded-lg text-red-600 dark:text-red-400 text-sm\">\n {error}\n </div>\n ) : positions.length === 0 ? (\n <div className=\"p-3 bg-n1-ww-gray-50 dark:bg-n1-ww-gray-950 rounded-lg text-n1-ww-gray-500 dark:text-n1-ww-gray-400 text-sm text-center\">\n No open positions\n </div>\n ) : (\n <div className=\"space-y-3\">\n {positions.map((position, index) => (\n <div\n key={index}\n className=\"p-3 bg-n1-ww-gray-50 dark:bg-n1-ww-gray-950 rounded-lg\"\n >\n <div className=\"flex items-center justify-between mb-2\">\n <div className=\"flex items-center\">\n <span className=\"font-medium text-n1-ww-gray-900 dark:text-n1-ww-gray-100 mr-2\">\n {position.symbol}\n </span>\n <span\n className={`text-xs px-2 py-0.5 rounded ${\n position.size > 0\n ? 'bg-green-100 dark:bg-green-900/20 text-green-600 dark:text-green-400'\n : 'bg-red-100 dark:bg-red-900/20 text-red-600 dark:text-red-400'\n }`}\n >\n {position.size > 0 ? 'Long' : 'Short'}{' '}\n {Math.abs(position.size)}\n </span>\n </div>\n </div>\n\n <div className=\"grid grid-cols-2 gap-2 text-sm\">\n <div>\n <div className=\"text-n1-ww-gray-500 dark:text-n1-ww-gray-400\">\n Entry Price\n </div>\n <div className=\"font-medium text-n1-ww-gray-900 dark:text-n1-ww-gray-100 font-mono\">\n {position.entryPrice.toFixed(2)}\n </div>\n </div>\n <div>\n <div className=\"text-n1-ww-gray-500 dark:text-n1-ww-gray-400\">\n Mark Price\n </div>\n <div className=\"font-medium text-n1-ww-gray-900 dark:text-n1-ww-gray-100 font-mono\">\n {position.markPrice.toFixed(2)}\n </div>\n </div>\n <div>\n <div className=\"text-n1-ww-gray-500 dark:text-n1-ww-gray-400\">PnL</div>\n <div\n className={`font-medium font-mono ${\n position.pnl >= 0\n ? 'text-green-600 dark:text-green-400'\n : 'text-red-600 dark:text-red-400'\n }`}\n >\n {position.pnl >= 0 ? '+' : ''}\n {position.pnl.toFixed(2)} (\n {position.pnlPercentage.toFixed(2)}%)\n </div>\n </div>\n <div>\n <div className=\"text-n1-ww-gray-500 dark:text-n1-ww-gray-400\">\n Liquidation\n </div>\n <div className=\"font-medium text-n1-ww-gray-900 dark:text-n1-ww-gray-100 font-mono\">\n {position.liquidationPrice\n ? position.liquidationPrice.toFixed(2)\n : 'N/A'}\n </div>\n </div>\n </div>\n </div>\n ))}\n\n {loading && positions.length > 0 && (\n <div className=\"flex justify-center mt-2\">\n <RefreshCw size={16} className=\"text-n1-ww-gray-400 n1-ww-animate-spin\" />\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n );\n}\n*/"]}
|
|
@@ -37,7 +37,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
37
37
|
import { useEffect } from 'react';
|
|
38
38
|
import { NordUser } from '@n1xyz/nord-ts';
|
|
39
39
|
import { generateSessionKeyPair, signEd25519 } from '@n1xyz/nts-sdk';
|
|
40
|
-
import { PublicKey
|
|
40
|
+
import { PublicKey } from '@solana/web3.js';
|
|
41
41
|
import { getSessionKeysFromLocalStorage, storeSessionKeysInLocalStorage } from '../../Logic/utils';
|
|
42
42
|
import { WalletError, WalletErrorCode } from '../../errors/types';
|
|
43
43
|
import { logger } from '../../utils/logger';
|
|
@@ -88,7 +88,7 @@ export function useNordUserInitialization() {
|
|
|
88
88
|
return null;
|
|
89
89
|
}
|
|
90
90
|
var signTransactionWithWallet = function (transaction) { return __awaiter(_this, void 0, void 0, function () {
|
|
91
|
-
var signedTransaction,
|
|
91
|
+
var signedTransaction, error_2;
|
|
92
92
|
return __generator(this, function (_a) {
|
|
93
93
|
switch (_a.label) {
|
|
94
94
|
case 0:
|
|
@@ -99,8 +99,11 @@ export function useNordUserInitialization() {
|
|
|
99
99
|
case 2:
|
|
100
100
|
signedTransaction = _a.sent();
|
|
101
101
|
logger.debug('Solana transaction signature received');
|
|
102
|
-
serializedTx = signedTransaction.serialize();
|
|
103
|
-
|
|
102
|
+
// const serializedTx = signedTransaction.serialize();
|
|
103
|
+
// return Transaction.from(serializedTx);
|
|
104
|
+
console.log('signedTransaction', signedTransaction.signatures);
|
|
105
|
+
console.log('signedTransaction - 2', signedTransaction);
|
|
106
|
+
return [2 /*return*/, signedTransaction];
|
|
104
107
|
case 3: throw new WalletError(WalletErrorCode.USER_REJECTED, 'Unsupported chain', { originalError: new Error('Unsupported chain') });
|
|
105
108
|
case 4:
|
|
106
109
|
error_2 = _a.sent();
|