github.com/gagliardetto/solana-go@v1.11.0/rpc/client.go (about)

     1  // Copyright 2021 github.com/gagliardetto
     2  // This file has been modified by github.com/gagliardetto
     3  //
     4  // Copyright 2020 dfuse Platform Inc.
     5  //
     6  // Licensed under the Apache License, Version 2.0 (the "License");
     7  // you may not use this file except in compliance with the License.
     8  // You may obtain a copy of the License at
     9  //
    10  //      http://www.apache.org/licenses/LICENSE-2.0
    11  //
    12  // Unless required by applicable law or agreed to in writing, software
    13  // distributed under the License is distributed on an "AS IS" BASIS,
    14  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  // See the License for the specific language governing permissions and
    16  // limitations under the License.
    17  
    18  package rpc
    19  
    20  import (
    21  	"context"
    22  	"errors"
    23  	"io"
    24  	"net"
    25  	"net/http"
    26  	"time"
    27  
    28  	"github.com/gagliardetto/solana-go/rpc/jsonrpc"
    29  	"github.com/klauspost/compress/gzhttp"
    30  )
    31  
    32  var (
    33  	ErrNotFound     = errors.New("not found")
    34  	ErrNotConfirmed = errors.New("not confirmed")
    35  )
    36  
    37  type Client struct {
    38  	rpcURL    string
    39  	rpcClient JSONRPCClient
    40  }
    41  
    42  type JSONRPCClient interface {
    43  	CallForInto(ctx context.Context, out interface{}, method string, params []interface{}) error
    44  	CallWithCallback(ctx context.Context, method string, params []interface{}, callback func(*http.Request, *http.Response) error) error
    45  	CallBatch(ctx context.Context, requests jsonrpc.RPCRequests) (jsonrpc.RPCResponses, error)
    46  }
    47  
    48  // New creates a new Solana JSON RPC client.
    49  // Client is safe for concurrent use by multiple goroutines.
    50  func New(rpcEndpoint string) *Client {
    51  	opts := &jsonrpc.RPCClientOpts{
    52  		HTTPClient: newHTTP(),
    53  	}
    54  
    55  	rpcClient := jsonrpc.NewClientWithOpts(rpcEndpoint, opts)
    56  	return NewWithCustomRPCClient(rpcClient)
    57  }
    58  
    59  // New creates a new Solana JSON RPC client with the provided custom headers.
    60  // The provided headers will be added to each RPC request sent via this RPC client.
    61  func NewWithHeaders(rpcEndpoint string, headers map[string]string) *Client {
    62  	opts := &jsonrpc.RPCClientOpts{
    63  		HTTPClient:    newHTTP(),
    64  		CustomHeaders: headers,
    65  	}
    66  	rpcClient := jsonrpc.NewClientWithOpts(rpcEndpoint, opts)
    67  	return NewWithCustomRPCClient(rpcClient)
    68  }
    69  
    70  // Close closes the client.
    71  func (cl *Client) Close() error {
    72  	if cl.rpcClient == nil {
    73  		return nil
    74  	}
    75  	if c, ok := cl.rpcClient.(io.Closer); ok {
    76  		return c.Close()
    77  	}
    78  	return nil
    79  }
    80  
    81  // NewWithCustomRPCClient creates a new Solana RPC client
    82  // with the provided RPC client.
    83  func NewWithCustomRPCClient(rpcClient JSONRPCClient) *Client {
    84  	return &Client{
    85  		rpcClient: rpcClient,
    86  	}
    87  }
    88  
    89  var (
    90  	defaultMaxIdleConnsPerHost = 9
    91  	defaultTimeout             = 5 * time.Minute
    92  	defaultKeepAlive           = 180 * time.Second
    93  )
    94  
    95  func newHTTPTransport() *http.Transport {
    96  	return &http.Transport{
    97  		IdleConnTimeout:     defaultTimeout,
    98  		MaxConnsPerHost:     defaultMaxIdleConnsPerHost,
    99  		MaxIdleConnsPerHost: defaultMaxIdleConnsPerHost,
   100  		Proxy:               http.ProxyFromEnvironment,
   101  		DialContext: (&net.Dialer{
   102  			Timeout:   defaultTimeout,
   103  			KeepAlive: defaultKeepAlive,
   104  			DualStack: true,
   105  		}).DialContext,
   106  		ForceAttemptHTTP2: true,
   107  		// MaxIdleConns:          100,
   108  		TLSHandshakeTimeout: 10 * time.Second,
   109  		// ExpectContinueTimeout: 1 * time.Second,
   110  	}
   111  }
   112  
   113  // newHTTP returns a new Client from the provided config.
   114  // Client is safe for concurrent use by multiple goroutines.
   115  func newHTTP() *http.Client {
   116  	tr := newHTTPTransport()
   117  
   118  	return &http.Client{
   119  		Timeout:   defaultTimeout,
   120  		Transport: gzhttp.Transport(tr),
   121  	}
   122  }
   123  
   124  // RPCCallForInto allows to access the raw RPC client and send custom requests.
   125  func (cl *Client) RPCCallForInto(ctx context.Context, out interface{}, method string, params []interface{}) error {
   126  	return cl.rpcClient.CallForInto(ctx, out, method, params)
   127  }
   128  
   129  func (cl *Client) RPCCallWithCallback(
   130  	ctx context.Context,
   131  	method string,
   132  	params []interface{},
   133  	callback func(*http.Request, *http.Response) error,
   134  ) error {
   135  	return cl.rpcClient.CallWithCallback(ctx, method, params, callback)
   136  }
   137  
   138  func (cl *Client) RPCCallBatch(
   139  	ctx context.Context,
   140  	requests jsonrpc.RPCRequests,
   141  ) (jsonrpc.RPCResponses, error) {
   142  	return cl.rpcClient.CallBatch(ctx, requests)
   143  }
   144  
   145  func NewBoolean(b bool) *bool {
   146  	return &b
   147  }
   148  
   149  func NewTransactionVersion(v uint64) *uint64 {
   150  	return &v
   151  }