@luxfi/exchange 0.1.0 → 0.2.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/dist/bridge/use-private-teleport.d.ts +1 -1
- package/dist/bridge/use-private-teleport.d.ts.map +1 -1
- package/dist/bridge/use-private-teleport.js +73 -71
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/package.json +5 -1
- package/src/bridge/cross-chain-store.ts +210 -0
- package/src/bridge/index.ts +81 -0
- package/src/bridge/private-teleport-types.ts +578 -0
- package/src/bridge/types.ts +125 -0
- package/src/bridge/use-cross-chain-mint.ts +299 -0
- package/src/bridge/use-private-teleport.ts +951 -0
- package/src/index.ts +2 -2
- package/dist/bridge/__tests__/use-private-teleport.test.d.ts +0 -2
- package/dist/bridge/__tests__/use-private-teleport.test.d.ts.map +0 -1
- package/dist/bridge/__tests__/use-private-teleport.test.js +0 -272
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* React hook for cross-chain private teleportation
|
|
5
5
|
* Enables: XVM UTXO → ZNote (shielded) → Z-Chain AMM → destination
|
|
6
6
|
*/
|
|
7
|
-
import { type PrivateTeleportConfig, type PrivateTeleportRequest, type TeleportRecord,
|
|
7
|
+
import { type PrivateTeleportConfig, type PrivateTeleportRequest, type TeleportRecord, TeleportState } from './private-teleport-types';
|
|
8
8
|
export interface UsePrivateTeleportOptions {
|
|
9
9
|
/** Custom config */
|
|
10
10
|
config?: Partial<PrivateTeleportConfig>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-private-teleport.d.ts","sourceRoot":"","sources":["../../src/bridge/use-private-teleport.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,cAAc,EACnB,
|
|
1
|
+
{"version":3,"file":"use-private-teleport.d.ts","sourceRoot":"","sources":["../../src/bridge/use-private-teleport.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { useCallback, useEffect, useState } from 'react';
|
|
8
8
|
import { usePublicClient, useWalletClient } from 'wagmi';
|
|
9
9
|
import { encodeFunctionData, keccak256, toHex } from 'viem';
|
|
10
|
-
import { DEFAULT_PRIVATE_TELEPORT_CONFIG, PRIVATE_TELEPORT_ABI, ZNOTE_ABI, } from './private-teleport-types';
|
|
10
|
+
import { TeleportState, DEFAULT_PRIVATE_TELEPORT_CONFIG, PRIVATE_TELEPORT_ABI, ZNOTE_ABI, } from './private-teleport-types';
|
|
11
11
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
12
12
|
// CRYPTO UTILITIES (STUB - REAL IMPL USES @luxfi/crypto)
|
|
13
13
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -20,7 +20,7 @@ async function generateCommitment(amount, blindingFactor) {
|
|
|
20
20
|
const bf = blindingFactor ?? toHex(BigInt(Math.random() * Number.MAX_SAFE_INTEGER));
|
|
21
21
|
// Pedersen: C = g^amount * h^blinding
|
|
22
22
|
// Simplified for now - real implementation uses elliptic curve ops
|
|
23
|
-
const commitment = keccak256(toHex(amount)
|
|
23
|
+
const commitment = keccak256(`${toHex(amount)}${bf.slice(2)}`);
|
|
24
24
|
return {
|
|
25
25
|
commitment,
|
|
26
26
|
blindingFactor: bf,
|
|
@@ -32,7 +32,7 @@ async function generateCommitment(amount, blindingFactor) {
|
|
|
32
32
|
*/
|
|
33
33
|
async function fheEncrypt(value, publicKey) {
|
|
34
34
|
// Simplified - real implementation uses TFHE
|
|
35
|
-
const ciphertext = keccak256(toHex(value)
|
|
35
|
+
const ciphertext = keccak256(`${toHex(value)}${publicKey.slice(2)}`);
|
|
36
36
|
return {
|
|
37
37
|
ciphertext,
|
|
38
38
|
publicKey,
|
|
@@ -44,7 +44,7 @@ async function fheEncrypt(value, publicKey) {
|
|
|
44
44
|
*/
|
|
45
45
|
async function generateRangeProof(amount, commitment, blindingFactor, rangeBits = 64) {
|
|
46
46
|
// Simplified - real implementation generates actual Bulletproof
|
|
47
|
-
const proof = keccak256(commitment
|
|
47
|
+
const proof = keccak256(`${commitment}${blindingFactor.slice(2)}${toHex(amount).slice(2)}`);
|
|
48
48
|
return {
|
|
49
49
|
proof,
|
|
50
50
|
commitment,
|
|
@@ -55,19 +55,19 @@ async function generateRangeProof(amount, commitment, blindingFactor, rangeBits
|
|
|
55
55
|
* Generate nullifier for spending a note
|
|
56
56
|
*/
|
|
57
57
|
function generateNullifier(commitment, spendingKey, noteIndex) {
|
|
58
|
-
return keccak256(commitment
|
|
58
|
+
return keccak256(`${commitment}${spendingKey.slice(2)}${toHex(noteIndex).slice(2)}`);
|
|
59
59
|
}
|
|
60
60
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
61
61
|
// STATE MAPPING
|
|
62
62
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
63
63
|
const stateMap = {
|
|
64
|
-
0:
|
|
65
|
-
1:
|
|
66
|
-
2:
|
|
67
|
-
3:
|
|
68
|
-
4:
|
|
69
|
-
5:
|
|
70
|
-
6:
|
|
64
|
+
0: TeleportState.INITIATED,
|
|
65
|
+
1: TeleportState.SHIELDED,
|
|
66
|
+
2: TeleportState.SWAP_COMPLETE,
|
|
67
|
+
3: TeleportState.EXPORTED,
|
|
68
|
+
4: TeleportState.COMPLETED,
|
|
69
|
+
5: TeleportState.CANCELLED,
|
|
70
|
+
6: TeleportState.EXPIRED,
|
|
71
71
|
};
|
|
72
72
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
73
73
|
// HOOK IMPLEMENTATION
|
|
@@ -148,7 +148,7 @@ export function usePrivateTeleport(options = {}) {
|
|
|
148
148
|
noteIndex: 0, // Would be extracted from event
|
|
149
149
|
}));
|
|
150
150
|
setCurrentTeleportId(teleportId);
|
|
151
|
-
setCurrentState(
|
|
151
|
+
setCurrentState(TeleportState.INITIATED);
|
|
152
152
|
return teleportId;
|
|
153
153
|
}
|
|
154
154
|
catch (err) {
|
|
@@ -191,8 +191,8 @@ export function usePrivateTeleport(options = {}) {
|
|
|
191
191
|
value: BigInt(0),
|
|
192
192
|
});
|
|
193
193
|
await publicClient.waitForTransactionReceipt({ hash });
|
|
194
|
-
setCurrentState(
|
|
195
|
-
onStateChange?.(teleportId,
|
|
194
|
+
setCurrentState(TeleportState.SWAP_COMPLETE);
|
|
195
|
+
onStateChange?.(teleportId, TeleportState.SWAP_COMPLETE);
|
|
196
196
|
}
|
|
197
197
|
catch (err) {
|
|
198
198
|
const e = err instanceof Error ? err : new Error(String(err));
|
|
@@ -204,6 +204,46 @@ export function usePrivateTeleport(options = {}) {
|
|
|
204
204
|
}
|
|
205
205
|
}, [walletClient, publicClient, config, onStateChange]);
|
|
206
206
|
// ─────────────────────────────────────────────────────────────────────────
|
|
207
|
+
// GET TELEPORT RECORD (defined early for use by other callbacks)
|
|
208
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
209
|
+
const getTeleportRecord = useCallback(async (teleportId) => {
|
|
210
|
+
if (!publicClient) {
|
|
211
|
+
throw new Error('Client not connected');
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
const result = await publicClient.readContract({
|
|
215
|
+
address: config.teleportContract,
|
|
216
|
+
abi: PRIVATE_TELEPORT_ABI,
|
|
217
|
+
functionName: 'getTeleport',
|
|
218
|
+
args: [teleportId],
|
|
219
|
+
});
|
|
220
|
+
const tuple = result;
|
|
221
|
+
const [id, state, sourceChain, destChain, sourceAsset, destAsset, noteCommitment, encryptedAmount, nullifierHash, sender, recipient, deadline, createdBlock, privateSwap] = tuple;
|
|
222
|
+
if (id === '0x' + '00'.repeat(32)) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
teleportId: id,
|
|
227
|
+
state: stateMap[state] ?? TeleportState.INITIATED,
|
|
228
|
+
sourceChain,
|
|
229
|
+
destChain,
|
|
230
|
+
sourceAsset,
|
|
231
|
+
destAsset,
|
|
232
|
+
noteCommitment,
|
|
233
|
+
encryptedAmount,
|
|
234
|
+
nullifierHash: nullifierHash !== '0x' + '00'.repeat(32) ? nullifierHash : undefined,
|
|
235
|
+
sender,
|
|
236
|
+
recipient,
|
|
237
|
+
deadline: Number(deadline),
|
|
238
|
+
createdBlock: Number(createdBlock),
|
|
239
|
+
privateSwap,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
}, [publicClient, config]);
|
|
246
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
207
247
|
// EXPORT TO DESTINATION
|
|
208
248
|
// ─────────────────────────────────────────────────────────────────────────
|
|
209
249
|
const exportToDestination = useCallback(async (teleportId) => {
|
|
@@ -250,8 +290,8 @@ export function usePrivateTeleport(options = {}) {
|
|
|
250
290
|
value: BigInt(0),
|
|
251
291
|
});
|
|
252
292
|
await publicClient.waitForTransactionReceipt({ hash });
|
|
253
|
-
setCurrentState(
|
|
254
|
-
onStateChange?.(teleportId,
|
|
293
|
+
setCurrentState(TeleportState.EXPORTED);
|
|
294
|
+
onStateChange?.(teleportId, TeleportState.EXPORTED);
|
|
255
295
|
}
|
|
256
296
|
catch (err) {
|
|
257
297
|
const e = err instanceof Error ? err : new Error(String(err));
|
|
@@ -283,8 +323,8 @@ export function usePrivateTeleport(options = {}) {
|
|
|
283
323
|
value: BigInt(0),
|
|
284
324
|
});
|
|
285
325
|
await publicClient.waitForTransactionReceipt({ hash });
|
|
286
|
-
setCurrentState(
|
|
287
|
-
onStateChange?.(teleportId,
|
|
326
|
+
setCurrentState(TeleportState.COMPLETED);
|
|
327
|
+
onStateChange?.(teleportId, TeleportState.COMPLETED);
|
|
288
328
|
// Clean up secrets
|
|
289
329
|
setSecrets(prev => {
|
|
290
330
|
const next = new Map(prev);
|
|
@@ -352,8 +392,8 @@ export function usePrivateTeleport(options = {}) {
|
|
|
352
392
|
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
353
393
|
// Extract exportTxId from logs
|
|
354
394
|
const exportTxId = receipt.logs[0]?.topics[1] ?? hash;
|
|
355
|
-
setCurrentState(
|
|
356
|
-
onStateChange?.(request.teleportId,
|
|
395
|
+
setCurrentState(TeleportState.COMPLETED);
|
|
396
|
+
onStateChange?.(request.teleportId, TeleportState.COMPLETED);
|
|
357
397
|
// Clean up secrets
|
|
358
398
|
setSecrets(prev => {
|
|
359
399
|
const next = new Map(prev);
|
|
@@ -392,7 +432,7 @@ export function usePrivateTeleport(options = {}) {
|
|
|
392
432
|
// Generate new commitment for recipient
|
|
393
433
|
const recipientCommitment = await generateCommitment(request.amount);
|
|
394
434
|
// Encrypt note to recipient's viewing key
|
|
395
|
-
const encryptedNote = keccak256(request.recipientViewKey
|
|
435
|
+
const encryptedNote = keccak256(`${request.recipientViewKey}${recipientCommitment.commitment.slice(2)}`);
|
|
396
436
|
// Generate nullifier
|
|
397
437
|
const nullifier = generateNullifier(record.noteCommitment, secret.spendingKey, secret.noteIndex);
|
|
398
438
|
// Get Merkle proof
|
|
@@ -403,7 +443,7 @@ export function usePrivateTeleport(options = {}) {
|
|
|
403
443
|
args: [BigInt(secret.noteIndex)],
|
|
404
444
|
});
|
|
405
445
|
// Generate transfer proof (proves amount conservation)
|
|
406
|
-
const transferProof = keccak256(record.noteCommitment
|
|
446
|
+
const transferProof = keccak256(`${record.noteCommitment}${recipientCommitment.commitment.slice(2)}`);
|
|
407
447
|
const txData = encodeFunctionData({
|
|
408
448
|
abi: PRIVATE_TELEPORT_ABI,
|
|
409
449
|
functionName: 'privateTransferToRecipient',
|
|
@@ -424,8 +464,8 @@ export function usePrivateTeleport(options = {}) {
|
|
|
424
464
|
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
425
465
|
// Extract note index from logs (simplified)
|
|
426
466
|
const newNoteIndex = Number(receipt.logs[0]?.data?.slice(0, 66) ?? '0');
|
|
427
|
-
setCurrentState(
|
|
428
|
-
onStateChange?.(request.teleportId,
|
|
467
|
+
setCurrentState(TeleportState.COMPLETED);
|
|
468
|
+
onStateChange?.(request.teleportId, TeleportState.COMPLETED);
|
|
429
469
|
// Clean up secrets for this teleport
|
|
430
470
|
setSecrets(prev => {
|
|
431
471
|
const next = new Map(prev);
|
|
@@ -467,7 +507,7 @@ export function usePrivateTeleport(options = {}) {
|
|
|
467
507
|
// Generate commitments for each output
|
|
468
508
|
const outputNotes = await Promise.all(outputs.map(async (output) => {
|
|
469
509
|
const commitment = await generateCommitment(output.amount);
|
|
470
|
-
const encryptedNote = keccak256(output.recipient
|
|
510
|
+
const encryptedNote = keccak256(`${output.recipient}${commitment.commitment.slice(2)}`);
|
|
471
511
|
return {
|
|
472
512
|
commitment: commitment.commitment,
|
|
473
513
|
encryptedNote,
|
|
@@ -484,7 +524,7 @@ export function usePrivateTeleport(options = {}) {
|
|
|
484
524
|
args: [BigInt(secret.noteIndex)],
|
|
485
525
|
});
|
|
486
526
|
// Generate split proof (proves sum of outputs = input)
|
|
487
|
-
const splitProof = keccak256(record.noteCommitment
|
|
527
|
+
const splitProof = keccak256(`${record.noteCommitment}${outputNotes.map(n => n.commitment.slice(2)).join('')}`);
|
|
488
528
|
const txData = encodeFunctionData({
|
|
489
529
|
abi: PRIVATE_TELEPORT_ABI,
|
|
490
530
|
functionName: 'splitAndTransfer',
|
|
@@ -504,8 +544,8 @@ export function usePrivateTeleport(options = {}) {
|
|
|
504
544
|
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
505
545
|
// Extract note indices from logs (simplified)
|
|
506
546
|
const noteIndices = outputs.map((_, i) => i);
|
|
507
|
-
setCurrentState(
|
|
508
|
-
onStateChange?.(teleportId,
|
|
547
|
+
setCurrentState(TeleportState.COMPLETED);
|
|
548
|
+
onStateChange?.(teleportId, TeleportState.COMPLETED);
|
|
509
549
|
// Clean up secrets for this teleport
|
|
510
550
|
setSecrets(prev => {
|
|
511
551
|
const next = new Map(prev);
|
|
@@ -544,8 +584,8 @@ export function usePrivateTeleport(options = {}) {
|
|
|
544
584
|
value: BigInt(0),
|
|
545
585
|
});
|
|
546
586
|
await publicClient.waitForTransactionReceipt({ hash });
|
|
547
|
-
setCurrentState(
|
|
548
|
-
onStateChange?.(teleportId,
|
|
587
|
+
setCurrentState(TeleportState.CANCELLED);
|
|
588
|
+
onStateChange?.(teleportId, TeleportState.CANCELLED);
|
|
549
589
|
// Clean up secrets
|
|
550
590
|
setSecrets(prev => {
|
|
551
591
|
const next = new Map(prev);
|
|
@@ -562,45 +602,7 @@ export function usePrivateTeleport(options = {}) {
|
|
|
562
602
|
setIsLoading(false);
|
|
563
603
|
}
|
|
564
604
|
}, [walletClient, publicClient, config, onStateChange]);
|
|
565
|
-
//
|
|
566
|
-
// GET TELEPORT RECORD
|
|
567
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
568
|
-
const getTeleportRecord = useCallback(async (teleportId) => {
|
|
569
|
-
if (!publicClient) {
|
|
570
|
-
throw new Error('Client not connected');
|
|
571
|
-
}
|
|
572
|
-
try {
|
|
573
|
-
const result = await publicClient.readContract({
|
|
574
|
-
address: config.teleportContract,
|
|
575
|
-
abi: PRIVATE_TELEPORT_ABI,
|
|
576
|
-
functionName: 'getTeleport',
|
|
577
|
-
args: [teleportId],
|
|
578
|
-
});
|
|
579
|
-
const [id, state, sourceChain, destChain, sourceAsset, destAsset, noteCommitment, encryptedAmount, nullifierHash, sender, recipient, deadline, createdBlock, privateSwap] = result;
|
|
580
|
-
if (id === '0x' + '00'.repeat(32)) {
|
|
581
|
-
return null;
|
|
582
|
-
}
|
|
583
|
-
return {
|
|
584
|
-
teleportId: id,
|
|
585
|
-
state: stateMap[state] ?? 'pending',
|
|
586
|
-
sourceChain,
|
|
587
|
-
destChain,
|
|
588
|
-
sourceAsset,
|
|
589
|
-
destAsset,
|
|
590
|
-
noteCommitment,
|
|
591
|
-
encryptedAmount,
|
|
592
|
-
nullifierHash: nullifierHash !== '0x' + '00'.repeat(32) ? nullifierHash : undefined,
|
|
593
|
-
sender,
|
|
594
|
-
recipient,
|
|
595
|
-
deadline: Number(deadline),
|
|
596
|
-
createdBlock: Number(createdBlock),
|
|
597
|
-
privateSwap,
|
|
598
|
-
};
|
|
599
|
-
}
|
|
600
|
-
catch {
|
|
601
|
-
return null;
|
|
602
|
-
}
|
|
603
|
-
}, [publicClient, config]);
|
|
605
|
+
// Wrapper for external API
|
|
604
606
|
const getTeleport = useCallback(async (teleportId) => {
|
|
605
607
|
return getTeleportRecord(teleportId);
|
|
606
608
|
}, [getTeleportRecord]);
|
|
@@ -628,7 +630,7 @@ export function usePrivateTeleport(options = {}) {
|
|
|
628
630
|
// POLLING
|
|
629
631
|
// ─────────────────────────────────────────────────────────────────────────
|
|
630
632
|
useEffect(() => {
|
|
631
|
-
if (!currentTeleportId || !publicClient || currentState ===
|
|
633
|
+
if (!currentTeleportId || !publicClient || currentState === TeleportState.COMPLETED || currentState === TeleportState.CANCELLED) {
|
|
632
634
|
return;
|
|
633
635
|
}
|
|
634
636
|
const poll = async () => {
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,cAAc,UAAU,CAAA;AAGxB,cAAc,UAAU,CAAA;AAGxB,cAAc,aAAa,CAAA;AAG3B,cAAc,OAAO,CAAA;AAGrB,cAAc,SAAS,CAAA;AAGvB,cAAc,UAAU,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,cAAc,UAAU,CAAA;AAGxB,cAAc,UAAU,CAAA;AAGxB,cAAc,aAAa,CAAA;AAG3B,cAAc,OAAO,CAAA;AAGrB,cAAc,SAAS,CAAA;AAGvB,cAAc,UAAU,CAAA;AAGxB,cAAc,UAAU,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -20,5 +20,5 @@ export * from './dex';
|
|
|
20
20
|
export * from './hooks';
|
|
21
21
|
// Stores
|
|
22
22
|
export * from './stores';
|
|
23
|
-
//
|
|
24
|
-
|
|
23
|
+
// Bridge (cross-chain + Z-Chain privacy layer)
|
|
24
|
+
export * from './bridge';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luxfi/exchange",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Lux Exchange SDK - React hooks and utilities for Lux DEX",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -34,6 +34,10 @@
|
|
|
34
34
|
"./dex": {
|
|
35
35
|
"types": "./dist/dex/index.d.ts",
|
|
36
36
|
"import": "./dist/dex/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./bridge": {
|
|
39
|
+
"types": "./dist/bridge/index.d.ts",
|
|
40
|
+
"import": "./dist/bridge/index.js"
|
|
37
41
|
}
|
|
38
42
|
},
|
|
39
43
|
"files": [
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zustand store for cross-chain mint operations
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { create } from 'zustand'
|
|
6
|
+
import { devtools, persist } from 'zustand/middleware'
|
|
7
|
+
import type { CrossChainMintRequest, CrossChainMintState, CrossChainMintStatus } from './types'
|
|
8
|
+
|
|
9
|
+
interface PendingMint extends CrossChainMintRequest {
|
|
10
|
+
state: CrossChainMintState
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface CrossChainStore {
|
|
14
|
+
/** Currently active mints by swap ID */
|
|
15
|
+
pendingMints: Record<string, PendingMint>
|
|
16
|
+
/** Most recent completed mints (for history) */
|
|
17
|
+
recentMints: PendingMint[]
|
|
18
|
+
/** Maximum number of recent mints to keep */
|
|
19
|
+
maxRecentMints: number
|
|
20
|
+
|
|
21
|
+
// Actions
|
|
22
|
+
initiateMint: (request: CrossChainMintRequest) => string
|
|
23
|
+
updateMintStatus: (swapId: string, status: CrossChainMintStatus) => void
|
|
24
|
+
updateMintTxHash: (swapId: string, field: 'sourceTxHash' | 'mintTxHash' | 'swapTxHash', hash: string) => void
|
|
25
|
+
completeMint: (swapId: string) => void
|
|
26
|
+
failMint: (swapId: string, error: string) => void
|
|
27
|
+
cancelMint: (swapId: string) => void
|
|
28
|
+
clearPendingMint: (swapId: string) => void
|
|
29
|
+
getPendingMint: (swapId: string) => PendingMint | undefined
|
|
30
|
+
getActiveMints: () => PendingMint[]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Generate a random swap ID
|
|
34
|
+
function generateSwapId(): string {
|
|
35
|
+
const bytes = new Uint8Array(32)
|
|
36
|
+
crypto.getRandomValues(bytes)
|
|
37
|
+
return '0x' + Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const useCrossChainStore = create<CrossChainStore>()(
|
|
41
|
+
devtools(
|
|
42
|
+
persist(
|
|
43
|
+
(set, get) => ({
|
|
44
|
+
pendingMints: {},
|
|
45
|
+
recentMints: [],
|
|
46
|
+
maxRecentMints: 20,
|
|
47
|
+
|
|
48
|
+
initiateMint: (request: CrossChainMintRequest) => {
|
|
49
|
+
const swapId = generateSwapId()
|
|
50
|
+
const now = Date.now()
|
|
51
|
+
|
|
52
|
+
const pendingMint: PendingMint = {
|
|
53
|
+
...request,
|
|
54
|
+
state: {
|
|
55
|
+
swapId: swapId as `0x${string}`,
|
|
56
|
+
status: 'initiating',
|
|
57
|
+
sourceTxHash: null,
|
|
58
|
+
mintTxHash: null,
|
|
59
|
+
swapTxHash: null,
|
|
60
|
+
error: null,
|
|
61
|
+
startedAt: now,
|
|
62
|
+
completedAt: null,
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
set((state) => ({
|
|
67
|
+
pendingMints: {
|
|
68
|
+
...state.pendingMints,
|
|
69
|
+
[swapId]: pendingMint,
|
|
70
|
+
},
|
|
71
|
+
}))
|
|
72
|
+
|
|
73
|
+
return swapId
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
updateMintStatus: (swapId: string, status: CrossChainMintStatus) => {
|
|
77
|
+
set((state) => {
|
|
78
|
+
const mint = state.pendingMints[swapId]
|
|
79
|
+
if (!mint) return state
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
pendingMints: {
|
|
83
|
+
...state.pendingMints,
|
|
84
|
+
[swapId]: {
|
|
85
|
+
...mint,
|
|
86
|
+
state: {
|
|
87
|
+
...mint.state,
|
|
88
|
+
status,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
updateMintTxHash: (swapId: string, field: 'sourceTxHash' | 'mintTxHash' | 'swapTxHash', hash: string) => {
|
|
97
|
+
set((state) => {
|
|
98
|
+
const mint = state.pendingMints[swapId]
|
|
99
|
+
if (!mint) return state
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
pendingMints: {
|
|
103
|
+
...state.pendingMints,
|
|
104
|
+
[swapId]: {
|
|
105
|
+
...mint,
|
|
106
|
+
state: {
|
|
107
|
+
...mint.state,
|
|
108
|
+
[field]: hash,
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
completeMint: (swapId: string) => {
|
|
117
|
+
set((state) => {
|
|
118
|
+
const mint = state.pendingMints[swapId]
|
|
119
|
+
if (!mint) return state
|
|
120
|
+
|
|
121
|
+
const completedMint: PendingMint = {
|
|
122
|
+
...mint,
|
|
123
|
+
state: {
|
|
124
|
+
...mint.state,
|
|
125
|
+
status: 'complete',
|
|
126
|
+
completedAt: Date.now(),
|
|
127
|
+
},
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const { [swapId]: _, ...remainingPending } = state.pendingMints
|
|
131
|
+
const newRecent = [completedMint, ...state.recentMints].slice(0, state.maxRecentMints)
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
pendingMints: remainingPending,
|
|
135
|
+
recentMints: newRecent,
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
failMint: (swapId: string, error: string) => {
|
|
141
|
+
set((state) => {
|
|
142
|
+
const mint = state.pendingMints[swapId]
|
|
143
|
+
if (!mint) return state
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
pendingMints: {
|
|
147
|
+
...state.pendingMints,
|
|
148
|
+
[swapId]: {
|
|
149
|
+
...mint,
|
|
150
|
+
state: {
|
|
151
|
+
...mint.state,
|
|
152
|
+
status: 'failed',
|
|
153
|
+
error,
|
|
154
|
+
completedAt: Date.now(),
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
}
|
|
159
|
+
})
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
cancelMint: (swapId: string) => {
|
|
163
|
+
set((state) => {
|
|
164
|
+
const mint = state.pendingMints[swapId]
|
|
165
|
+
if (!mint) return state
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
pendingMints: {
|
|
169
|
+
...state.pendingMints,
|
|
170
|
+
[swapId]: {
|
|
171
|
+
...mint,
|
|
172
|
+
state: {
|
|
173
|
+
...mint.state,
|
|
174
|
+
status: 'cancelled',
|
|
175
|
+
completedAt: Date.now(),
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
clearPendingMint: (swapId: string) => {
|
|
184
|
+
set((state) => {
|
|
185
|
+
const { [swapId]: _, ...remainingPending } = state.pendingMints
|
|
186
|
+
return { pendingMints: remainingPending }
|
|
187
|
+
})
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
getPendingMint: (swapId: string) => {
|
|
191
|
+
return get().pendingMints[swapId]
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
getActiveMints: () => {
|
|
195
|
+
const { pendingMints } = get()
|
|
196
|
+
return Object.values(pendingMints).filter(
|
|
197
|
+
(m) => m.state.status !== 'failed' && m.state.status !== 'cancelled'
|
|
198
|
+
)
|
|
199
|
+
},
|
|
200
|
+
}),
|
|
201
|
+
{
|
|
202
|
+
name: 'cross-chain-mint-storage',
|
|
203
|
+
partialize: (state) => ({
|
|
204
|
+
recentMints: state.recentMints,
|
|
205
|
+
}),
|
|
206
|
+
}
|
|
207
|
+
),
|
|
208
|
+
{ name: 'CrossChainStore' }
|
|
209
|
+
)
|
|
210
|
+
)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-chain bridge module for XVM ↔ C-Chain atomic swaps
|
|
3
|
+
*
|
|
4
|
+
* Enables one-click minting of wrapped tokens on C-Chain from X-Chain assets
|
|
5
|
+
* using Warp cross-chain messaging and HTLC atomic swaps.
|
|
6
|
+
*
|
|
7
|
+
* Includes Z-Chain privacy layer for fully shielded cross-chain teleportation:
|
|
8
|
+
* XVM UTXO → ZNote (shielded) → Z-Chain AMM (private swap) → destination
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* // Standard cross-chain mint
|
|
13
|
+
* import { useCrossChainMint } from '@luxfi/exchange/bridge'
|
|
14
|
+
*
|
|
15
|
+
* function MintButton() {
|
|
16
|
+
* const { mint, isLoading } = useCrossChainMint()
|
|
17
|
+
*
|
|
18
|
+
* const handleMint = async () => {
|
|
19
|
+
* const swapId = await mint({
|
|
20
|
+
* sourceAsset: '0x...', // XVM asset ID
|
|
21
|
+
* amount: 1000000n, // 1 token (6 decimals)
|
|
22
|
+
* recipient: '0x...', // C-Chain recipient
|
|
23
|
+
* targetToken: '0x...', // Optional: swap to this token
|
|
24
|
+
* minReceive: 990000n, // Slippage protection
|
|
25
|
+
* })
|
|
26
|
+
* console.log('Mint initiated:', swapId)
|
|
27
|
+
* }
|
|
28
|
+
*
|
|
29
|
+
* return (
|
|
30
|
+
* <button onClick={handleMint} disabled={isLoading}>
|
|
31
|
+
* {isLoading ? 'Minting...' : 'Mint on C-Chain'}
|
|
32
|
+
* </button>
|
|
33
|
+
* )
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```tsx
|
|
39
|
+
* // Private teleport with Z-Chain
|
|
40
|
+
* import { usePrivateTeleport } from '@luxfi/exchange/bridge'
|
|
41
|
+
*
|
|
42
|
+
* function PrivateTeleportButton() {
|
|
43
|
+
* const { teleport, executeSwap, exportToDestination, isLoading } = usePrivateTeleport()
|
|
44
|
+
*
|
|
45
|
+
* const handlePrivateTeleport = async () => {
|
|
46
|
+
* // 1. Initiate shielded teleport (creates ZNote)
|
|
47
|
+
* const teleportId = await teleport({
|
|
48
|
+
* sourceChain: '0x...', // X-Chain ID
|
|
49
|
+
* destChain: '0x...', // C-Chain ID
|
|
50
|
+
* sourceAsset: '0x...', // Asset to teleport
|
|
51
|
+
* destAsset: '0x...', // Asset to receive (if swapping)
|
|
52
|
+
* amount: 1000000n, // Amount (will be encrypted)
|
|
53
|
+
* recipient: '0x...', // Destination address
|
|
54
|
+
* deadline: Math.floor(Date.now() / 1000) + 3600,
|
|
55
|
+
* privateSwap: true, // Use Z-Chain AMM for private swap
|
|
56
|
+
* })
|
|
57
|
+
*
|
|
58
|
+
* // 2. Execute private swap on Z-Chain (optional)
|
|
59
|
+
* await executeSwap(teleportId, '0x...poolId', 990000n)
|
|
60
|
+
*
|
|
61
|
+
* // 3. Export to destination chain with range proof
|
|
62
|
+
* await exportToDestination(teleportId)
|
|
63
|
+
* }
|
|
64
|
+
*
|
|
65
|
+
* return (
|
|
66
|
+
* <button onClick={handlePrivateTeleport} disabled={isLoading}>
|
|
67
|
+
* Private Teleport
|
|
68
|
+
* </button>
|
|
69
|
+
* )
|
|
70
|
+
* }
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
|
|
74
|
+
// Standard cross-chain (public)
|
|
75
|
+
export * from './types'
|
|
76
|
+
export * from './cross-chain-store'
|
|
77
|
+
export * from './use-cross-chain-mint'
|
|
78
|
+
|
|
79
|
+
// Private teleport (Z-Chain privacy layer)
|
|
80
|
+
export * from './private-teleport-types'
|
|
81
|
+
export * from './use-private-teleport'
|