github.com/gagliardetto/solana-go@v1.11.0/rpc/ws/blockSubscribe.go (about) 1 // Copyright 2022 github.com/gagliardetto 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package ws 16 17 import ( 18 "fmt" 19 20 "github.com/gagliardetto/solana-go" 21 "github.com/gagliardetto/solana-go/rpc" 22 ) 23 24 type BlockResult struct { 25 Context struct { 26 Slot uint64 27 } `json:"context"` 28 Value struct { 29 Slot uint64 `json:"slot"` 30 Err interface{} `json:"err,omitempty"` 31 Block *rpc.GetBlockResult `json:"block,omitempty"` 32 } `json:"value"` 33 } 34 35 type BlockSubscribeFilter interface { 36 isBlockSubscribeFilter() 37 } 38 39 var _ BlockSubscribeFilter = BlockSubscribeFilterAll("") 40 41 type BlockSubscribeFilterAll string 42 43 func (_ BlockSubscribeFilterAll) isBlockSubscribeFilter() {} 44 45 type BlockSubscribeFilterMentionsAccountOrProgram struct { 46 Pubkey solana.PublicKey `json:"pubkey"` 47 } 48 49 func (_ BlockSubscribeFilterMentionsAccountOrProgram) isBlockSubscribeFilter() {} 50 51 func NewBlockSubscribeFilterAll() BlockSubscribeFilter { 52 return BlockSubscribeFilterAll("") 53 } 54 55 func NewBlockSubscribeFilterMentionsAccountOrProgram(pubkey solana.PublicKey) *BlockSubscribeFilterMentionsAccountOrProgram { 56 return &BlockSubscribeFilterMentionsAccountOrProgram{ 57 Pubkey: pubkey, 58 } 59 } 60 61 type BlockSubscribeOpts struct { 62 Commitment rpc.CommitmentType 63 Encoding solana.EncodingType `json:"encoding,omitempty"` 64 65 // Level of transaction detail to return. 66 TransactionDetails rpc.TransactionDetailsType 67 68 // Whether to populate the rewards array. If parameter not provided, the default includes rewards. 69 Rewards *bool 70 71 // Max transaction version to return in responses. 72 // If the requested block contains a transaction with a higher version, an error will be returned. 73 MaxSupportedTransactionVersion *uint64 74 } 75 76 // NOTE: Unstable, disabled by default 77 // 78 // Subscribe to receive notification anytime a new block is Confirmed or Finalized. 79 // 80 // **This subscription is unstable and only available if the validator was started 81 // with the `--rpc-pubsub-enable-block-subscription` flag. The format of this 82 // subscription may change in the future** 83 func (cl *Client) BlockSubscribe( 84 filter BlockSubscribeFilter, 85 opts *BlockSubscribeOpts, 86 ) (*BlockSubscription, error) { 87 var params []interface{} 88 if filter != nil { 89 switch v := filter.(type) { 90 case BlockSubscribeFilterAll: 91 params = append(params, "all") 92 case *BlockSubscribeFilterMentionsAccountOrProgram: 93 params = append(params, rpc.M{"mentionsAccountOrProgram": v.Pubkey}) 94 } 95 } 96 if opts != nil { 97 obj := make(rpc.M) 98 if opts.Commitment != "" { 99 obj["commitment"] = opts.Commitment 100 } 101 if opts.Encoding != "" { 102 if !solana.IsAnyOfEncodingType( 103 opts.Encoding, 104 // Valid encodings: 105 // solana.EncodingJSON, // TODO 106 // solana.EncodingJSONParsed, // TODO 107 solana.EncodingBase58, 108 solana.EncodingBase64, 109 solana.EncodingBase64Zstd, 110 ) { 111 return nil, fmt.Errorf("provided encoding is not supported: %s", opts.Encoding) 112 } 113 obj["encoding"] = opts.Encoding 114 } 115 if opts.TransactionDetails != "" { 116 obj["transactionDetails"] = opts.TransactionDetails 117 } 118 if opts.Rewards != nil { 119 obj["rewards"] = opts.Rewards 120 } 121 if opts.MaxSupportedTransactionVersion != nil { 122 obj["maxSupportedTransactionVersion"] = *opts.MaxSupportedTransactionVersion 123 } 124 if len(obj) > 0 { 125 params = append(params, obj) 126 } 127 } 128 genSub, err := cl.subscribe( 129 params, 130 nil, 131 "blockSubscribe", 132 "blockUnsubscribe", 133 func(msg []byte) (interface{}, error) { 134 var res BlockResult 135 err := decodeResponseFromMessage(msg, &res) 136 return &res, err 137 }, 138 ) 139 if err != nil { 140 return nil, err 141 } 142 return &BlockSubscription{ 143 sub: genSub, 144 }, nil 145 } 146 147 type BlockSubscription struct { 148 sub *Subscription 149 } 150 151 func (sw *BlockSubscription) Recv() (*BlockResult, error) { 152 select { 153 case d := <-sw.sub.stream: 154 return d.(*BlockResult), nil 155 case err := <-sw.sub.err: 156 return nil, err 157 } 158 } 159 160 func (sw *BlockSubscription) Err() <-chan error { 161 return sw.sub.err 162 } 163 164 func (sw *BlockSubscription) Response() <-chan *BlockResult { 165 typedChan := make(chan *BlockResult, 1) 166 go func(ch chan *BlockResult) { 167 // TODO: will this subscription yield more than one result? 168 d, ok := <-sw.sub.stream 169 if !ok { 170 return 171 } 172 ch <- d.(*BlockResult) 173 }(typedChan) 174 return typedChan 175 } 176 177 func (sw *BlockSubscription) Unsubscribe() { 178 sw.sub.Unsubscribe() 179 }