Skip to content

Commit 875bc51

Browse files
authored
Merge pull request #2 from kylebolton/feature/readmeUpdate
Add sonic (non railgun assets to tests) + update readme.MD
2 parents a360fc4 + d341b56 commit 875bc51

File tree

2 files changed

+122
-1
lines changed

2 files changed

+122
-1
lines changed

README.md

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
# Liquyn Swap
22

3-
A minimal, mobile-first bridge dApp for swapping and bridging from any chain to Hyperliquid. Built for the Hyperliquid Hackathon.
3+
A minimal, mobile-first bridge dApp for swapping and bridging from any chain to Hyperliquid, with privacy bult in as standard. Built for the Hyperliquid Hackathon.
44

55
**Live Demo:** https://liquyn-swap.vercel.app/
66
**Repository:** https://github.com/kylebolton/HyperliquidHackathon
77

88
## Features
99

1010
- 🌉 Cross-chain bridging to Hyperliquid via LI.FI
11+
- 🔒 **Privacy Mode** - Break on-chain traceability with Railgun
1112
- 📱 Mobile-first responsive design
1213
- 💰 Auto-deposit to Hyperliquid L1 trading account
1314
- 🔄 Multiple route comparison (fastest, cheapest, recommended)
@@ -32,6 +33,7 @@ npm run dev
3233
- Tailwind CSS v3
3334
- RainbowKit + wagmi v2 + viem
3435
- LI.FI SDK
36+
- Railgun SDK (privacy)
3537
- Motion (framer-motion)
3638
- Three.js
3739
- TanStack Query
@@ -64,6 +66,35 @@ import { DepositToHyperliquid } from './components/deposit/DepositToHyperliquid'
6466

6567
→ All bridging to **Hyperliquid** (Chain ID: 999)
6668

