Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
cf7a42e
Initial plan
Copilot Jul 3, 2025
444acab
Initial plan for cross-chain payments implementation
Copilot Jul 3, 2025
a384441
Implement comprehensive cross-chain payment functionality
Copilot Jul 3, 2025
bdd5864
Fix code review issues: fee scoring, monitoring loop, event handling,…
Copilot Jul 5, 2025
c23aad2
Implement comprehensive code review improvements: address validation,…
Copilot Jul 5, 2025
3180d29
Transform landing page into comprehensive developer onboarding experi…
Copilot Jul 5, 2025
75ac479
Fix Netlify build error by replacing template literals with safer str…
Copilot Jul 5, 2025
f64cb50
Fix Netlify build errors: resolve WalletContext and SVMPay reference …
Copilot Jul 5, 2025
2a9a325
Complete rebuild of svm-pay.com from scratch with top-notch quality
Copilot Jul 5, 2025
06f85b6
Fix CSS missing issue and update Netlify config for proper deployment
Copilot Jul 5, 2025
142e129
Fix Netlify deploy path configuration
Copilot Jul 5, 2025
93097ea
Add comprehensive docs and demo pages with navigation and routing
Copilot Jul 5, 2025
6b93758
Implement all footer pages: examples, pricing, community, blog, chang…
Copilot Jul 5, 2025
77fcefa
Fix SPA routing: add Netlify redirects for React Router
Copilot Jul 5, 2025
59c7dda
Add comprehensive tutorials with 20+ real-world scenarios across all …
Copilot Jul 5, 2025
45ff0b6
Implement individual tutorial pages with copy functionality and compr…
Copilot Jul 5, 2025
ac74ce3
Fix TypeScript build errors: remove unused React and ReactNode imports
Copilot Jul 5, 2025
182a493
Remove mocks and implement real bridge API integrations with fallback…
Copilot Jul 5, 2025
57e6f1a
Complete comprehensive tutorials with syntax highlighting and 15+ ind…
Copilot Jul 5, 2025
bc50774
Add DeFi and Social tutorial categories with advanced examples
Copilot Jul 5, 2025
7f0de48
Complete missing tutorials: NFT Marketplace, NFT Drop Platform, and C…
Copilot Jul 5, 2025
a80da24
Complete tutorial system and team updates: add Mao Mao characters, mi…
Copilot Jul 5, 2025
deed33a
Update team profile pictures to Mao Mao character images with fallbac…
Copilot Jul 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
320 changes: 320 additions & 0 deletions __tests__/bridge/cross-chain.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
/**
* Tests for cross-chain payment functionality
*/

import {
CrossChainTransferRequest,
RequestType,
SVMNetwork,
EVMNetwork,
BridgeQuote,
BridgeTransferStatus,
PaymentStatus
} from '../../src/core/types';
import { CrossChainPaymentManager, CrossChainRequestFactory } from '../../src/core/cross-chain';
import { WormholeBridgeAdapter } from '../../src/bridge/wormhole';
import { AllbridgeBridgeAdapter } from '../../src/bridge/allbridge';
import { BridgeAdapterFactory } from '../../src/bridge/adapter';
import { getBestBridgeQuote, validateCrossChainRequest } from '../../src/bridge/utils';

