1+ /**
2+ * Tests for cross-chain payment functionality
3+ */
4+
5+ import {
6+ CrossChainTransferRequest ,
7+ RequestType ,
8+ SVMNetwork ,
9+ EVMNetwork ,
10+ BridgeQuote ,
11+ BridgeTransferStatus ,
12+ PaymentStatus
13+ } from '../../src/core/types' ;
14+ import { CrossChainPaymentManager , CrossChainRequestFactory } from '../../src/core/cross-chain' ;
15+ import { WormholeBridgeAdapter } from '../../src/bridge/wormhole' ;
16+ import { AllbridgeBridgeAdapter } from '../../src/bridge/allbridge' ;
17+ import { BridgeAdapterFactory } from '../../src/bridge/adapter' ;
18+ import { getBestBridgeQuote , validateCrossChainRequest } from '../../src/bridge/utils' ;
19+
20+ describe ( 'Cross-Chain Payment Functionality' , ( ) => {
21+ let wormholeAdapter : WormholeBridgeAdapter ;
22+ let allbridgeAdapter : AllbridgeBridgeAdapter ;
23+ let paymentManager : CrossChainPaymentManager ;
24+
25+ beforeEach ( ( ) => {
26+ // Clear any existing adapters
27+ BridgeAdapterFactory [ 'adapters' ] . clear ( ) ;
28+
29+ // Create bridge adapters
30+ wormholeAdapter = new WormholeBridgeAdapter ( ) ;
31+ allbridgeAdapter = new AllbridgeBridgeAdapter ( ) ;
32+
33+ // Register adapters
34+ BridgeAdapterFactory . registerAdapter ( wormholeAdapter ) ;
35+ BridgeAdapterFactory . registerAdapter ( allbridgeAdapter ) ;
36+
37+ // Create payment manager
38+ paymentManager = new CrossChainPaymentManager ( ) ;
39+ } ) ;
40+
41+ describe ( 'CrossChainRequestFactory' , ( ) => {
42+ it ( 'should create a valid cross-chain transfer request' , ( ) => {
43+ const request = CrossChainRequestFactory . createTransferRequest ( {
44+ sourceNetwork : EVMNetwork . ETHEREUM ,
45+ destinationNetwork : SVMNetwork . SOLANA ,
46+ recipient : 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263' ,
47+ amount : '100' ,
48+ token : '0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F' , // USDC on Ethereum
49+ bridge : 'wormhole' ,
50+ label : 'Test Payment' ,
51+ message : 'Cross-chain test' ,
52+ memo : 'test-memo'
53+ } ) ;
54+
55+ expect ( request . type ) . toBe ( RequestType . CROSS_CHAIN_TRANSFER ) ;
56+ expect ( request . sourceNetwork ) . toBe ( EVMNetwork . ETHEREUM ) ;
57+ expect ( request . destinationNetwork ) . toBe ( SVMNetwork . SOLANA ) ;
58+ expect ( request . amount ) . toBe ( '100' ) ;
59+ expect ( request . token ) . toBe ( '0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F' ) ;
60+ expect ( request . bridge ) . toBe ( 'wormhole' ) ;
61+ } ) ;
62+ } ) ;
63+
64+ describe ( 'Bridge Adapters' , ( ) => {
65+ describe ( 'WormholeBridgeAdapter' , ( ) => {
66+ it ( 'should support transfer from Ethereum to Solana for USDC' , ( ) => {
67+ const supported = wormholeAdapter . supportsTransfer (
68+ EVMNetwork . ETHEREUM ,
69+ SVMNetwork . SOLANA ,
70+ '0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F' // USDC
71+ ) ;
72+ expect ( supported ) . toBe ( true ) ;
73+ } ) ;
74+
75+ it ( 'should not support unsupported token' , ( ) => {
76+ const supported = wormholeAdapter . supportsTransfer (
77+ EVMNetwork . ETHEREUM ,
78+ SVMNetwork . SOLANA ,
79+ '0x1234567890123456789012345678901234567890' // Random token
80+ ) ;
81+ expect ( supported ) . toBe ( false ) ;
82+ } ) ;
83+
84+ it ( 'should generate a valid quote' , async ( ) => {
85+ const request : CrossChainTransferRequest = {
86+ type : RequestType . CROSS_CHAIN_TRANSFER ,
87+ network : SVMNetwork . SOLANA ,
88+ sourceNetwork : EVMNetwork . ETHEREUM ,
89+ destinationNetwork : SVMNetwork . SOLANA ,
90+ recipient : 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263' ,
91+ amount : '100' ,
92+ token : '0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F'
93+ } ;
94+
95+ const quote = await wormholeAdapter . quote ( request ) ;
96+
97+ expect ( quote . id ) . toContain ( 'wormhole-' ) ;
98+ expect ( quote . inputAmount ) . toBe ( '100' ) ;
99+ expect ( parseFloat ( quote . outputAmount ) ) . toBeLessThan ( 100 ) ; // Due to fees
100+ expect ( parseFloat ( quote . fee ) ) . toBeGreaterThan ( 0 ) ;
101+ expect ( quote . estimatedTime ) . toBe ( 300 ) ; // 5 minutes
102+ } ) ;
103+ } ) ;
104+
105+ describe ( 'AllbridgeBridgeAdapter' , ( ) => {
106+ it ( 'should support transfer from Ethereum to Solana for USDC' , ( ) => {
107+ const supported = allbridgeAdapter . supportsTransfer (
108+ EVMNetwork . ETHEREUM ,
109+ SVMNetwork . SOLANA ,
110+ '0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F' // USDC
111+ ) ;
112+ expect ( supported ) . toBe ( true ) ;
113+ } ) ;
114+
115+ it ( 'should generate a valid quote with lower fees than Wormhole' , async ( ) => {
116+ const request : CrossChainTransferRequest = {
117+ type : RequestType . CROSS_CHAIN_TRANSFER ,
118+ network : SVMNetwork . SOLANA ,
119+ sourceNetwork : EVMNetwork . ETHEREUM ,
120+ destinationNetwork : SVMNetwork . SOLANA ,
121+ recipient : 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263' ,
122+ amount : '100' ,
123+ token : '0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F'
124+ } ;
125+
126+ const [ wormholeQuote , allbridgeQuote ] = await Promise . all ( [
127+ wormholeAdapter . quote ( request ) ,
128+ allbridgeAdapter . quote ( request )
129+ ] ) ;
130+
131+ // Allbridge should have lower fees
132+ expect ( parseFloat ( allbridgeQuote . fee ) ) . toBeLessThan ( parseFloat ( wormholeQuote . fee ) ) ;
133+ expect ( allbridgeQuote . estimatedTime ) . toBeLessThan ( wormholeQuote . estimatedTime ) ;
134+ } ) ;
135+ } ) ;
136+ } ) ;
137+
138+ describe ( 'BridgeAdapterFactory' , ( ) => {
139+ it ( 'should find compatible adapters for a transfer' , ( ) => {
140+ const compatibleAdapters = BridgeAdapterFactory . findCompatibleAdapters (
141+ EVMNetwork . ETHEREUM ,
142+ SVMNetwork . SOLANA ,
143+ '0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F' // USDC
144+ ) ;
145+
146+ expect ( compatibleAdapters ) . toHaveLength ( 2 ) ; // Wormhole and Allbridge
147+ expect ( compatibleAdapters . map ( a => a . info . id ) ) . toContain ( 'wormhole' ) ;
148+ expect ( compatibleAdapters . map ( a => a . info . id ) ) . toContain ( 'allbridge' ) ;
149+ } ) ;
150+
151+ it ( 'should return empty array for unsupported transfers' , ( ) => {
152+ const compatibleAdapters = BridgeAdapterFactory . findCompatibleAdapters (
153+ EVMNetwork . ETHEREUM ,
154+ SVMNetwork . SOLANA ,
155+ '0x1234567890123456789012345678901234567890' // Unsupported token
156+ ) ;
157+
158+ expect ( compatibleAdapters ) . toHaveLength ( 0 ) ;
159+ } ) ;
160+ } ) ;
161+
162+ describe ( 'Bridge Utils' , ( ) => {
163+ it ( 'should validate a valid cross-chain request' , ( ) => {
164+ const request : CrossChainTransferRequest = {
165+ type : RequestType . CROSS_CHAIN_TRANSFER ,
166+ network : SVMNetwork . SOLANA ,
167+ sourceNetwork : EVMNetwork . ETHEREUM ,
168+ destinationNetwork : SVMNetwork . SOLANA ,
169+ recipient : 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263' ,
170+ amount : '100' ,
171+ token : '0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F'
172+ } ;
173+
174+ expect ( ( ) => validateCrossChainRequest ( request ) ) . not . toThrow ( ) ;
175+ } ) ;
176+
177+ it ( 'should throw for invalid cross-chain request' , ( ) => {
178+ const request : CrossChainTransferRequest = {
179+ type : RequestType . CROSS_CHAIN_TRANSFER ,
180+ network : SVMNetwork . SOLANA ,
181+ sourceNetwork : SVMNetwork . SOLANA , // Same as destination
182+ destinationNetwork : SVMNetwork . SOLANA ,
183+ recipient : 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263' ,
184+ amount : '100' ,
185+ token : '0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F'
186+ } ;
187+
188+ expect ( ( ) => validateCrossChainRequest ( request ) ) . toThrow ( 'Source and destination networks cannot be the same' ) ;
189+ } ) ;
190+
191+ it ( 'should get the best bridge quote' , async ( ) => {
192+ const request : CrossChainTransferRequest = {
193+ type : RequestType . CROSS_CHAIN_TRANSFER ,
194+ network : SVMNetwork . SOLANA ,
195+ sourceNetwork : EVMNetwork . ETHEREUM ,
196+ destinationNetwork : SVMNetwork . SOLANA ,
197+ recipient : 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263' ,
198+ amount : '100' ,
199+ token : '0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F'
200+ } ;
201+
202+ const result = await getBestBridgeQuote ( request ) ;
203+
204+ expect ( result ) . not . toBeNull ( ) ;
205+ expect ( result ?. quote ) . toBeDefined ( ) ;
206+ expect ( result ?. adapter ) . toBeDefined ( ) ;
207+
208+ // Should select Allbridge due to lower fees and faster time
209+ expect ( result ?. adapter . info . id ) . toBe ( 'allbridge' ) ;
210+ } ) ;
211+ } ) ;
212+
213+ describe ( 'CrossChainPaymentManager' , ( ) => {
214+ it ( 'should execute a cross-chain payment' , async ( ) => {
215+ const request : CrossChainTransferRequest = {
216+ type : RequestType . CROSS_CHAIN_TRANSFER ,
217+ network : SVMNetwork . SOLANA ,
218+ sourceNetwork : EVMNetwork . ETHEREUM ,
219+ destinationNetwork : SVMNetwork . SOLANA ,
220+ recipient : 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263' ,
221+ amount : '100' ,
222+ token : '0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F'
223+ } ;
224+
225+ const result = await paymentManager . executePayment ( request ) ;
226+
227+ expect ( result . paymentId ) . toContain ( 'cc-payment-' ) ;
228+ expect ( result . bridge . info . id ) . toBe ( 'allbridge' ) ; // Should select best bridge
229+ expect ( result . status ) . toBe ( PaymentStatus . BRIDGING ) ;
230+ expect ( result . bridgeResult . transferId ) . toMatch ( / a l l b r i d g e - ( t r a n s f e r | f a l l b a c k ) - / ) ; // Handle both real and fallback transfers
231+ } ) ;
232+
233+ it ( 'should get payment status' , async ( ) => {
234+ const request : CrossChainTransferRequest = {
235+ type : RequestType . CROSS_CHAIN_TRANSFER ,
236+ network : SVMNetwork . SOLANA ,
237+ sourceNetwork : EVMNetwork . ETHEREUM ,
238+ destinationNetwork : SVMNetwork . SOLANA ,
239+ recipient : 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263' ,
240+ amount : '100' ,
241+ token : '0xa0B86a33E6441c4D0c85C81a1a4E18A3f3f3F77F'
242+ } ;
243+
244+ const result = await paymentManager . executePayment ( request ) ;
245+ const status = await paymentManager . getPaymentStatus ( result . paymentId ) ;
246+
247+ expect ( status ) . not . toBeNull ( ) ;
248+ expect ( status ?. id ) . toBe ( result . paymentId ) ;
249+ expect ( status ?. status ) . toBe ( PaymentStatus . BRIDGING ) ;
250+ expect ( status ?. bridgeUsed ) . toBe ( 'allbridge' ) ;
251+ } ) ;
252+ } ) ;
253+
254+ describe ( 'Negative Test Cases' , ( ) => {
255+ describe ( 'Unsupported Token Quotes' , ( ) => {
256+ it ( 'should throw error for unsupported token in Wormhole' , async ( ) => {
257+ const request : CrossChainTransferRequest = {
258+ type : RequestType . CROSS_CHAIN_TRANSFER ,
259+ network : SVMNetwork . SOLANA ,
260+ sourceNetwork : EVMNetwork . ETHEREUM ,
261+ destinationNetwork : SVMNetwork . SOLANA ,
262+ recipient : 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263' ,
263+ amount : '100' ,
264+ token : '0x1234567890123456789012345678901234567890' // Unsupported token
265+ } ;
266+
267+ await expect ( wormholeAdapter . quote ( request ) ) . rejects . toThrow (
268+ 'Wormhole does not support transfer from ethereum to solana for token 0x1234567890123456789012345678901234567890'
269+ ) ;
270+ } ) ;
271+
272+ it ( 'should throw error for unsupported token in Allbridge' , async ( ) => {
273+ const request : CrossChainTransferRequest = {
274+ type : RequestType . CROSS_CHAIN_TRANSFER ,
275+ network : SVMNetwork . SOLANA ,
276+ sourceNetwork : EVMNetwork . ETHEREUM ,
277+ destinationNetwork : SVMNetwork . SOLANA ,
278+ recipient : 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263' ,
279+ amount : '100' ,
280+ token : '0x9876543210987654321098765432109876543210' // Unsupported token
281+ } ;
282+
283+ await expect ( allbridgeAdapter . quote ( request ) ) . rejects . toThrow (
284+ 'Allbridge does not support transfer from ethereum to solana for token 0x9876543210987654321098765432109876543210'
285+ ) ;
286+ } ) ;
287+
288+ it ( 'should return null from getBestBridgeQuote for unsupported token' , async ( ) => {
289+ const request : CrossChainTransferRequest = {
290+ type : RequestType . CROSS_CHAIN_TRANSFER ,
291+ network : SVMNetwork . SOLANA ,
292+ sourceNetwork : EVMNetwork . ETHEREUM ,
293+ destinationNetwork : SVMNetwork . SOLANA ,
294+ recipient : 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263' ,
295+ amount : '100' ,
296+ token : '0x1111111111111111111111111111111111111111' // Unsupported token
297+ } ;
298+
299+ const result = await getBestBridgeQuote ( request ) ;
300+ expect ( result ) . toBeNull ( ) ;
301+ } ) ;
302+
303+ it ( 'should throw error when no compatible bridges found' , async ( ) => {
304+ const request : CrossChainTransferRequest = {
305+ type : RequestType . CROSS_CHAIN_TRANSFER ,
306+ network : SVMNetwork . SOLANA ,
307+ sourceNetwork : EVMNetwork . ETHEREUM ,
308+ destinationNetwork : SVMNetwork . SOLANA ,
309+ recipient : 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263' ,
310+ amount : '100' ,
311+ token : '0x2222222222222222222222222222222222222222' // Unsupported token
312+ } ;
313+
314+ await expect ( paymentManager . executePayment ( request ) ) . rejects . toThrow (
315+ 'No compatible bridges found for this transfer'
316+ ) ;
317+ } ) ;
318+ } ) ;
319+ } ) ;
320+ } ) ;
0 commit comments