@@ -15,6 +15,8 @@ import { useChain } from 'starshipjs';
1515import { Comet38Adapter } from '@interchainjs/cosmos/adapters' ;
1616import { MsgSend } from 'interchainjs/cosmos/bank/v1beta1/tx' ;
1717import { getAllBalances , MsgTransfer , transfer } from 'interchainjs' ;
18+ // @ts -ignore
19+ import WebSocket from 'ws' ;
1820
1921const cosmosHdPath = "m/44'/118'/0'/0/0" ;
2022
@@ -122,6 +124,166 @@ describe('Token transfers', () => {
122124 }
123125 } , 100000 ) ;
124126
127+ it ( 'monitor osmosis token transfer via WebSocket events' , async ( ) => {
128+ // Initialize the signer for this test (similar to direct mode test)
129+ signer = new DirectSigner ( protoSigner , {
130+ queryClient : client ,
131+ chainId : 'osmosis-1' ,
132+ addressPrefix : commonPrefix
133+ } ) ;
134+
135+ // Convert HTTP RPC endpoint to WebSocket endpoint
136+ const rpcEndpoint = await getRpcEndpoint ( ) ;
137+ const wsEndpoint = rpcEndpoint . replace ( 'http' , 'ws' ) + '/websocket' ;
138+
139+ // Use raw WebSocket approach (similar to the old test) to work around WebSocket client issues
140+ const ws = new WebSocket ( wsEndpoint ) ;
141+
142+ let eventReceived = false ;
143+ let receivedEvent : any = null ;
144+
145+ // Set up WebSocket connection and subscription
146+ const wsConnectionPromise = new Promise < void > ( ( resolve , reject ) => {
147+ const timeoutId = setTimeout ( ( ) => {
148+ reject ( new Error ( 'Timeout waiting for WebSocket connection' ) ) ;
149+ } , 10000 ) ;
150+
151+ ws . on ( 'open' , ( ) => {
152+ clearTimeout ( timeoutId ) ;
153+ console . log ( 'Raw WebSocket connected' ) ;
154+
155+ // Subscribe to Tx events for the recipient address
156+ const subscribeMsg = {
157+ jsonrpc : '2.0' ,
158+ id : 'tx-subscribe' ,
159+ method : 'subscribe' ,
160+ params : {
161+ query : `tm.event='Tx' AND transfer.recipient='${ address2 } '`
162+ }
163+ } ;
164+
165+ console . log ( 'Sending subscription message:' , JSON . stringify ( subscribeMsg ) ) ;
166+ ws . send ( JSON . stringify ( subscribeMsg ) ) ;
167+
168+ // Wait a bit for subscription to be processed, then resolve
169+ setTimeout ( ( ) => {
170+ resolve ( ) ;
171+ } , 1000 ) ;
172+ } ) ;
173+
174+ ws . on ( 'error' , ( error ) => {
175+ clearTimeout ( timeoutId ) ;
176+ console . error ( 'WebSocket error:' , error ) ;
177+ reject ( error ) ;
178+ } ) ;
179+ } ) ;
180+
181+ // Set up message handler for subscription events
182+ ws . on ( 'message' , ( data ) => {
183+ try {
184+ const response = JSON . parse ( data . toString ( ) ) ;
185+ console . log ( 'Received WebSocket message:' , response ) ;
186+
187+ // Check if this is a subscription event with transaction data
188+ if ( response . result && response . result . events && ! eventReceived ) {
189+ console . log ( 'Found transaction event!' ) ;
190+ eventReceived = true ;
191+ receivedEvent = response ;
192+ }
193+ } catch ( error ) {
194+ console . error ( 'Error parsing WebSocket message:' , error ) ;
195+ }
196+ } ) ;
197+
198+ // Wait for WebSocket to be ready and subscription to be set up
199+ await wsConnectionPromise ;
200+
201+ // Setup fees and token for the transaction
202+ const fee = {
203+ amount : [
204+ {
205+ denom,
206+ amount : '100000' ,
207+ } ,
208+ ] ,
209+ gas : '550000' ,
210+ } ;
211+
212+ const token = {
213+ amount : '5000000' , // Different amount to distinguish from other tests
214+ denom,
215+ } ;
216+
217+ try {
218+ // Send the transaction using the existing signer
219+ console . log ( 'Sending transaction from' , address , 'to' , address2 , 'amount:' , token . amount ) ;
220+ const msgSend = MsgSend . fromPartial ( {
221+ fromAddress : address ,
222+ toAddress : address2 ,
223+ amount : [ token ]
224+ } ) ;
225+
226+ const result = await send ( signer , address , msgSend , fee , 'websocket test transaction' ) ;
227+ console . log ( 'Transaction sent, waiting for event...' ) ;
228+
229+ // Wait for the WebSocket event (with a timeout)
230+ const txEventPromise = new Promise < any > ( ( resolve ) => {
231+ const checkInterval = setInterval ( ( ) => {
232+ if ( eventReceived ) {
233+ clearInterval ( checkInterval ) ;
234+ resolve ( receivedEvent ) ;
235+ }
236+ } , 100 ) ;
237+
238+ // Timeout after 20 seconds
239+ setTimeout ( ( ) => {
240+ clearInterval ( checkInterval ) ;
241+ resolve ( null ) ;
242+ } , 20000 ) ;
243+ } ) ;
244+
245+ const txEvent = await txEventPromise ;
246+
247+ // Verify WebSocket event was received
248+ expect ( txEvent ) . not . toBeNull ( ) ;
249+ expect ( txEvent ) . toBeDefined ( ) ;
250+
251+ // Verify the event structure (similar to old test)
252+ expect ( txEvent . result ) . toBeDefined ( ) ;
253+ expect ( txEvent . result . events ) . toBeDefined ( ) ;
254+
255+ // Verify transfer event details
256+ const events = txEvent . result . events ;
257+ expect ( events [ 'transfer.recipient' ] ) . toContain ( address2 ) ;
258+ expect ( events [ 'transfer.amount' ] ) . toContain ( `${ token . amount } ${ denom } ` ) ;
259+ expect ( events [ 'tm.event' ] ) . toContain ( 'Tx' ) ;
260+
261+ // Verify the transaction was successful by checking balance
262+ const { balance } = await getBalance ( client , { address : address2 , denom } ) ;
263+
264+ // The balance should include the transferred amount
265+ // Note: We can't predict the exact final balance since other tests may have run
266+ // but we can verify the balance is at least the amount we transferred
267+ expect ( BigInt ( balance ! . amount ) ) . toBeGreaterThanOrEqual ( BigInt ( token . amount ) ) ;
268+ expect ( balance ! . denom ) . toEqual ( denom ) ;
269+
270+ } catch ( error ) {
271+ console . error ( 'Error in WebSocket monitoring test:' , error ) ;
272+ throw error ;
273+ } finally {
274+ // Clean up the WebSocket connection
275+ try {
276+ if ( ws . readyState === WebSocket . OPEN || ws . readyState === WebSocket . CONNECTING ) {
277+ ws . close ( ) ;
278+ }
279+ // Wait a bit for the connection to close
280+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
281+ } catch ( cleanupError ) {
282+ console . warn ( 'Error during cleanup:' , cleanupError ) ;
283+ }
284+ }
285+ } , 30000 ) ;
286+
125287 it ( 'amino mode: send osmosis token to address' , async ( ) => {
126288 // Use wallet.toOfflineAminoSigner() to get proper offline signer with cached account data
127289 const aminoOfflineSigner = await wallet . toOfflineAminoSigner ( ) ;
0 commit comments