github.com/gagliardetto/solana-go@v1.11.0/rpc/types.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 "encoding/base64" 22 stdjson "encoding/json" 23 "fmt" 24 "math/big" 25 26 bin "github.com/gagliardetto/binary" 27 28 "github.com/gagliardetto/solana-go" 29 ) 30 31 type Context struct { 32 Slot uint64 `json:"slot"` 33 } 34 35 type RPCContext struct { 36 Context Context `json:"context,omitempty"` 37 } 38 39 type GetBalanceResult struct { 40 RPCContext 41 Value uint64 `json:"value"` 42 } 43 44 type GetRecentBlockhashResult struct { 45 RPCContext 46 Value *BlockhashResult `json:"value"` 47 } 48 49 type BlockhashResult struct { 50 Blockhash solana.Hash `json:"blockhash"` 51 FeeCalculator FeeCalculator `json:"feeCalculator"` 52 } 53 54 type FeeCalculator struct { 55 LamportsPerSignature uint64 `json:"lamportsPerSignature"` 56 } 57 58 type GetConfirmedBlockResult struct { 59 Blockhash solana.Hash `json:"blockhash"` 60 61 // could be zeroes if ledger was clean-up and this is unavailable 62 PreviousBlockhash solana.Hash `json:"previousBlockhash"` 63 64 ParentSlot uint64 `json:"parentSlot"` 65 Transactions []TransactionWithMeta `json:"transactions"` 66 Signatures []solana.Signature `json:"signatures"` 67 Rewards []BlockReward `json:"rewards"` 68 BlockTime *solana.UnixTimeSeconds `json:"blockTime,omitempty"` 69 } 70 71 type BlockReward struct { 72 // The public key of the account that received the reward. 73 Pubkey solana.PublicKey `json:"pubkey"` 74 75 // Number of reward lamports credited or debited by the account, as a i64. 76 Lamports int64 `json:"lamports"` 77 78 // Account balance in lamports after the reward was applied. 79 PostBalance uint64 `json:"postBalance"` 80 81 // Type of reward: "Fee", "Rent", "Voting", "Staking". 82 RewardType RewardType `json:"rewardType"` 83 84 // Vote account commission when the reward was credited, 85 // only present for voting and staking rewards. 86 Commission *uint8 `json:"commission,omitempty"` 87 } 88 89 type RewardType string 90 91 const ( 92 RewardTypeFee RewardType = "Fee" 93 RewardTypeRent RewardType = "Rent" 94 RewardTypeVoting RewardType = "Voting" 95 RewardTypeStaking RewardType = "Staking" 96 ) 97 98 type TransactionWithMeta struct { 99 // The slot this transaction was processed in. 100 Slot uint64 `json:"slot"` 101 102 // Estimated production time, as Unix timestamp (seconds since the Unix epoch) 103 // of when the transaction was processed. 104 // Nil if not available. 105 BlockTime *solana.UnixTimeSeconds `json:"blockTime" bin:"optional"` 106 107 Transaction *DataBytesOrJSON `json:"transaction"` 108 109 // Transaction status metadata object 110 Meta *TransactionMeta `json:"meta,omitempty"` 111 Version TransactionVersion `json:"version"` 112 } 113 114 func (dt TransactionWithMeta) GetParsedTransaction() (*solana.Transaction, error) { 115 if dt.Transaction == nil { 116 return nil, fmt.Errorf("transaction is nil") 117 } 118 if dt.Transaction.rawDataEncoding != solana.EncodingJSONParsed { 119 return nil, fmt.Errorf("data is not in JSONParsed encoding") 120 } 121 var parsedTransaction solana.Transaction 122 if err := json.Unmarshal(dt.Transaction.asJSON, &parsedTransaction); err != nil { 123 return nil, err 124 } 125 return &parsedTransaction, nil 126 } 127 128 func (twm TransactionWithMeta) MustGetTransaction() *solana.Transaction { 129 tx, err := twm.GetTransaction() 130 if err != nil { 131 panic(err) 132 } 133 return tx 134 } 135 136 func (twm TransactionWithMeta) GetTransaction() (*solana.Transaction, error) { 137 tx := new(solana.Transaction) 138 err := tx.UnmarshalWithDecoder(bin.NewBinDecoder(twm.Transaction.GetBinary())) 139 if err != nil { 140 return nil, err 141 } 142 return tx, nil 143 } 144 145 type TransactionParsed struct { 146 Meta *TransactionMeta `json:"meta,omitempty"` 147 Transaction *solana.Transaction `json:"transaction"` 148 } 149 150 type TokenBalance struct { 151 // Index of the account in which the token balance is provided for. 152 AccountIndex uint16 `json:"accountIndex"` 153 154 // Pubkey of token balance's owner. 155 Owner *solana.PublicKey `json:"owner,omitempty"` 156 157 // Pubkey of the token's mint. 158 Mint solana.PublicKey `json:"mint"` 159 UiTokenAmount *UiTokenAmount `json:"uiTokenAmount"` 160 } 161 162 type UiTokenAmount struct { 163 // Raw amount of tokens as a string, ignoring decimals. 164 Amount string `json:"amount"` 165 166 // TODO: <number> == int64 ??? 167 // Number of decimals configured for token's mint. 168 Decimals uint8 `json:"decimals"` 169 170 // DEPRECATED: Token amount as a float, accounting for decimals. 171 UiAmount *float64 `json:"uiAmount"` 172 173 // Token amount as a string, accounting for decimals. 174 UiAmountString string `json:"uiAmountString"` 175 } 176 177 type LoadedAddresses struct { 178 ReadOnly solana.PublicKeySlice `json:"readonly"` 179 Writable solana.PublicKeySlice `json:"writable"` 180 } 181 182 type TransactionMeta struct { 183 // Error if transaction failed, null if transaction succeeded. 184 // https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L24 185 Err interface{} `json:"err"` 186 187 // Fee this transaction was charged 188 Fee uint64 `json:"fee"` 189 190 // Array of u64 account balances from before the transaction was processed 191 PreBalances []uint64 `json:"preBalances"` 192 193 // Array of u64 account balances after the transaction was processed 194 PostBalances []uint64 `json:"postBalances"` 195 196 // List of inner instructions or omitted if inner instruction recording 197 // was not yet enabled during this transaction 198 InnerInstructions []InnerInstruction `json:"innerInstructions"` 199 200 // List of token balances from before the transaction was processed 201 // or omitted if token balance recording was not yet enabled during this transaction 202 PreTokenBalances []TokenBalance `json:"preTokenBalances"` 203 204 // List of token balances from after the transaction was processed 205 // or omitted if token balance recording was not yet enabled during this transaction 206 PostTokenBalances []TokenBalance `json:"postTokenBalances"` 207 208 // Array of string log messages or omitted if log message 209 // recording was not yet enabled during this transaction 210 LogMessages []string `json:"logMessages"` 211 212 // DEPRECATED: Transaction status. 213 Status DeprecatedTransactionMetaStatus `json:"status"` 214 215 Rewards []BlockReward `json:"rewards"` 216 217 LoadedAddresses LoadedAddresses `json:"loadedAddresses"` 218 219 ComputeUnitsConsumed *uint64 `json:"computeUnitsConsumed"` 220 } 221 222 type InnerInstruction struct { 223 // TODO: <number> == int64 ??? 224 // Index of the transaction instruction from which the inner instruction(s) originated 225 Index uint16 `json:"index"` 226 227 // Ordered list of inner program instructions that were invoked during a single transaction instruction. 228 Instructions []solana.CompiledInstruction `json:"instructions"` 229 } 230 231 // Ok interface{} `json:"Ok"` // <null> Transaction was successful 232 // Err interface{} `json:"Err"` // Transaction failed with TransactionError 233 type DeprecatedTransactionMetaStatus M 234 235 type TransactionSignature struct { 236 // Error if transaction failed, nil if transaction succeeded. 237 Err interface{} `json:"err"` 238 239 // Memo associated with the transaction, nil if no memo is present. 240 Memo *string `json:"memo"` 241 242 // Transaction signature. 243 Signature solana.Signature `json:"signature"` 244 245 // The slot that contains the block with the transaction. 246 Slot uint64 `json:"slot,omitempty"` 247 248 // Estimated production time, as Unix timestamp (seconds since the Unix epoch) 249 // of when transaction was processed. Nil if not available. 250 BlockTime *solana.UnixTimeSeconds `json:"blockTime,omitempty"` 251 252 ConfirmationStatus ConfirmationStatusType `json:"confirmationStatus,omitempty"` 253 } 254 255 type GetAccountInfoResult struct { 256 RPCContext 257 Value *Account `json:"value"` 258 } 259 260 // GetBinary returns the binary representation of the account data. 261 func (a *GetAccountInfoResult) GetBinary() []byte { 262 if a == nil { 263 return nil 264 } 265 if a.Value == nil { 266 return nil 267 } 268 if a.Value.Data == nil { 269 return nil 270 } 271 return a.Value.Data.GetBinary() 272 } 273 274 // Bytes returns the binary representation of the account data. 275 func (a *GetAccountInfoResult) Bytes() []byte { 276 return a.GetBinary() 277 } 278 279 type IsValidBlockhashResult struct { 280 RPCContext 281 Value bool `json:"value"` // True if the blockhash is still valid. 282 } 283 284 type Account struct { 285 // Number of lamports assigned to this account 286 Lamports uint64 `json:"lamports"` 287 288 // Pubkey of the program this account has been assigned to 289 Owner solana.PublicKey `json:"owner"` 290 291 // Data associated with the account, either as encoded binary data or JSON format {<program>: <state>}, depending on encoding parameter 292 Data *DataBytesOrJSON `json:"data"` 293 294 // Boolean indicating if the account contains a program (and is strictly read-only) 295 Executable bool `json:"executable"` 296 297 // The epoch at which this account will next owe rent 298 RentEpoch *big.Int `json:"rentEpoch"` 299 } 300 301 type DataBytesOrJSON struct { 302 rawDataEncoding solana.EncodingType 303 asDecodedBinary solana.Data 304 asJSON stdjson.RawMessage 305 } 306 307 func DataBytesOrJSONFromBase64(stringBase64 string) (*DataBytesOrJSON, error) { 308 decodedData, err := base64.StdEncoding.DecodeString(stringBase64) 309 if err != nil { 310 return nil, err 311 } 312 return DataBytesOrJSONFromBytes(decodedData), nil 313 } 314 315 // DataBytesOrJSONFromBytes creates a new `DataBytesOrJSON` from the provided bytes. 316 func DataBytesOrJSONFromBytes(data []byte) *DataBytesOrJSON { 317 return &DataBytesOrJSON{ 318 rawDataEncoding: solana.EncodingBase64, 319 asDecodedBinary: solana.Data{ 320 Encoding: solana.EncodingBase64, 321 Content: data, 322 }, 323 } 324 } 325 326 func (dt DataBytesOrJSON) MarshalJSON() ([]byte, error) { 327 if dt.rawDataEncoding == solana.EncodingJSONParsed || dt.rawDataEncoding == solana.EncodingJSON { 328 return json.Marshal(dt.asJSON) 329 } 330 return json.Marshal(dt.asDecodedBinary) 331 } 332 333 func (wrap *DataBytesOrJSON) UnmarshalJSON(data []byte) error { 334 if len(data) == 0 || (len(data) == 4 && string(data) == "null") { 335 // TODO: is this an error? 336 return nil 337 } 338 339 firstChar := data[0] 340 341 switch firstChar { 342 // Check if first character is `[`, standing for a JSON array. 343 case '[': 344 // It's base64 (or similar) 345 { 346 err := wrap.asDecodedBinary.UnmarshalJSON(data) 347 if err != nil { 348 return err 349 } 350 wrap.rawDataEncoding = wrap.asDecodedBinary.Encoding 351 } 352 case '{': 353 // It's JSON, most likely. 354 // TODO: is it always JSON??? 355 { 356 // Store raw bytes, and unmarshal on request. 357 wrap.asJSON = data 358 wrap.rawDataEncoding = solana.EncodingJSONParsed 359 } 360 default: 361 return fmt.Errorf("unknown kind: %v", data) 362 } 363 364 return nil 365 } 366 367 // GetBinary returns the decoded bytes if the encoding is 368 // "base58", "base64", or "base64+zstd". 369 func (dt *DataBytesOrJSON) GetBinary() []byte { 370 return dt.asDecodedBinary.Content 371 } 372 373 // GetRawJSON returns a stdjson.RawMessage when the data 374 // encoding is "jsonParsed". 375 func (dt *DataBytesOrJSON) GetRawJSON() stdjson.RawMessage { 376 return dt.asJSON 377 } 378 379 type DataSlice struct { 380 Offset *uint64 `json:"offset,omitempty"` 381 Length *uint64 `json:"length,omitempty"` 382 } 383 type GetProgramAccountsOpts struct { 384 Commitment CommitmentType `json:"commitment,omitempty"` 385 386 Encoding solana.EncodingType `json:"encoding,omitempty"` 387 388 // Limit the returned account data 389 DataSlice *DataSlice `json:"dataSlice,omitempty"` 390 391 // Filter on accounts, implicit AND between filters. 392 // Filter results using various filter objects; 393 // account must meet all filter criteria to be included in results. 394 Filters []RPCFilter `json:"filters,omitempty"` 395 } 396 397 type GetProgramAccountsResult []*KeyedAccount 398 399 type KeyedAccount struct { 400 Pubkey solana.PublicKey `json:"pubkey"` 401 Account *Account `json:"account"` 402 } 403 404 type GetConfirmedSignaturesForAddress2Opts struct { 405 Limit *uint64 `json:"limit,omitempty"` 406 Before solana.Signature `json:"before,omitempty"` 407 Until solana.Signature `json:"until,omitempty"` 408 Commitment CommitmentType `json:"commitment,omitempty"` 409 } 410 411 type GetConfirmedSignaturesForAddress2Result []*TransactionSignature 412 413 type RPCFilter struct { 414 Memcmp *RPCFilterMemcmp `json:"memcmp,omitempty"` 415 DataSize uint64 `json:"dataSize,omitempty"` 416 } 417 418 type RPCFilterMemcmp struct { 419 Offset uint64 `json:"offset"` 420 Bytes solana.Base58 `json:"bytes"` 421 } 422 423 type CommitmentType string 424 425 const ( 426 CommitmentMax CommitmentType = "max" // Deprecated as of v1.5.5 427 CommitmentRecent CommitmentType = "recent" // Deprecated as of v1.5.5 428 CommitmentRoot CommitmentType = "root" // Deprecated as of v1.5.5 429 CommitmentSingle CommitmentType = "single" // Deprecated as of v1.5.5 430 CommitmentSingleGossip CommitmentType = "singleGossip" // Deprecated as of v1.5.5 431 432 // The node will query the most recent block confirmed by supermajority 433 // of the cluster as having reached maximum lockout, 434 // meaning the cluster has recognized this block as finalized. 435 CommitmentFinalized CommitmentType = "finalized" 436 437 // The node will query the most recent block that has been voted on by supermajority of the cluster. 438 // - It incorporates votes from gossip and replay. 439 // - It does not count votes on descendants of a block, only direct votes on that block. 440 // - This confirmation level also upholds "optimistic confirmation" guarantees in release 1.3 and onwards. 441 CommitmentConfirmed CommitmentType = "confirmed" 442 443 // The node will query its most recent block. Note that the block may still be skipped by the cluster. 444 CommitmentProcessed CommitmentType = "processed" 445 ) 446 447 type ParsedTransaction struct { 448 Signatures []solana.Signature `json:"signatures"` 449 Message ParsedMessage `json:"message"` 450 } 451 452 type ParsedTransactionMeta struct { 453 // Error if transaction failed, null if transaction succeeded. 454 // https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L24 455 Err interface{} `json:"err"` 456 457 // Fee this transaction was charged 458 Fee uint64 `json:"fee"` 459 460 // Array of u64 account balances from before the transaction was processed 461 PreBalances []uint64 `json:"preBalances"` 462 463 // Array of u64 account balances after the transaction was processed 464 PostBalances []uint64 `json:"postBalances"` 465 466 // List of inner instructions or omitted if inner instruction recording 467 // was not yet enabled during this transaction 468 InnerInstructions []ParsedInnerInstruction `json:"innerInstructions"` 469 470 // List of token balances from before the transaction was processed 471 // or omitted if token balance recording was not yet enabled during this transaction 472 PreTokenBalances []TokenBalance `json:"preTokenBalances"` 473 474 // List of token balances from after the transaction was processed 475 // or omitted if token balance recording was not yet enabled during this transaction 476 PostTokenBalances []TokenBalance `json:"postTokenBalances"` 477 478 // Array of string log messages or omitted if log message 479 // recording was not yet enabled during this transaction 480 LogMessages []string `json:"logMessages"` 481 } 482 483 type ParsedInnerInstruction struct { 484 Index uint64 `json:"index"` 485 Instructions []*ParsedInstruction `json:"instructions"` 486 } 487 488 type ParsedMessageAccount struct { 489 PublicKey solana.PublicKey `json:"pubkey"` 490 Signer bool `json:"signer"` 491 Writable bool `json:"writable"` 492 } 493 494 type ParsedMessage struct { 495 AccountKeys []ParsedMessageAccount `json:"accountKeys"` 496 Instructions []*ParsedInstruction `json:"instructions"` 497 RecentBlockHash string `json:"recentBlockhash"` 498 } 499 500 type ParsedInstruction struct { 501 Program string `json:"program,omitempty"` 502 ProgramId solana.PublicKey `json:"programId,omitempty"` 503 Parsed *InstructionInfoEnvelope `json:"parsed,omitempty"` 504 Data solana.Base58 `json:"data,omitempty"` 505 Accounts []solana.PublicKey `json:"accounts,omitempty"` 506 } 507 508 type InstructionInfoEnvelope struct { 509 asString string 510 asInstructionInfo *InstructionInfo 511 } 512 513 type InstructionInfo struct { 514 Info map[string]interface{} `json:"info"` 515 InstructionType string `json:"type"` 516 } 517 518 type TransactionOpts struct { 519 Encoding solana.EncodingType `json:"encoding,omitempty"` 520 SkipPreflight bool `json:"skipPreflight,omitempty"` 521 PreflightCommitment CommitmentType `json:"preflightCommitment,omitempty"` 522 MaxRetries *uint `json:"maxRetries"` 523 MinContextSlot *uint64 `json:"minContextSlot"` 524 } 525 526 func (opts *TransactionOpts) ToMap() M { 527 obj := M{} 528 529 if opts.Encoding == "" { 530 // default to base64 encoding 531 obj["encoding"] = "base64" 532 } else { 533 obj["encoding"] = opts.Encoding 534 } 535 536 obj["skipPreflight"] = opts.SkipPreflight 537 538 if opts.PreflightCommitment != "" { 539 obj["preflightCommitment"] = opts.PreflightCommitment 540 } 541 542 if opts.MaxRetries != nil { 543 obj["maxRetries"] = *opts.MaxRetries 544 } 545 546 if opts.MinContextSlot != nil { 547 obj["minContextSlot"] = *opts.MinContextSlot 548 } 549 550 return obj 551 } 552 553 type M map[string]interface{}