func New(conf Config) *EthereumWS {
	startSendWorker := make(chan bool)
	startReadWorker := make(chan bool)
	etws := &EthereumWS{
		state: State{
			addRequestIfNotExist: make(chan StateObject),
			getRequest:           make(chan StateObject),
			deleteRequest:        make(chan StateObject),
			response:             make(chan StateObject),
		},
		requestQueue: make(chan Request, 1000),
		requests:     map[int64]chan<- Response{},
	}
	go etws.State()
	go func() {
		<-startSendWorker
		for {
			select {
			case req := <-etws.requestQueue:
				if err := etws.conn.WriteJSON(req); err != nil {
					logger.Error(err)
					etws.state.getRequest <- StateObject{req.ID, nil}
					stateResponse := <-etws.state.response
					respChan := stateResponse.c
					respChan <- Response{error: err}
				}
			}
		}
	}()
	go func() {
		<-startReadWorker
		for {
			_, response, err := etws.conn.ReadMessage()
			if err != nil {
				logger.Error(err)
				continue
			}
			var resp Response
			if err := json.Unmarshal(response, &resp); err != nil {
				logger.Error(err)
				continue
			}
			etws.state.getRequest <- StateObject{resp.ID, nil}
			stateResponse := <-etws.state.response
			if stateResponse.id == 0 {
				logger.Error(fmt.Sprintf("failed to get response channel for ID: %d", stateResponse.id))
				continue
			}
			etws.state.deleteRequest <- StateObject{resp.ID, nil}
			_ = <-etws.state.response
			respChan := stateResponse.c
			respChan <- resp
		}
	}()
	go func() {
		for {
			co, _, err := wsg.DefaultDialer.Dial(conf.WSUrl, nil)
			if err == nil {
				etws.conn = co
				break
			}
			logger.Error(err)
			time.Sleep(conf.Retry)
		}
		startReadWorker <- true
		startSendWorker <- true
	}()
	return etws
}
create new ethereum websocket
package ethws
import (
	"encoding/json"
	"fmt"
	"time"
	wsg "github.com/gorilla/websocket"
	log "github.com/ipfs/go-log"
)
const rpcVersion = "2.0"
var logger = log.Logger("ethws")
type Config struct {
	Retry time.Duration
	WSUrl string
}
type Request struct {
	ID      int64         `json:"id"`
	Method  string        `json:"method"`
	Params  []interface{} `json:"params"`
	JsonRPC string        `json:"jsonrpc"`
}
func (r *Request) Marshal() ([]byte, error) {
	return json.Marshal(r)
}
type Error struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}
type Response struct {
	JsonRPC  string      `json:"jsonrpc"`
	RPCError *Error      `json:"error,omitempty"`
	Result   interface{} `json:"result"`
	ID       int64       `json:"id"`
	error    error
}
func (r *Response) Error() error {
	return r.error
}
type EthereumWS struct {
	state State
	requestQueue chan Request
	requests     map[int64]chan<- Response
	conn         *wsg.Conn
}
type State struct {
	addRequestIfNotExist chan StateObject
	getRequest           chan StateObject
	deleteRequest        chan StateObject
	response             chan StateObject
}
type StateObject struct {
	id int64
	c  chan<- Response
}
func (ws *EthereumWS) State() {
	for {
		select {
		case getRequest := <-ws.state.getRequest:
			if respChan, exist := ws.requests[getRequest.id]; exist {
				ws.state.response <- StateObject{getRequest.id, respChan}
				break
			}
			ws.state.response <- StateObject{0, nil}
		case addRequest := <-ws.state.addRequestIfNotExist:
			if respChan, exist := ws.requests[addRequest.id]; exist {
				ws.state.response <- StateObject{addRequest.id, respChan}
				break
			}
			ws.requests[addRequest.id] = addRequest.c
			ws.state.response <- StateObject{0, nil}
		case deleteRequest := <-ws.state.deleteRequest:
			delete(ws.requests, deleteRequest.id)
			ws.state.response <- StateObject{0, nil}
		}  
	}  
}  
func (ws *EthereumWS) SendRequest(r Request) (<-chan Response, error) {
	c := make(chan Response)
	for {
		id := time.Now().UnixNano()
		ws.state.addRequestIfNotExist <- StateObject{id, c}
		stateResponse := <-ws.state.response
		if stateResponse.id == 0 {
			r.ID = id
			break
		}
	}
	ws.requestQueue <- r
	return c, nil
}