crypto-swap 1.0.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/LICENSE +21 -0
- package/README.md +59 -0
- package/assets/ui/css/base.css +397 -0
- package/assets/ui/css/components.css +377 -0
- package/assets/ui/css/home.css +465 -0
- package/assets/ui/css/layout.css +16 -0
- package/assets/ui/index.html +292 -0
- package/assets/ui/js/app.js +221 -0
- package/assets/ui/js/exchange.js +728 -0
- package/assets/ui/js/order.js +362 -0
- package/assets/ui/order.html +707 -0
- package/package.json +27 -0
- package/swap.js +1030 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LightningEX - Order Detail Page
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const API_BASE = 'https://api.lightningex.io';
|
|
6
|
+
let orderId = null;
|
|
7
|
+
let pollInterval = null;
|
|
8
|
+
|
|
9
|
+
function getOrderIdFromUrl() {
|
|
10
|
+
const params = new URLSearchParams(window.location.search);
|
|
11
|
+
return params.get('id');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
15
|
+
orderId = getOrderIdFromUrl();
|
|
16
|
+
if (!orderId) {
|
|
17
|
+
showError();
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
document.getElementById('displayOrderId').textContent = orderId;
|
|
21
|
+
loadOrderStatus();
|
|
22
|
+
pollInterval = setInterval(loadOrderStatus, 15000);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
function showError() {
|
|
26
|
+
document.getElementById('loadingState').classList.add('hidden');
|
|
27
|
+
document.getElementById('errorState').classList.remove('hidden');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function loadOrderStatus() {
|
|
31
|
+
try {
|
|
32
|
+
const response = await fetch(`${API_BASE}/exchange/order/get?id=${orderId}`);
|
|
33
|
+
const result = await response.json();
|
|
34
|
+
|
|
35
|
+
if (result.code !== 200) {
|
|
36
|
+
showError();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const data = result.data;
|
|
41
|
+
document.getElementById('loadingState').classList.add('hidden');
|
|
42
|
+
document.getElementById('orderContent').classList.remove('hidden');
|
|
43
|
+
|
|
44
|
+
updateProgressSteps(data.status);
|
|
45
|
+
updateStatusContent(data);
|
|
46
|
+
updateTransactionDetails(data);
|
|
47
|
+
|
|
48
|
+
if (['Complete', 'Failed', 'Refund', 'Request Overdue'].includes(data.status)) {
|
|
49
|
+
clearInterval(pollInterval);
|
|
50
|
+
}
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error('Failed to load order:', error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function updateProgressSteps(currentStatus) {
|
|
57
|
+
const steps = document.querySelectorAll('.step');
|
|
58
|
+
const statusOrder = ['Awaiting Deposit', 'Confirming Deposit', 'Exchanging', 'Sending', 'Complete'];
|
|
59
|
+
const currentIndex = statusOrder.indexOf(currentStatus);
|
|
60
|
+
|
|
61
|
+
steps.forEach((step, index) => {
|
|
62
|
+
step.classList.remove('active', 'completed');
|
|
63
|
+
if (index < currentIndex) {
|
|
64
|
+
step.classList.add('completed');
|
|
65
|
+
step.querySelector('.step-circle').textContent = '✓';
|
|
66
|
+
} else if (index === currentIndex) {
|
|
67
|
+
step.classList.add('active');
|
|
68
|
+
step.querySelector('.step-circle').textContent = index + 1;
|
|
69
|
+
} else {
|
|
70
|
+
step.querySelector('.step-circle').textContent = index + 1;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (['Failed', 'Refund', 'Action Request', 'Request Overdue'].includes(currentStatus)) {
|
|
75
|
+
steps.forEach(step => step.classList.remove('active'));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const STATUS_MESSAGES = {
|
|
80
|
+
'Awaiting Deposit': {
|
|
81
|
+
lines: [
|
|
82
|
+
{ text: 'Your order will automatically proceed to the next step once your deposit receives its first confirmation on the blockchain.' },
|
|
83
|
+
{ text: 'If you do not deposit the amount shown above, or your deposit does not arrive within 1 hour, for security purposes your order will not be processed automatically.' }
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
'Confirming Deposit': {
|
|
87
|
+
lines: [
|
|
88
|
+
{ text: 'You successfully sent {sendAmount} {send}! Please wait while your deposit is being confirmed.', highlight: 'You successfully sent {sendAmount} {send}!' },
|
|
89
|
+
{ text: 'Your order will automatically proceed to the next step after the deposit transaction gets 1 confirmation.' },
|
|
90
|
+
{ text: 'Nothing more is expected from you in this step.' }
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
'Exchanging': {
|
|
94
|
+
lines: [
|
|
95
|
+
{ text: 'Your deposit has been confirmed! We are now exchanging your {sendAmount} {send} to {receiveAmount} {receive}.', highlight: 'We are now exchanging your {sendAmount} {send} to {receiveAmount} {receive}' },
|
|
96
|
+
{ text: 'This may take a few minutes, please be patient.' }
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
'Sending': {
|
|
100
|
+
lines: [
|
|
101
|
+
{ text: 'Your order is almost complete! We are forwarding {receiveAmount} {receive} to the following address: {receiveAddress}', highlight: 'We are forwarding {receiveAmount} {receive}' }
|
|
102
|
+
]
|
|
103
|
+
},
|
|
104
|
+
'Complete': {
|
|
105
|
+
lines: [
|
|
106
|
+
{ text: 'Your order is successfully completed! We forwarded {receiveAmount} {receive} to the following address: {receiveAddress}', highlight: 'We forwarded {receiveAmount} {receive}' }
|
|
107
|
+
]
|
|
108
|
+
},
|
|
109
|
+
'Action Request': {
|
|
110
|
+
lines: [
|
|
111
|
+
{ text: 'This transaction has been detected with risks and needs to be verified before proceeding.' },
|
|
112
|
+
{ text: 'Please contact lightningex.io for support.' }
|
|
113
|
+
]
|
|
114
|
+
},
|
|
115
|
+
'Failed': {
|
|
116
|
+
lines: [
|
|
117
|
+
{ text: 'The order failed because of one of the following reasons:', highlight: 'The order failed' },
|
|
118
|
+
{ text: '• You didn\'t send {sendAmount} {send} on the {sendNetwork} network.' },
|
|
119
|
+
{ text: '• Your deposit was not confirmed within 2 hours.' },
|
|
120
|
+
{ text: '• For any other reason.' }
|
|
121
|
+
]
|
|
122
|
+
},
|
|
123
|
+
'Request Overdue': {
|
|
124
|
+
lines: [
|
|
125
|
+
{ text: 'The transaction request has expired.', highlight: 'expired' },
|
|
126
|
+
{ text: 'If you have other questions, please seek support from lightningex.io.' }
|
|
127
|
+
]
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
function formatStatusMessage(status, data) {
|
|
132
|
+
const msgInfo = STATUS_MESSAGES[status];
|
|
133
|
+
if (!msgInfo) return '';
|
|
134
|
+
|
|
135
|
+
return msgInfo.lines.map(lineObj => {
|
|
136
|
+
const line = typeof lineObj === 'string' ? lineObj : lineObj.text;
|
|
137
|
+
const highlight = typeof lineObj === 'object' ? lineObj.highlight : null;
|
|
138
|
+
|
|
139
|
+
let formatted = line;
|
|
140
|
+
Object.keys(data).forEach(key => {
|
|
141
|
+
if (data[key] !== null && data[key] !== undefined) {
|
|
142
|
+
formatted = formatted.replace(new RegExp(`{${key}}`, 'g'), data[key]);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
if (highlight) {
|
|
147
|
+
let highlightText = highlight;
|
|
148
|
+
Object.keys(data).forEach(key => {
|
|
149
|
+
if (data[key] !== null && data[key] !== undefined) {
|
|
150
|
+
highlightText = highlightText.replace(new RegExp(`{${key}}`, 'g'), data[key]);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
formatted = formatted.replace(highlightText, `<span class="status-highlight">${highlightText}</span>`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return formatted;
|
|
157
|
+
}).join('<br>');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function updateStatusContent(data) {
|
|
161
|
+
const container = document.getElementById('statusContent');
|
|
162
|
+
const statusClass = getStatusClass(data.status);
|
|
163
|
+
const statusMessage = formatStatusMessage(data.status, data);
|
|
164
|
+
|
|
165
|
+
let html = `
|
|
166
|
+
<div class="status-header">
|
|
167
|
+
<div class="status-badge ${statusClass}">${data.status}</div>
|
|
168
|
+
</div>
|
|
169
|
+
`;
|
|
170
|
+
|
|
171
|
+
if (statusMessage) {
|
|
172
|
+
html += `
|
|
173
|
+
<div class="status-message-box">
|
|
174
|
+
<p>${statusMessage}</p>
|
|
175
|
+
</div>
|
|
176
|
+
`;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (data.status === 'Awaiting Deposit') {
|
|
180
|
+
html += `
|
|
181
|
+
<div class="deposit-section">
|
|
182
|
+
<div class="qr-container">
|
|
183
|
+
<div id="qrcode"></div>
|
|
184
|
+
</div>
|
|
185
|
+
<div class="deposit-info">
|
|
186
|
+
<div class="info-row">
|
|
187
|
+
<span class="info-label">Send ${data.send} on ${data.sendNetwork} network to this address</span>
|
|
188
|
+
<div class="info-value">
|
|
189
|
+
<span class="address-value">${data.sendAddress}</span>
|
|
190
|
+
<button class="copy-btn" onclick="copyToClipboard('${data.sendAddress}')">Copy</button>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
${data.sendTag ? `
|
|
194
|
+
<div class="info-row">
|
|
195
|
+
<span class="info-label">Tag/MEMO (Required)</span>
|
|
196
|
+
<div class="info-value">
|
|
197
|
+
<span class="address-value">${data.sendTag}</span>
|
|
198
|
+
<button class="copy-btn" onclick="copyToClipboard('${data.sendTag}')">Copy</button>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
` : ''}
|
|
202
|
+
<div class="info-row">
|
|
203
|
+
<span class="info-label">Amount to Send</span>
|
|
204
|
+
<div class="info-value">
|
|
205
|
+
<span>${data.sendAmount} ${data.send}</span>
|
|
206
|
+
<button class="copy-btn" onclick="copyToClipboard('${data.sendAmount}')">Copy</button>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
<div class="warning-box">
|
|
210
|
+
⚠️ Please send exactly the specified amount. Sending a different amount may result in delays or additional fees.
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
`;
|
|
215
|
+
|
|
216
|
+
setTimeout(() => {
|
|
217
|
+
const qrContainer = document.getElementById('qrcode');
|
|
218
|
+
if (qrContainer) {
|
|
219
|
+
qrContainer.innerHTML = '';
|
|
220
|
+
new QRCode(qrContainer, {
|
|
221
|
+
text: data.sendAddress,
|
|
222
|
+
width: 140,
|
|
223
|
+
height: 140,
|
|
224
|
+
colorDark: '#000000',
|
|
225
|
+
colorLight: '#ffffff'
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}, 0);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (['Confirming Deposit', 'Exchanging', 'Sending'].includes(data.status)) {
|
|
232
|
+
html += `
|
|
233
|
+
<div class="processing-container">
|
|
234
|
+
<div class="loading-spinner"></div>
|
|
235
|
+
<p style="color: var(--neon-cyan);">Estimated time: ${data.processingTime || '3-5'} minutes</p>
|
|
236
|
+
</div>
|
|
237
|
+
`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (data.status === 'Complete') {
|
|
241
|
+
html += `
|
|
242
|
+
<div class="complete-container">
|
|
243
|
+
<div class="complete-icon">🎉</div>
|
|
244
|
+
<h2 style="color: var(--neon-green); margin-bottom: 20px;">Exchange Complete!</h2>
|
|
245
|
+
<a href="./index.html" class="btn btn-primary">Start New Exchange →</a>
|
|
246
|
+
</div>
|
|
247
|
+
`;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (['Failed', 'Refund', 'Action Request', 'Request Overdue'].includes(data.status)) {
|
|
251
|
+
const icon = data.status === 'Action Request' ? '⚠️' : (data.status === 'Request Overdue' ? '⏰' : '❌');
|
|
252
|
+
html += `
|
|
253
|
+
<div class="failed-container">
|
|
254
|
+
<div class="failed-icon">${icon}</div>
|
|
255
|
+
${data.statusNote ? `<p style="color: var(--text-secondary); margin-top: 10px;">${data.statusNote}</p>` : ''}
|
|
256
|
+
<a href="https://www.lightningex.io" target="_blank" class="btn btn-secondary" style="margin-top: 20px;">Contact Support</a>
|
|
257
|
+
</div>
|
|
258
|
+
`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
container.innerHTML = html;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function updateTransactionDetails(data) {
|
|
265
|
+
document.getElementById('sendAmount').textContent = data.sendAmount;
|
|
266
|
+
document.getElementById('sendCurrency').textContent = `${data.send} (${data.sendNetwork})`;
|
|
267
|
+
document.getElementById('receiveAmount').textContent = data.receiveAmount;
|
|
268
|
+
document.getElementById('receiveCurrency').textContent = `${data.receive} (${data.receiveNetwork})`;
|
|
269
|
+
|
|
270
|
+
const detailList = document.getElementById('detailList');
|
|
271
|
+
const createdAt = data.createdAt ? new Date(data.createdAt).toLocaleString() : 'N/A';
|
|
272
|
+
|
|
273
|
+
detailList.innerHTML = `
|
|
274
|
+
<div class="detail-item">
|
|
275
|
+
<span class="detail-item-label">Order ID</span>
|
|
276
|
+
<span class="detail-item-value">${data.id}</span>
|
|
277
|
+
</div>
|
|
278
|
+
<div class="detail-item">
|
|
279
|
+
<span class="detail-item-label">Created At</span>
|
|
280
|
+
<span class="detail-item-value">${createdAt}</span>
|
|
281
|
+
</div>
|
|
282
|
+
<div class="detail-item">
|
|
283
|
+
<span class="detail-item-label">Receive Address</span>
|
|
284
|
+
<span class="detail-item-value">${data.receiveAddress}</span>
|
|
285
|
+
</div>
|
|
286
|
+
${data.receiveTag ? `
|
|
287
|
+
<div class="detail-item">
|
|
288
|
+
<span class="detail-item-label">Receive Tag/MEMO</span>
|
|
289
|
+
<span class="detail-item-value">${data.receiveTag}</span>
|
|
290
|
+
</div>
|
|
291
|
+
` : ''}
|
|
292
|
+
${data.networkFee ? `
|
|
293
|
+
<div class="detail-item">
|
|
294
|
+
<span class="detail-item-label">Network Fee</span>
|
|
295
|
+
<span class="detail-item-value">${data.networkFee} ${data.receive}</span>
|
|
296
|
+
</div>
|
|
297
|
+
` : ''}
|
|
298
|
+
`;
|
|
299
|
+
|
|
300
|
+
const txLinksContainer = document.getElementById('txLinks');
|
|
301
|
+
let linksHtml = '';
|
|
302
|
+
|
|
303
|
+
if (data.hashIn && data.hashIn.length > 0 && data.hashInExplorer) {
|
|
304
|
+
linksHtml += `
|
|
305
|
+
<div class="tx-link">
|
|
306
|
+
<span>Incoming Transaction</span>
|
|
307
|
+
<a href="${data.hashInExplorer.replace('{{txid}}', data.hashIn[0])}" target="_blank">View on Explorer →</a>
|
|
308
|
+
</div>
|
|
309
|
+
`;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (data.hashOut && data.hashOut.length > 0 && data.hashOutExplorer) {
|
|
313
|
+
linksHtml += `
|
|
314
|
+
<div class="tx-link">
|
|
315
|
+
<span>Outgoing Transaction</span>
|
|
316
|
+
<a href="${data.hashOutExplorer.replace('{{txid}}', data.hashOut[0])}" target="_blank">View on Explorer →</a>
|
|
317
|
+
</div>
|
|
318
|
+
`;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
txLinksContainer.innerHTML = linksHtml;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function getStatusClass(status) {
|
|
325
|
+
const statusClasses = {
|
|
326
|
+
'Awaiting Deposit': 'awaiting',
|
|
327
|
+
'Confirming Deposit': 'confirming',
|
|
328
|
+
'Exchanging': 'exchanging',
|
|
329
|
+
'Sending': 'sending',
|
|
330
|
+
'Complete': 'complete',
|
|
331
|
+
'Failed': 'failed',
|
|
332
|
+
'Refund': 'refund',
|
|
333
|
+
'Action Request': 'action-request',
|
|
334
|
+
'Request Overdue': 'request-overdue'
|
|
335
|
+
};
|
|
336
|
+
return statusClasses[status] || 'awaiting';
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function copyToClipboard(text) {
|
|
340
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
341
|
+
alert('Copied to clipboard!');
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function copyOrderUrl() {
|
|
346
|
+
const url = window.location.href;
|
|
347
|
+
navigator.clipboard.writeText(url).then(() => {
|
|
348
|
+
const btn = document.getElementById('copyUrlBtn');
|
|
349
|
+
const originalText = btn.textContent;
|
|
350
|
+
btn.textContent = 'Copied!';
|
|
351
|
+
setTimeout(() => {
|
|
352
|
+
btn.textContent = originalText;
|
|
353
|
+
}, 2000);
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function copyOrderId() {
|
|
358
|
+
if (!orderId) return;
|
|
359
|
+
navigator.clipboard.writeText(orderId).then(() => {
|
|
360
|
+
alert('Order ID copied!');
|
|
361
|
+
});
|
|
362
|
+
}
|