github.com/gagliardetto/solana-go@v1.11.0/rpc/getTransaction.go (about) 1 // Copyright 2021 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 rpc 16 17 import ( 18 "context" 19 "fmt" 20 21 bin "github.com/gagliardetto/binary" 22 "github.com/gagliardetto/solana-go" 23 ) 24 25 type GetTransactionOpts struct { 26 Encoding solana.EncodingType `json:"encoding,omitempty"` 27 28 // Desired commitment. "processed" is not supported. If parameter not provided, the default is "finalized". 29 Commitment CommitmentType `json:"commitment,omitempty"` 30 31 // Max transaction version to return in responses. 32 // If the requested block contains a transaction with a higher version, an error will be returned. 33 MaxSupportedTransactionVersion *uint64 34 } 35 36 // GetTransaction returns transaction details for a confirmed transaction. 37 // 38 // NEW: This method is only available in solana-core v1.7 or newer. 39 // Please use `getConfirmedTransaction` for solana-core v1.6 40 func (cl *Client) GetTransaction( 41 ctx context.Context, 42 txSig solana.Signature, // transaction signature 43 opts *GetTransactionOpts, 44 ) (out *GetTransactionResult, err error) { 45 params := []interface{}{txSig} 46 if opts != nil { 47 obj := M{} 48 if opts.Encoding != "" { 49 if !solana.IsAnyOfEncodingType( 50 opts.Encoding, 51 // Valid encodings: 52 // solana.EncodingJSON, // TODO 53 // solana.EncodingJSONParsed, // TODO 54 solana.EncodingBase58, 55 solana.EncodingBase64, 56 solana.EncodingBase64Zstd, 57 ) { 58 return nil, fmt.Errorf("provided encoding is not supported: %s", opts.Encoding) 59 } 60 obj["encoding"] = opts.Encoding 61 } 62 if opts.Commitment != "" { 63 obj["commitment"] = opts.Commitment 64 } 65 if opts.MaxSupportedTransactionVersion != nil { 66 obj["maxSupportedTransactionVersion"] = *opts.MaxSupportedTransactionVersion 67 } 68 if len(obj) > 0 { 69 params = append(params, obj) 70 } 71 } 72 err = cl.rpcClient.CallForInto(ctx, &out, "getTransaction", params) 73 if err != nil { 74 return nil, err 75 } 76 if out == nil { 77 return nil, ErrNotFound 78 } 79 return 80 } 81 82 type GetTransactionResult struct { 83 // The slot this transaction was processed in. 84 Slot uint64 `json:"slot"` 85 86 // Estimated production time, as Unix timestamp (seconds since the Unix epoch) 87 // of when the transaction was processed. 88 // Nil if not available. 89 BlockTime *solana.UnixTimeSeconds `json:"blockTime" bin:"optional"` 90 91 Transaction *TransactionResultEnvelope `json:"transaction" bin:"optional"` 92 Meta *TransactionMeta `json:"meta,omitempty" bin:"optional"` 93 Version TransactionVersion `json:"version"` 94 } 95 96 // TransactionResultEnvelope will contain a *solana.Transaction if the requested encoding is `solana.EncodingJSON` 97 // (which is also the default when the encoding is not specified), 98 // or a `solana.Data` in case of EncodingBase58, EncodingBase64. 99 type TransactionResultEnvelope struct { 100 asDecodedBinary solana.Data 101 asParsedTransaction *solana.Transaction 102 } 103 104 func (wrap TransactionResultEnvelope) MarshalJSON() ([]byte, error) { 105 if wrap.asParsedTransaction != nil { 106 return json.Marshal(wrap.asParsedTransaction) 107 } 108 return json.Marshal(wrap.asDecodedBinary) 109 } 110 111 func (wrap *TransactionResultEnvelope) UnmarshalJSON(data []byte) error { 112 if len(data) == 0 || (len(data) == 4 && string(data) == "null") { 113 // TODO: is this an error? 114 return nil 115 } 116 117 firstChar := data[0] 118 119 switch firstChar { 120 // Check if first character is `[`, standing for a JSON array. 121 case '[': 122 // It's base64 (or similar) 123 { 124 err := wrap.asDecodedBinary.UnmarshalJSON(data) 125 if err != nil { 126 return err 127 } 128 } 129 case '{': 130 // It's JSON, most likely. 131 { 132 return json.Unmarshal(data, &wrap.asParsedTransaction) 133 } 134 default: 135 return fmt.Errorf("Unknown kind: %v", data) 136 } 137 138 return nil 139 } 140 141 // GetBinary returns the decoded bytes if the encoding is 142 // "base58", "base64". 143 func (dt *TransactionResultEnvelope) GetBinary() []byte { 144 return dt.asDecodedBinary.Content 145 } 146 147 func (dt *TransactionResultEnvelope) GetData() solana.Data { 148 return dt.asDecodedBinary 149 } 150 151 // GetRawJSON returns a *solana.Transaction when the data 152 // encoding is EncodingJSON. 153 func (dt *TransactionResultEnvelope) GetTransaction() (*solana.Transaction, error) { 154 if dt.asDecodedBinary.Content != nil { 155 tx := new(solana.Transaction) 156 err := tx.UnmarshalWithDecoder(bin.NewBinDecoder(dt.asDecodedBinary.Content)) 157 if err != nil { 158 return nil, err 159 } 160 return tx, nil 161 } 162 return dt.asParsedTransaction, nil 163 } 164 165 func (obj TransactionResultEnvelope) MarshalWithEncoder(encoder *bin.Encoder) (err error) { 166 return encoder.Encode(obj.asDecodedBinary) 167 } 168 169 func (obj *TransactionResultEnvelope) UnmarshalWithDecoder(decoder *bin.Decoder) (err error) { 170 return decoder.Decode(&obj.asDecodedBinary) 171 } 172 173 func (obj GetTransactionResult) MarshalWithEncoder(encoder *bin.Encoder) (err error) { 174 err = encoder.WriteUint64(obj.Slot, bin.LE) 175 if err != nil { 176 return err 177 } 178 { 179 if obj.BlockTime == nil { 180 err = encoder.WriteBool(false) 181 if err != nil { 182 return err 183 } 184 } else { 185 err = encoder.WriteBool(true) 186 if err != nil { 187 return err 188 } 189 err = encoder.WriteInt64(int64(*obj.BlockTime), bin.LE) 190 if err != nil { 191 return err 192 } 193 } 194 } 195 { 196 if obj.Transaction == nil { 197 err = encoder.WriteBool(false) 198 if err != nil { 199 return err 200 } 201 } else { 202 err = encoder.WriteBool(true) 203 if err != nil { 204 return err 205 } 206 err = obj.Transaction.MarshalWithEncoder(encoder) 207 if err != nil { 208 return err 209 } 210 } 211 } 212 { 213 if obj.Meta == nil { 214 err = encoder.WriteBool(false) 215 if err != nil { 216 return err 217 } 218 } else { 219 err = encoder.WriteBool(true) 220 if err != nil { 221 return err 222 } 223 // NOTE: storing as JSON bytes: 224 buf, err := json.Marshal(obj.Meta) 225 if err != nil { 226 return err 227 } 228 err = encoder.WriteBytes(buf, true) 229 if err != nil { 230 return err 231 } 232 } 233 } 234 return nil 235 } 236 237 func (obj *GetTransactionResult) UnmarshalWithDecoder(decoder *bin.Decoder) (err error) { 238 // Deserialize `Slot`: 239 obj.Slot, err = decoder.ReadUint64(bin.LE) 240 if err != nil { 241 return err 242 } 243 // Deserialize `BlockTime` (optional): 244 { 245 ok, err := decoder.ReadBool() 246 if err != nil { 247 return err 248 } 249 if ok { 250 err = decoder.Decode(&obj.BlockTime) 251 if err != nil { 252 return err 253 } 254 } 255 } 256 { 257 ok, err := decoder.ReadBool() 258 if err != nil { 259 return err 260 } 261 if ok { 262 obj.Transaction = new(TransactionResultEnvelope) 263 err = obj.Transaction.UnmarshalWithDecoder(decoder) 264 if err != nil { 265 return err 266 } 267 } 268 } 269 { 270 ok, err := decoder.ReadBool() 271 if err != nil { 272 return err 273 } 274 if ok { 275 // NOTE: storing as JSON bytes: 276 buf, err := decoder.ReadByteSlice() 277 if err != nil { 278 return err 279 } 280 err = json.Unmarshal(buf, &obj.Meta) 281 if err != nil { 282 return err 283 } 284 } 285 } 286 return nil 287 }