importasyncioimportjsonimportosimporturllib.requestfromsymbolchain.CryptoTypesimportPrivateKeyfromsymbolchain.facade.SymbolFacadeimportSymbolFacadefromsymbolchain.symbol.NetworkimportAddressfromwebsocketsimportconnectNODE_URL=os.getenv('NODE_URL','https://reference.symboltest.net:3001')WS_URL=NODE_URL.replace('http','ws',1)+'/ws'print(f'Using node {NODE_URL}')defannounce_transaction(payload,endpoint,label):announce_request=urllib.request.Request(f'{NODE_URL}{endpoint}',data=payload.encode(),headers={'Content-Type':'application/json'},method='PUT')withurllib.request.urlopen(announce_request)asresponse:response.read()print(label)MONITOR_ADDRESS=os.getenv('MONITOR_ADDRESS','TCHBDENCLKEBILBPWP3JPB2XNY64OE7PYHHE32I')print(f'Monitoring address: {MONITOR_ADDRESS}')SIGNER_PRIVATE_KEY=os.getenv('SIGNER_PRIVATE_KEY','0000000000000000000000000000000000000000000000000000000000000000')facade=SymbolFacade('testnet')signer_key_pair=SymbolFacade.KeyPair(PrivateKey(SIGNER_PRIVATE_KEY))asyncdefmain():asyncwithconnect(WS_URL)aswebsocket:# Connect to WebSocketresponse=json.loads(awaitwebsocket.recv())uid=response['uid']print(f'Connected to {WS_URL} with uid {uid}')# Subscribe to transaction channelschannels=[f'unconfirmedAdded/{MONITOR_ADDRESS}',f'unconfirmedRemoved/{MONITOR_ADDRESS}',f'confirmedAdded/{MONITOR_ADDRESS}',]forchannelinchannels:awaitwebsocket.send(json.dumps({'uid':uid,'subscribe':channel}))name=channel.split('/')[0]print(f'Subscribed to {name} channel')# Build and announce a transfer transactionwithurllib.request.urlopen(f'{NODE_URL}/network/fees/transaction')asresp:fee_json=json.loads(resp.read().decode())fee_multiplier=max(fee_json['medianFeeMultiplier'],fee_json['minFeeMultiplier'])transaction=facade.create_transaction_from_descriptor({'type':'transfer_transaction_v1','recipient_address':Address(MONITOR_ADDRESS)},signer_key_pair.public_key,fee_multiplier,2*60*60)signature=facade.sign_transaction(signer_key_pair,transaction)json_payload=facade.transaction_factory.attach_signature(transaction,signature)transaction_hash=str(facade.hash_transaction(transaction))announce_transaction(json_payload,'/transactions',f'Announced transaction {transaction_hash[:16]}...')# Wait for confirmation via WebSocketasyncforraw_messageinwebsocket:message=json.loads(raw_message)topic=message['topic']message_hash=message['data']['meta']['hash']name=topic.split('/')[0]print(f'{name}: hash={message_hash[:16]}...')if(name=='confirmedAdded'andmessage_hash==transaction_hash):print(f'Transaction {transaction_hash[:16]}... confirmed')break# Unsubscribe before closingforchannelinchannels:awaitwebsocket.send(json.dumps({'uid':uid,'unsubscribe':channel}))print('Unsubscribed from all channels')try:asyncio.run(main())exceptExceptionaserror:print(error)
import{PrivateKey}from'symbol-sdk';import{SymbolFacade,descriptors}from'symbol-sdk/symbol';constNODE_URL=process.env.NODE_URL||'https://reference.symboltest.net:3001';constWS_URL=`${NODE_URL.replace('http','ws')}/ws`;console.log(`Using node ${NODE_URL}`);asyncfunctionannounceTransaction(payload,endpoint,label){awaitfetch(`${NODE_URL}${endpoint}`,{method:'PUT',headers:{'Content-Type':'application/json'},body:payload});console.log(label);}constMONITOR_ADDRESS=process.env.MONITOR_ADDRESS||'TCHBDENCLKEBILBPWP3JPB2XNY64OE7PYHHE32I';console.log(`Monitoring address: ${MONITOR_ADDRESS}`);constSIGNER_PRIVATE_KEY=process.env.SIGNER_PRIVATE_KEY||'0000000000000000000000000000000000000000000000000000000000000000';constfacade=newSymbolFacade('testnet');constsignerKeyPair=newSymbolFacade.KeyPair(newPrivateKey(SIGNER_PRIVATE_KEY));try{// Connect to WebSocketconstwebsocket=newWebSocket(WS_URL);constuid=awaitnewPromise(resolve=>{websocket.addEventListener('message',event=>{constmessage=JSON.parse(event.data);resolve(message.uid);},{once:true});});console.log(`Connected to ${WS_URL} with uid ${uid}`);// Subscribe to transaction channelsconstchannels=[`unconfirmedAdded/${MONITOR_ADDRESS}`,`unconfirmedRemoved/${MONITOR_ADDRESS}`,`confirmedAdded/${MONITOR_ADDRESS}`];for(constchannelofchannels){websocket.send(JSON.stringify({uid,subscribe:channel}));constname=channel.split('/')[0];console.log(`Subscribed to ${name} channel`);}// Build and announce a transfer transactionconstfeeResponse=awaitfetch(`${NODE_URL}/network/fees/transaction`);constfeeJSON=awaitfeeResponse.json();constfeeMultiplier=Math.max(feeJSON.medianFeeMultiplier,feeJSON.minFeeMultiplier);consttransaction=facade.createTransactionFromTypedDescriptor(newdescriptors.TransferTransactionV1Descriptor(newSymbolFacade.Address(MONITOR_ADDRESS),undefined,undefined),signerKeyPair.publicKey,feeMultiplier,2*60*60);constsignature=facade.signTransaction(signerKeyPair,transaction);constjsonPayload=facade.transactionFactory.static.attachSignature(transaction,signature);consttransactionHash=facade.hashTransaction(transaction).toString();constconfirmed=newPromise(resolve=>{websocket.addEventListener('message',event=>{constmessage=JSON.parse(event.data);consttopic=message.topic;constmessageHash=message.data.meta.hash;constname=topic.split('/')[0];console.log(`${name}: hash=${messageHash.substring(0,16)}...`);if('confirmedAdded'===name&&messageHash===transactionHash){console.log(`Transaction ${transactionHash.substring(0,16)}`+'... confirmed');resolve();}});});awaitannounceTransaction(jsonPayload,'/transactions',`Announced transaction ${transactionHash.substring(0,16)}...`);// Wait for confirmation via WebSocketawaitconfirmed;// Unsubscribe before closingfor(constchannelofchannels)websocket.send(JSON.stringify({uid,unsubscribe:channel}));console.log('Unsubscribed from all channels');websocket.close();}catch(error){console.error(error);}
//JAVA 21+//DEPS org.symbol:symbol-sdk:3.3.1//DEPS org.glassfish.tyrus.bundles:tyrus-standalone-client:2.2.0importjava.io.IOException;importjava.net.URI;importjava.net.http.HttpClient;importjava.net.http.HttpRequest;importjava.net.http.HttpResponse;importjava.net.http.HttpResponse.BodyHandlers;importjava.util.List;importjava.util.concurrent.CompletableFuture;importcom.fasterxml.jackson.databind.JsonNode;importcom.fasterxml.jackson.databind.ObjectMapper;importjakarta.websocket.ClientEndpoint;importjakarta.websocket.ContainerProvider;importjakarta.websocket.OnMessage;importjakarta.websocket.RemoteEndpoint;importjakarta.websocket.Session;importjakarta.websocket.WebSocketContainer;importorg.symbol.sdk.CryptoTypes;importorg.symbol.sdk.facade.SymbolFacade;importorg.symbol.sdk.symbol.Address;importorg.symbol.sdk.symbol.KeyPair;importorg.symbol.sdk.symbol.SymbolTransactionFactory;importorg.symbol.sdk.symbol.descriptors.*;importorg.symbol.sdk.symbol.models.*;@ClientEndpointpublicfinalclassListenTransactionFlow{privatestaticfinalObjectMapperJSON_MAPPER=newObjectMapper();privatestaticfinalHttpClientHTTP_CLIENT=HttpClient.newHttpClient();privatefinalCompletableFuture<String>uidFuture=newCompletableFuture<>();privatefinalCompletableFuture<Void>confirmed=newCompletableFuture<>();privatefinalStringnodeUrl=System.getenv().getOrDefault("NODE_URL","https://reference.symboltest.net:3001");privatefinalStringwsUrl=nodeUrl.replaceFirst("http","ws")+"/ws";privatefinalSymbolFacadefacade=newSymbolFacade("testnet");privateStringtransactionHash;publicstaticvoidmain(finalString[]args){try{newListenTransactionFlow().run();}catch(finalExceptionex){System.out.println(null==ex.getMessage()?ex.toString():ex.getMessage());}}privatevoidrun()throwsException{System.out.printf("Using node %s%n",nodeUrl);finalStringmonitorAddress=System.getenv().getOrDefault("MONITOR_ADDRESS","TCHBDENCLKEBILBPWP3JPB2XNY64OE7PYHHE32I");System.out.printf("Monitoring address: %s%n",monitorAddress);finalStringsignerPrivateKey=System.getenv().getOrDefault("SIGNER_PRIVATE_KEY","0".repeat(64));finalKeyPairsignerKeyPair=newKeyPair(newCryptoTypes.PrivateKey(signerPrivateKey));// Connect to WebSocketfinalWebSocketContainercontainer=ContainerProvider.getWebSocketContainer();finalSessionsession=container.connectToServer(this,URI.create(wsUrl));finalRemoteEndpoint.Basicremote=session.getBasicRemote();finalStringuid=uidFuture.join();System.out.printf("Connected to %s with uid %s%n",wsUrl,uid);// Subscribe to transaction channelsfinalList<String>channels=List.of("unconfirmedAdded/"+monitorAddress,"unconfirmedRemoved/"+monitorAddress,"confirmedAdded/"+monitorAddress);for(finalStringchannel:channels){remote.sendText(JSON_MAPPER.createObjectNode().put("uid",uid).put("subscribe",channel).toString());System.out.printf("Subscribed to %s channel%n",channel.split("/")[0]);}// Build and announce a transfer transactionfinalJsonNodefeeJSON=getJson("/network/fees/transaction");finallongfeeMultiplier=Math.max(feeJSON.get("medianFeeMultiplier").asLong(),feeJSON.get("minFeeMultiplier").asLong());finalTransactiontransaction=facade.createTransactionFromTypedDescriptor(newTransferTransactionV1Descriptor(newAddress(monitorAddress),null,null),signerKeyPair.getPublicKey(),feeMultiplier,2*60*60);finalCryptoTypes.Signaturesignature=facade.signTransaction(signerKeyPair,transaction);finalStringjsonPayload=SymbolTransactionFactory.attachSignature(transaction,signature);transactionHash=facade.hashTransaction(transaction).toString();announceTransaction(jsonPayload,"/transactions","Announced transaction "+transactionHash.substring(0,16)+"...");// Wait for confirmation via WebSocketconfirmed.join();// Unsubscribe before closingfor(finalStringchannel:channels)remote.sendText(JSON_MAPPER.createObjectNode().put("uid",uid).put("unsubscribe",channel).toString());System.out.println("Unsubscribed from all channels");session.close();}privateJsonNodegetJson(finalStringpath)throwsIOException,InterruptedException{finalHttpRequestrequest=HttpRequest.newBuilder(URI.create(nodeUrl+path)).GET().build();finalHttpResponse<String>response=HTTP_CLIENT.send(request,BodyHandlers.ofString());returnJSON_MAPPER.readTree(response.body());}privatevoidannounceTransaction(finalStringpayload,finalStringendpoint,finalStringlabel)throwsIOException,InterruptedException{finalHttpRequestrequest=HttpRequest.newBuilder(URI.create(nodeUrl+endpoint)).header("Content-Type","application/json").PUT(HttpRequest.BodyPublishers.ofString(payload)).build();HTTP_CLIENT.send(request,BodyHandlers.ofString());System.out.println(label);}// Handle incoming messages@OnMessagepublicvoidonMessage(finalStringpayload)throwsIOException{finalJsonNodemessage=JSON_MAPPER.readTree(payload);// Special case for the initial handshake messageif(message.has("uid")){uidFuture.complete(message.get("uid").asText());return;}finalStringtopic=message.get("topic").asText();finalStringmessageHash=message.get("data").get("meta").get("hash").asText();finalStringname=topic.split("/")[0];System.out.printf("%s: hash=%s...%n",name,messageHash.substring(0,16));if("confirmedAdded".equals(name)&&messageHash.equals(transactionHash)){System.out.printf("Transaction %s... confirmed%n",transactionHash.substring(0,16));confirmed.complete(null);}}}
asyncwithconnect(WS_URL)aswebsocket:# Connect to WebSocketresponse=json.loads(awaitwebsocket.recv())uid=response['uid']print(f'Connected to {WS_URL} with uid {uid}')
// Connect to WebSocketconstwebsocket=newWebSocket(WS_URL);constuid=awaitnewPromise(resolve=>{websocket.addEventListener('message',event=>{constmessage=JSON.parse(event.data);resolve(message.uid);},{once:true});});console.log(`Connected to ${WS_URL} with uid ${uid}`);
// Connect to WebSocketfinalWebSocketContainercontainer=ContainerProvider.getWebSocketContainer();finalSessionsession=container.connectToServer(this,URI.create(wsUrl));finalRemoteEndpoint.Basicremote=session.getBasicRemote();finalStringuid=uidFuture.join();System.out.printf("Connected to %s with uid %s%n",wsUrl,uid);
# Subscribe to transaction channelschannels=[f'unconfirmedAdded/{MONITOR_ADDRESS}',f'unconfirmedRemoved/{MONITOR_ADDRESS}',f'confirmedAdded/{MONITOR_ADDRESS}',]forchannelinchannels:awaitwebsocket.send(json.dumps({'uid':uid,'subscribe':channel}))name=channel.split('/')[0]print(f'Subscribed to {name} channel')
// Subscribe to transaction channelsconstchannels=[`unconfirmedAdded/${MONITOR_ADDRESS}`,`unconfirmedRemoved/${MONITOR_ADDRESS}`,`confirmedAdded/${MONITOR_ADDRESS}`];for(constchannelofchannels){websocket.send(JSON.stringify({uid,subscribe:channel}));constname=channel.split('/')[0];console.log(`Subscribed to ${name} channel`);}
// Subscribe to transaction channelsfinalList<String>channels=List.of("unconfirmedAdded/"+monitorAddress,"unconfirmedRemoved/"+monitorAddress,"confirmedAdded/"+monitorAddress);for(finalStringchannel:channels){remote.sendText(JSON_MAPPER.createObjectNode().put("uid",uid).put("subscribe",channel).toString());System.out.printf("Subscribed to %s channel%n",channel.split("/")[0]);}
# Build and announce a transfer transactionwithurllib.request.urlopen(f'{NODE_URL}/network/fees/transaction')asresp:fee_json=json.loads(resp.read().decode())fee_multiplier=max(fee_json['medianFeeMultiplier'],fee_json['minFeeMultiplier'])transaction=facade.create_transaction_from_descriptor({'type':'transfer_transaction_v1','recipient_address':Address(MONITOR_ADDRESS)},signer_key_pair.public_key,fee_multiplier,2*60*60)signature=facade.sign_transaction(signer_key_pair,transaction)json_payload=facade.transaction_factory.attach_signature(transaction,signature)transaction_hash=str(facade.hash_transaction(transaction))
// Build and announce a transfer transactionconstfeeResponse=awaitfetch(`${NODE_URL}/network/fees/transaction`);constfeeJSON=awaitfeeResponse.json();constfeeMultiplier=Math.max(feeJSON.medianFeeMultiplier,feeJSON.minFeeMultiplier);consttransaction=facade.createTransactionFromTypedDescriptor(newdescriptors.TransferTransactionV1Descriptor(newSymbolFacade.Address(MONITOR_ADDRESS),undefined,undefined),signerKeyPair.publicKey,feeMultiplier,2*60*60);constsignature=facade.signTransaction(signerKeyPair,transaction);constjsonPayload=facade.transactionFactory.static.attachSignature(transaction,signature);consttransactionHash=facade.hashTransaction(transaction).toString();
// Build and announce a transfer transactionfinalJsonNodefeeJSON=getJson("/network/fees/transaction");finallongfeeMultiplier=Math.max(feeJSON.get("medianFeeMultiplier").asLong(),feeJSON.get("minFeeMultiplier").asLong());finalTransactiontransaction=facade.createTransactionFromTypedDescriptor(newTransferTransactionV1Descriptor(newAddress(monitorAddress),null,null),signerKeyPair.getPublicKey(),feeMultiplier,2*60*60);finalCryptoTypes.Signaturesignature=facade.signTransaction(signerKeyPair,transaction);finalStringjsonPayload=SymbolTransactionFactory.attachSignature(transaction,signature);transactionHash=facade.hashTransaction(transaction).toString();
// Handle incoming messages@OnMessagepublicvoidonMessage(finalStringpayload)throwsIOException{finalJsonNodemessage=JSON_MAPPER.readTree(payload);// Special case for the initial handshake messageif(message.has("uid")){uidFuture.complete(message.get("uid").asText());return;}finalStringtopic=message.get("topic").asText();finalStringmessageHash=message.get("data").get("meta").get("hash").asText();finalStringname=topic.split("/")[0];System.out.printf("%s: hash=%s...%n",name,messageHash.substring(0,16));if("confirmedAdded".equals(name)&&messageHash.equals(transactionHash)){System.out.printf("Transaction %s... confirmed%n",transactionHash.substring(0,16));confirmed.complete(null);}}
# Unsubscribe before closingforchannelinchannels:awaitwebsocket.send(json.dumps({'uid':uid,'unsubscribe':channel}))print('Unsubscribed from all channels')
// Unsubscribe before closingfor(constchannelofchannels)websocket.send(JSON.stringify({uid,unsubscribe:channel}));console.log('Unsubscribed from all channels');websocket.close();
// Unsubscribe before closingfor(finalStringchannel:channels)remote.sendText(JSON_MAPPER.createObjectNode().put("uid",uid).put("unsubscribe",channel).toString());System.out.println("Unsubscribed from all channels");session.close();
Using node https://reference.symboltest.net:3001
Monitoring address: TCHBDENCLKEBILBPWP3JPB2XNY64OE7PYHHE32I
Connected to wss://reference.symboltest.net:3001/ws with uid Hj3kL9mN2pQr5tVw=
Subscribed to unconfirmedAdded channel
Subscribed to unconfirmedRemoved channel
Subscribed to confirmedAdded channel
Announced transaction 7A3F1B9E4C2D8A65...
unconfirmedAdded: hash=7A3F1B9E4C2D8A65...
unconfirmedRemoved: hash=7A3F1B9E4C2D8A65...
confirmedAdded: hash=7A3F1B9E4C2D8A65...
Transaction 7A3F1B9E4C2D8A65... confirmed
Unsubscribed from all channels