69+
## Privacy Mode
70+
71+
Enable **Privacy Mode** to break the on-chain link between your source wallet and Hyperliquid account using [Railgun](https://www.railgun.org/).
72+
73+
### How it works
74+
75+
1. **Shield** - Your tokens are encrypted into Railgun's private pool
76+
2. **Bridge** - Funds are privately bridged to the destination chain
77+
3. **Unshield** - Tokens are delivered to your Hyperliquid wallet with no traceable link
78+
79+
### Supported Privacy Chains
80+
81+
Privacy routing is available through these Railgun-supported networks:
82+
83+
| Chain | ID |
84+
|-------|-----|
85+
| Ethereum | 1 |
86+
| Arbitrum | 42161 |
87+
| Polygon | 137 |
88+
| BNB Chain | 56 |
89+
90+
**Starting from any chain?** No problem! If you're bridging from a non-Railgun chain (like Sonic, Base, or Avalanche), your funds will automatically bridge to a Railgun-supported chain first, then go through the privacy flow.
91+
92+
### Privacy Considerations
93+
94+
- Adds ~5-15 minutes to transaction time for shielding/unshielding
95+
- Small additional fees for Railgun relayer services
96+
- Uses zero-knowledge proofs - your transaction details remain private
97+
6798
## LI.FI Integration
6899

69100
Sonic and all other source chains are fully integrated with LI.FI for:

src/hooks/usePrivacyRoute.test.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,3 +627,93 @@ describe('Privacy Route Integration', () => {
627627
expect(totalTime).toBe(960); // 16 minutes total
628628
});
629629
});
630+
631+
describe('Sonic Privacy Routing', () => {
632+
// Sonic (chain ID 146) is NOT a Railgun-supported chain.
633+
// When privacy mode is enabled from Sonic, funds must first bridge to a
634+
// Railgun-supported chain (Arbitrum, Polygon, BSC, or Ethereum) before
635+
// going through the privacy flow.
636+
637+
const SONIC_CHAIN_ID = 146;
638+
const RAILGUN_CHAINS = [1, 137, 42161, 56]; // Ethereum, Polygon, Arbitrum, BSC
639+
640+
it('should recognize Sonic is NOT a Railgun-supported chain', async () => {
641+
const railgunService = await import('../services/railgun');
642+
643+
expect(railgunService.isRailgunSupported(SONIC_CHAIN_ID)).toBe(false);
644+
});
645+
646+
it('should recognize all Railgun-supported chains', async () => {
647+
const railgunService = await import('../services/railgun');
648+
649+
RAILGUN_CHAINS.forEach(chainId => {
650+
expect(railgunService.isRailgunSupported(chainId)).toBe(true);
651+
});
652+
});
653+
654+
it('should route Sonic to a Railgun chain for privacy (defaults to Arbitrum)', async () => {
655+
const railgunService = await import('../services/railgun');
656+
657+
// When source chain (Sonic) doesn't support Railgun, it should pick a default
658+
const recommendedChain = railgunService.getBestRailgunChain(SONIC_CHAIN_ID);
659+
660+
// Should return Arbitrum (42161) as the default Railgun chain
661+
expect(recommendedChain).toBe(42161);
662+
expect(RAILGUN_CHAINS).toContain(recommendedChain);
663+
});
664+
665+
it('should use source chain if it IS a Railgun chain', async () => {
666+
const railgunService = await import('../services/railgun');
667+
668+
// If user starts from Arbitrum, use Arbitrum for privacy
669+
const fromArbitrum = railgunService.getBestRailgunChain(42161);
670+
expect(fromArbitrum).toBe(42161);
671+
672+
// If user starts from Polygon, use Polygon for privacy
673+
vi.mocked(railgunService.getBestRailgunChain).mockImplementationOnce((chainId) => {
674+
if (chainId && [1, 137, 42161, 56].includes(chainId)) return chainId as any;
675+
return 42161;
676+
});
677+
const fromPolygon = railgunService.getBestRailgunChain(137);
678+
expect(fromPolygon).toBe(137);
679+
});
680+
681+
it('should include bridge_to_railgun step when starting from non-Railgun chain like Sonic', async () => {
682+
const railgunService = await import('../services/railgun');
683+
const steps = railgunService.getPrivacySteps();
684+
685+
// The first step should be bridge_to_railgun for non-Railgun source chains
686+
const bridgeStep = steps.find(s => s.id === 'bridge_to_railgun');
687+
expect(bridgeStep).toBeDefined();
688+
expect(bridgeStep?.label).toBe('Bridge to Privacy Chain');
689+
expect(bridgeStep?.description).toContain('Railgun');
690+
});
691+
692+
it('should support privacy mode for Sonic users via bridging', async () => {
693+
// This test verifies the full flow works for Sonic users:
694+
// 1. User is on Sonic (146)
695+
// 2. Privacy mode bridges to Arbitrum (42161)
696+
// 3. Funds are shielded on Arbitrum
697+
// 4. After wait period, unshield to new address
698+
// 5. Bridge to Hyperliquid (999)
699+
700+
const railgunService = await import('../services/railgun');
701+
702+
// Verify Sonic is not directly supported
703+
expect(railgunService.isRailgunSupported(SONIC_CHAIN_ID)).toBe(false);
704+
705+
// Verify we get a valid Railgun chain for the privacy flow
706+
const privacyChainId = railgunService.getBestRailgunChain(SONIC_CHAIN_ID);
707+
expect(railgunService.isRailgunSupported(privacyChainId)).toBe(true);
708+
709+
// Verify we can get a privacy quote for that chain
710+
const quote = railgunService.getPrivacyQuote(privacyChainId as any);
711+
expect(quote).toBeDefined();
712+
expect(quote.chainId).toBe(privacyChainId);
713+
expect(parseFloat(quote.totalFeeUSD)).toBeGreaterThan(0);
714+
715+
// Verify the chain name is correct
716+
const chainName = railgunService.getRailgunChainName(privacyChainId as any);
717+
expect(chainName).toBe('Arbitrum');
718+
});
719+
});

0 commit comments

Comments
 (0)