@@ -3,6 +3,7 @@ import JSONbig from 'json-bigint'
33
44import httpMethods from 'http-client'
55import _openWebSocket from 'open-websocket'
6+ import { createHmacSignature , createAsymmetricSignature } from 'signature'
67
78const endpoints = {
89 base : 'wss://stream.binance.com:9443/ws' ,
@@ -880,6 +881,107 @@ const getStreamMethods = (opts, variator = '') => {
880881
881882export const keepStreamAlive = ( method , listenKey ) => method ( { listenKey } )
882883
884+ const userWebSocketApi = opts => ( cb , transform ) => {
885+ const isTestnet = opts . testnet || ( opts . httpBase && opts . httpBase . includes ( 'testnet' ) )
886+ const wsApiUrl = isTestnet
887+ ? opts . wsApiTestnet || 'wss://ws-api.testnet.binance.vision/ws-api/v3'
888+ : opts . wsApi || 'wss://ws-api.binance.com:443/ws-api/v3'
889+
890+ let requestId = 1
891+ const errorHandler = userErrorHandler ( cb , transform )
892+ const w = openWebSocket ( wsApiUrl )
893+
894+ const sendSubscribe = ( ) => {
895+ const timestamp = opts . getTime ? opts . getTime ( ) : Date . now ( )
896+ const paramsStr = `apiKey=${ opts . apiKey } ×tamp=${ timestamp } `
897+
898+ const doSend = signature => {
899+ w . send (
900+ JSONbig . stringify ( {
901+ id : requestId ++ ,
902+ method : 'userDataStream.subscribe.signature' ,
903+ params : {
904+ apiKey : opts . apiKey ,
905+ timestamp,
906+ signature,
907+ } ,
908+ } ) ,
909+ )
910+ }
911+
912+ if ( opts . apiSecret ) {
913+ createHmacSignature ( paramsStr , opts . apiSecret ) . then ( doSend )
914+ } else if ( opts . privateKey ) {
915+ doSend ( createAsymmetricSignature ( paramsStr , opts . privateKey ) )
916+ }
917+ }
918+
919+ return new Promise ( ( resolve , reject ) => {
920+ let resolved = false
921+
922+ w . onopen = ( ) => {
923+ sendSubscribe ( )
924+ if ( opts . emitSocketOpens ) {
925+ userOpenHandler ( cb , transform ) ( )
926+ }
927+ }
928+
929+ w . onmessage = msg => {
930+ const data = JSONbig . parse ( msg . data )
931+
932+ // Control response (subscription/unsubscription)
933+ if ( 'id' in data ) {
934+ if ( data . error ) {
935+ const err = new Error ( data . error . msg || 'WebSocket API error' )
936+ err . code = data . error . code
937+ if ( ! resolved ) {
938+ resolved = true
939+ reject ( err )
940+ } else if ( opts . emitStreamErrors ) {
941+ errorHandler ( err )
942+ }
943+ } else if ( ! resolved ) {
944+ resolved = true
945+ resolve ( options => {
946+ try {
947+ w . send (
948+ JSONbig . stringify ( {
949+ id : requestId ++ ,
950+ method : 'userDataStream.unsubscribe' ,
951+ } ) ,
952+ )
953+ } catch ( e ) {
954+ // Ignore send errors during close
955+ }
956+ w . close ( 1000 , 'Close handle was called' , {
957+ keepClosed : true ,
958+ ...options ,
959+ } )
960+ } )
961+ }
962+ return
963+ }
964+
965+ // User data event - unwrap if in wrapped format
966+ let eventData = data
967+ if ( data . event && typeof data . event === 'object' ) {
968+ eventData = data . event
969+ }
970+
971+ if ( eventData . e ) {
972+ userEventHandler ( cb , transform ) ( { data : JSONbig . stringify ( eventData ) } )
973+ }
974+ }
975+
976+ w . onerror = event => {
977+ const error = event . error || event . message || new Error ( 'WebSocket error' )
978+ if ( opts . emitSocketErrors ) {
979+ errorHandler ( typeof error === 'string' ? new Error ( error ) : error )
980+ }
981+ }
982+ } )
983+ }
984+
883985const user = ( opts , variator ) => ( cb , transform ) => {
884986 const [ getDataStream , keepDataStream , closeDataStream ] = getStreamMethods ( opts , variator )
885987
@@ -1029,7 +1131,7 @@ export default opts => {
10291131 miniTicker,
10301132 allMiniTickers,
10311133 customSubStream,
1032- user : user ( opts ) ,
1134+ user : userWebSocketApi ( opts ) ,
10331135
10341136 marginUser : user ( opts , 'margin' ) ,
10351137
0 commit comments