describe('Cross-Chain Payment Functionality', () => {
let wormholeAdapter: WormholeBridgeAdapter;
let allbridgeAdapter: AllbridgeBridgeAdapter;
let paymentManager: CrossChainPaymentManager;

beforeEach(() => {
// Clear any existing adapters
BridgeAdapterFactory['adapters'].clear();

// Create bridge adapters
wormholeAdapter = new WormholeBridgeAdapter();
allbridgeAdapter = new AllbridgeBridgeAdapter();

// Register adapters
BridgeAdapterFactory.registerAdapter(wormholeAdapter);
BridgeAdapterFactory.registerAdapter(allbridgeAdapter);

// Create payment manager
paymentManager = new CrossChainPaymentManager();
});

describe('CrossChainRequestFactory', () => {
it('should create a valid cross-chain transfer request', () => {
const request = CrossChainRequestFactory.createTransferRequest({
sourceNetwork: EVMNetwork.ETHEREUM,
destinationNetwork: SVMNetwork.SOLANA,
recipient: 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263',
amount: '100',
token: '0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F', // USDC on Ethereum
bridge: 'wormhole',
label: 'Test Payment',
message: 'Cross-chain test',
memo: 'test-memo'
});

expect(request.type).toBe(RequestType.CROSS_CHAIN_TRANSFER);
expect(request.sourceNetwork).toBe(EVMNetwork.ETHEREUM);
expect(request.destinationNetwork).toBe(SVMNetwork.SOLANA);
expect(request.amount).toBe('100');
expect(request.token).toBe('0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F');
expect(request.bridge).toBe('wormhole');
});
});

describe('Bridge Adapters', () => {
describe('WormholeBridgeAdapter', () => {
it('should support transfer from Ethereum to Solana for USDC', () => {
const supported = wormholeAdapter.supportsTransfer(
EVMNetwork.ETHEREUM,
SVMNetwork.SOLANA,
'0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F' // USDC
);
expect(supported).toBe(true);
});

it('should not support unsupported token', () => {
const supported = wormholeAdapter.supportsTransfer(
EVMNetwork.ETHEREUM,
SVMNetwork.SOLANA,
'0x1234567890123456789012345678901234567890' // Random token
);
expect(supported).toBe(false);
});

it('should generate a valid quote', async () => {
const request: CrossChainTransferRequest = {
type: RequestType.CROSS_CHAIN_TRANSFER,
network: SVMNetwork.SOLANA,
sourceNetwork: EVMNetwork.ETHEREUM,
destinationNetwork: SVMNetwork.SOLANA,
recipient: 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263',
amount: '100',
token: '0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F'
};

const quote = await wormholeAdapter.quote(request);

expect(quote.id).toContain('wormhole-');
expect(quote.inputAmount).toBe('100');
expect(parseFloat(quote.outputAmount)).toBeLessThan(100); // Due to fees
expect(parseFloat(quote.fee)).toBeGreaterThan(0);
expect(quote.estimatedTime).toBe(300); // 5 minutes
});
});

describe('AllbridgeBridgeAdapter', () => {
it('should support transfer from Ethereum to Solana for USDC', () => {
const supported = allbridgeAdapter.supportsTransfer(
EVMNetwork.ETHEREUM,
SVMNetwork.SOLANA,
'0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F' // USDC
);
expect(supported).toBe(true);
});

it('should generate a valid quote with lower fees than Wormhole', async () => {
const request: CrossChainTransferRequest = {
type: RequestType.CROSS_CHAIN_TRANSFER,
network: SVMNetwork.SOLANA,
sourceNetwork: EVMNetwork.ETHEREUM,
destinationNetwork: SVMNetwork.SOLANA,
recipient: 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263',
amount: '100',
token: '0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F'
};

const [wormholeQuote, allbridgeQuote] = await Promise.all([
wormholeAdapter.quote(request),
allbridgeAdapter.quote(request)
]);

// Allbridge should have lower fees
expect(parseFloat(allbridgeQuote.fee)).toBeLessThan(parseFloat(wormholeQuote.fee));
expect(allbridgeQuote.estimatedTime).toBeLessThan(wormholeQuote.estimatedTime);
});
});
});

describe('BridgeAdapterFactory', () => {
it('should find compatible adapters for a transfer', () => {
const compatibleAdapters = BridgeAdapterFactory.findCompatibleAdapters(
EVMNetwork.ETHEREUM,
SVMNetwork.SOLANA,
'0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F' // USDC
);

expect(compatibleAdapters).toHaveLength(2); // Wormhole and Allbridge
expect(compatibleAdapters.map(a => a.info.id)).toContain('wormhole');
expect(compatibleAdapters.map(a => a.info.id)).toContain('allbridge');
});

it('should return empty array for unsupported transfers', () => {
const compatibleAdapters = BridgeAdapterFactory.findCompatibleAdapters(
EVMNetwork.ETHEREUM,
SVMNetwork.SOLANA,
'0x1234567890123456789012345678901234567890' // Unsupported token
);

expect(compatibleAdapters).toHaveLength(0);
});
});

describe('Bridge Utils', () => {
it('should validate a valid cross-chain request', () => {
const request: CrossChainTransferRequest = {
type: RequestType.CROSS_CHAIN_TRANSFER,
network: SVMNetwork.SOLANA,
sourceNetwork: EVMNetwork.ETHEREUM,
destinationNetwork: SVMNetwork.SOLANA,
recipient: 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263',
amount: '100',
token: '0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F'
};

expect(() => validateCrossChainRequest(request)).not.toThrow();
});

it('should throw for invalid cross-chain request', () => {
const request: CrossChainTransferRequest = {
type: RequestType.CROSS_CHAIN_TRANSFER,
network: SVMNetwork.SOLANA,
sourceNetwork: SVMNetwork.SOLANA, // Same as destination
destinationNetwork: SVMNetwork.SOLANA,
recipient: 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263',
amount: '100',
token: '0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F'
};

expect(() => validateCrossChainRequest(request)).toThrow('Source and destination networks cannot be the same');
});

it('should get the best bridge quote', async () => {
const request: CrossChainTransferRequest = {
type: RequestType.CROSS_CHAIN_TRANSFER,
network: SVMNetwork.SOLANA,
sourceNetwork: EVMNetwork.ETHEREUM,
destinationNetwork: SVMNetwork.SOLANA,
recipient: 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263',
amount: '100',
token: '0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F'
};

const result = await getBestBridgeQuote(request);

expect(result).not.toBeNull();
expect(result?.quote).toBeDefined();
expect(result?.adapter).toBeDefined();

// Should select Allbridge due to lower fees and faster time
expect(result?.adapter.info.id).toBe('allbridge');
});
});

describe('CrossChainPaymentManager', () => {
it('should execute a cross-chain payment', async () => {
const request: CrossChainTransferRequest = {
type: RequestType.CROSS_CHAIN_TRANSFER,
network: SVMNetwork.SOLANA,
sourceNetwork: EVMNetwork.ETHEREUM,
destinationNetwork: SVMNetwork.SOLANA,
recipient: 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263',
amount: '100',
token: '0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F'
};

const result = await paymentManager.executePayment(request);

expect(result.paymentId).toContain('cc-payment-');
expect(result.bridge.info.id).toBe('allbridge'); // Should select best bridge
expect(result.status).toBe(PaymentStatus.BRIDGING);
expect(result.bridgeResult.transferId).toMatch(/allbridge-(transfer|fallback)-/); // Handle both real and fallback transfers
});

it('should get payment status', async () => {
const request: CrossChainTransferRequest = {
type: RequestType.CROSS_CHAIN_TRANSFER,
network: SVMNetwork.SOLANA,
sourceNetwork: EVMNetwork.ETHEREUM,
destinationNetwork: SVMNetwork.SOLANA,
recipient: 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263',
amount: '100',
token: '0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F'
};

const result = await paymentManager.executePayment(request);
const status = await paymentManager.getPaymentStatus(result.paymentId);

expect(status).not.toBeNull();
expect(status?.id).toBe(result.paymentId);
expect(status?.status).toBe(PaymentStatus.BRIDGING);
expect(status?.bridgeUsed).toBe('allbridge');
});
});

describe('Negative Test Cases', () => {
describe('Unsupported Token Quotes', () => {
it('should throw error for unsupported token in Wormhole', async () => {
const request: CrossChainTransferRequest = {
type: RequestType.CROSS_CHAIN_TRANSFER,
network: SVMNetwork.SOLANA,
sourceNetwork: EVMNetwork.ETHEREUM,
destinationNetwork: SVMNetwork.SOLANA,
recipient: 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263',
amount: '100',
token: '0x1234567890123456789012345678901234567890' // Unsupported token
};

await expect(wormholeAdapter.quote(request)).rejects.toThrow(
'Wormhole does not support transfer from ethereum to solana for token 0x1234567890123456789012345678901234567890'
);
});

it('should throw error for unsupported token in Allbridge', async () => {
const request: CrossChainTransferRequest = {
type: RequestType.CROSS_CHAIN_TRANSFER,
network: SVMNetwork.SOLANA,
sourceNetwork: EVMNetwork.ETHEREUM,
destinationNetwork: SVMNetwork.SOLANA,
recipient: 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263',
amount: '100',
token: '0x9876543210987654321098765432109876543210' // Unsupported token
};

await expect(allbridgeAdapter.quote(request)).rejects.toThrow(
'Allbridge does not support transfer from ethereum to solana for token 0x9876543210987654321098765432109876543210'
);
});

it('should return null from getBestBridgeQuote for unsupported token', async () => {
const request: CrossChainTransferRequest = {
type: RequestType.CROSS_CHAIN_TRANSFER,
network: SVMNetwork.SOLANA,
sourceNetwork: EVMNetwork.ETHEREUM,
destinationNetwork: SVMNetwork.SOLANA,
recipient: 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263',
amount: '100',
token: '0x1111111111111111111111111111111111111111' // Unsupported token
};

const result = await getBestBridgeQuote(request);
expect(result).toBeNull();
});

it('should throw error when no compatible bridges found', async () => {
const request: CrossChainTransferRequest = {
type: RequestType.CROSS_CHAIN_TRANSFER,
network: SVMNetwork.SOLANA,
sourceNetwork: EVMNetwork.ETHEREUM,
destinationNetwork: SVMNetwork.SOLANA,
recipient: 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263',
amount: '100',
token: '0x2222222222222222222222222222222222222222' // Unsupported token
};

await expect(paymentManager.executePayment(request)).rejects.toThrow(
'No compatible bridges found for this transfer'
);
});
});
});
});
Loading
Loading