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{}