github.com/mavryk-network/mvgo@v1.19.9/rpc/contracts.go (about)

     1  // Copyright (c) 2020-2022 Blockwatch Data Inc.
     2  // Author: alex@blockwatch.cc
     3  
     4  package rpc
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"encoding/json"
    10  	"fmt"
    11  	"strconv"
    12  
    13  	"github.com/mavryk-network/mvgo/mavryk"
    14  	"github.com/mavryk-network/mvgo/micheline"
    15  )
    16  
    17  // UnparsingMode defines the way types and values are represented in Micheline script
    18  // and storage. This affects timestamps, keys, addresses, signatures and nested pairs.
    19  // Optimized encodings use integers for timestamps and bytes instead of base58 encoded
    20  // values. Legacy mode is supposed to output 2-ary pairs only, but is messed up on
    21  // certain endpoints (e.g. /script/normalized), so there's no guarantee.
    22  type UnparsingMode string
    23  
    24  const (
    25  	UnparsingModeInvalid   = ""
    26  	UnparsingModeLegacy    = "Optimized_legacy"
    27  	UnparsingModeOptimized = "Optimized"
    28  	UnparsingModeReadable  = "Readable"
    29  )
    30  
    31  func (m UnparsingMode) String() string {
    32  	return string(m)
    33  }
    34  
    35  // Contracts holds a list of addresses
    36  type Contracts []mavryk.Address
    37  
    38  // Contracts holds info about a Tezos account
    39  type ContractInfo struct {
    40  	Balance        int64          `json:"balance,string"`
    41  	Delegate       mavryk.Address `json:"delegate"`
    42  	Counter        int64          `json:"counter,string"`
    43  	Manager        string         `json:"manager"`
    44  	FrozenDeposits struct {
    45  		InitialAmount int64 `json:"initial_amount,string"`
    46  		ActualAmount  int64 `json:"actual_amount,string"`
    47  	} `json:"frozen_deposits"`
    48  	FrozenDepositsPseudotokens int64 `json:"frozen_deposits_pseudotokens,string"`
    49  	MissedAttestations         struct {
    50  		RemainingSlots int64 `json:"remaining_slots"`
    51  		MissedLevels   int64 `json:"missed_levels"`
    52  	} `json:"missed_attestations"`
    53  	StakingParameters struct {
    54  		// TODO
    55  	} `json:"staking_parameters"`
    56  	UnstakeRequests struct {
    57  		Delegate mavryk.Address `json:"delegate"`
    58  		Requests []struct {
    59  			Cycle           int64 `json:"cycle"`
    60  			RequestedAmount int64 `json:"requested_amount,string"`
    61  		} `json:"requests"`
    62  	} `json:"unstake_requests"`
    63  	UnstakedFrozenDeposits []UnstakedDeposit `json:"unstaked_frozen_deposits"`
    64  }
    65  
    66  type UnstakedDeposit struct {
    67  	Cycle         int64 `json:"cycle"`
    68  	InitialAmount int64 `json:"initial_amount,string"`
    69  	ActualAmount  int64 `json:"actual_amount,string"`
    70  }
    71  
    72  // [[44,{"initial_amount":"1007000000","actual_amount":"1007000000"}]]
    73  func (u *UnstakedDeposit) UnmarshalJSON(buf []byte) error {
    74  	if len(buf) == 0 {
    75  		return nil
    76  	}
    77  	s, data, ok := bytes.Cut(buf[1:len(buf)-1], []byte{','})
    78  	if !ok || buf[0] != '[' || buf[len(buf)-1] != ']' {
    79  		return fmt.Errorf("UnstakedDeposit: invalid format")
    80  	}
    81  	num, err := strconv.ParseInt(string(s), 10, 64)
    82  	if err != nil {
    83  		return err
    84  	}
    85  	u.Cycle = num
    86  	type alias *UnstakedDeposit
    87  	return json.Unmarshal(data, alias(u))
    88  }
    89  
    90  func (i ContractInfo) IsRevealed() bool {
    91  	return mavryk.IsPublicKey(i.Manager)
    92  }
    93  
    94  func (i ContractInfo) ManagerKey() mavryk.Key {
    95  	key, _ := mavryk.ParseKey(i.Manager)
    96  	return key
    97  }
    98  
    99  // GetContract returns info about an account at block id.
   100  func (c *Client) GetContract(ctx context.Context, addr mavryk.Address, id BlockID) (*ContractInfo, error) {
   101  	u := fmt.Sprintf("chains/main/blocks/%s/context/contracts/%s", id, addr)
   102  	var info ContractInfo
   103  	err := c.Get(ctx, u, &info)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  	return &info, nil
   108  }
   109  
   110  // GetContractBalance returns the spendable balance for this account at block id.
   111  func (c *Client) GetContractBalance(ctx context.Context, addr mavryk.Address, id BlockID) (mavryk.Z, error) {
   112  	u := fmt.Sprintf("chains/main/blocks/%s/context/contracts/%s/balance", id, addr)
   113  	var bal mavryk.Z
   114  	err := c.Get(ctx, u, &bal)
   115  	return bal, err
   116  }
   117  
   118  // GetManagerKey returns the revealed public key of an account at block id.
   119  func (c *Client) GetManagerKey(ctx context.Context, addr mavryk.Address, id BlockID) (mavryk.Key, error) {
   120  	u := fmt.Sprintf("chains/main/blocks/%s/context/contracts/%s/manager_key", id, addr)
   121  	var key mavryk.Key
   122  	err := c.Get(ctx, u, &key)
   123  	return key, err
   124  }
   125  
   126  // GetContractExt returns info about an account at block id including its public key when revealed.
   127  func (c *Client) GetContractExt(ctx context.Context, addr mavryk.Address, id BlockID) (*ContractInfo, error) {
   128  	u := fmt.Sprintf("chains/main/blocks/%s/context/raw/json/contracts/index/%s", id, addr)
   129  	var info ContractInfo
   130  	err := c.Get(ctx, u, &info)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  	return &info, nil
   135  }
   136  
   137  // ListContracts returns a list of all known contracts at head. This call may be very SLOW for
   138  // large chains and there is no means to limit the result. Use with caution and consider
   139  // calling an indexer API instead.
   140  func (c *Client) ListContracts(ctx context.Context, id BlockID) (Contracts, error) {
   141  	contracts := make(Contracts, 0)
   142  	u := fmt.Sprintf("chains/main/blocks/%s/context/contracts", id)
   143  	if err := c.Get(ctx, u, &contracts); err != nil {
   144  		return nil, err
   145  	}
   146  	return contracts, nil
   147  }
   148  
   149  type rawContract struct {
   150  	Script micheline.Script
   151  }
   152  
   153  // GetContractScript returns the originated contract script in default data mode.
   154  func (c *Client) GetContractScript(ctx context.Context, addr mavryk.Address) (*micheline.Script, error) {
   155  	u := fmt.Sprintf("chains/main/blocks/head/context/contracts/%s", addr)
   156  	var rc rawContract
   157  	err := c.Get(ctx, u, &rc)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  	return &rc.Script, nil
   162  }
   163  
   164  // GetNormalizedScript returns the originated contract script with global constants
   165  // expanded using given unparsing mode.
   166  func (c *Client) GetNormalizedScript(ctx context.Context, addr mavryk.Address, mode UnparsingMode) (*micheline.Script, error) {
   167  	u := fmt.Sprintf("chains/main/blocks/head/context/contracts/%s/script/normalized", addr)
   168  	s := micheline.NewScript()
   169  	if mode == "" {
   170  		mode = UnparsingModeOptimized
   171  	}
   172  	postData := struct {
   173  		Mode UnparsingMode `json:"unparsing_mode"`
   174  	}{
   175  		Mode: mode,
   176  	}
   177  	err := c.Post(ctx, u, &postData, s)
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  	return s, nil
   182  }
   183  
   184  // GetContractStorage returns the contract's storage at block id.
   185  func (c *Client) GetContractStorage(ctx context.Context, addr mavryk.Address, id BlockID) (micheline.Prim, error) {
   186  	u := fmt.Sprintf("chains/main/blocks/%s/context/contracts/%s/storage", id, addr)
   187  	prim := micheline.Prim{}
   188  	err := c.Get(ctx, u, &prim)
   189  	if err != nil {
   190  		return micheline.InvalidPrim, err
   191  	}
   192  	return prim, nil
   193  }
   194  
   195  // GetContractStorageNormalized returns contract's storage at block id using unparsing mode.
   196  func (c *Client) GetContractStorageNormalized(ctx context.Context, addr mavryk.Address, id BlockID, mode UnparsingMode) (micheline.Prim, error) {
   197  	u := fmt.Sprintf("chains/main/blocks/%s/context/contracts/%s/storage/normalized", id, addr)
   198  	if mode == "" {
   199  		mode = UnparsingModeOptimized
   200  	}
   201  	postData := struct {
   202  		Mode UnparsingMode `json:"unparsing_mode"`
   203  	}{
   204  		Mode: mode,
   205  	}
   206  	prim := micheline.Prim{}
   207  	err := c.Post(ctx, u, &postData, &prim)
   208  	if err != nil {
   209  		return micheline.InvalidPrim, err
   210  	}
   211  	return prim, nil
   212  }
   213  
   214  // GetContractEntrypoints returns the contract's entrypoints.
   215  func (c *Client) GetContractEntrypoints(ctx context.Context, addr mavryk.Address) (map[string]micheline.Type, error) {
   216  	u := fmt.Sprintf("chains/main/blocks/head/context/contracts/%s/entrypoints", addr)
   217  	type eptype struct {
   218  		Entrypoints map[string]micheline.Type `json:"entrypoints"`
   219  	}
   220  	eps := &eptype{}
   221  	err := c.Get(ctx, u, eps)
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  	return eps.Entrypoints, nil
   226  }
   227  
   228  // ListBigmapKeys returns all keys in the bigmap at block id. This call may be very SLOW for
   229  // large bigmaps and there is no means to limit the result. Use of this method is discouraged.
   230  // Instead, call the ListBigmapValuesExt method below. In case you require the pre-image of
   231  // bigmap keys consider calling an indexer API instead.
   232  func (c *Client) ListBigmapKeys(ctx context.Context, bigmap int64, id BlockID) ([]mavryk.ExprHash, error) {
   233  	u := fmt.Sprintf("chains/main/blocks/%s/context/raw/json/big_maps/index/%d/contents", id, bigmap)
   234  	hashes := make([]mavryk.ExprHash, 0)
   235  	err := c.Get(ctx, u, &hashes)
   236  	if err != nil {
   237  		return nil, err
   238  	}
   239  	return hashes, nil
   240  }
   241  
   242  // ListActiveBigmapKeys returns all keys in the bigmap at block id. This call may be very SLOW for
   243  // large bigmaps and there is no means to limit the result. Use of this method is discouraged.
   244  // Instead, call the ListActiveBigmapValuesExt method below. In case you require the pre-image of
   245  // bigmap keys consider calling an indexer API instead.
   246  func (c *Client) ListActiveBigmapKeys(ctx context.Context, bigmap int64) ([]mavryk.ExprHash, error) {
   247  	return c.ListBigmapKeys(ctx, bigmap, Head)
   248  }
   249  
   250  // GetBigmapValue returns value at key hash from bigmap at block id
   251  func (c *Client) GetBigmapValue(ctx context.Context, bigmap int64, hash mavryk.ExprHash, id BlockID) (micheline.Prim, error) {
   252  	u := fmt.Sprintf("chains/main/blocks/%s/context/big_maps/%d/%s", id, bigmap, hash)
   253  	prim := micheline.Prim{}
   254  	err := c.Get(ctx, u, &prim)
   255  	if err != nil {
   256  		return micheline.InvalidPrim, err
   257  	}
   258  	return prim, nil
   259  }
   260  
   261  // GetActiveBigmapValue returns current active value at key hash from bigmap.
   262  func (c *Client) GetActiveBigmapValue(ctx context.Context, bigmap int64, hash mavryk.ExprHash) (micheline.Prim, error) {
   263  	return c.GetBigmapValue(ctx, bigmap, hash, Head)
   264  }
   265  
   266  // ListBigmapValues returns all values from bigmap at block id. This call may be very SLOW for
   267  // large bigmaps and there is no means to limit the result. Use of this method is discouraged.
   268  // Instead, call the ListBigmapValuesExt method below. In case you require the pre-image of
   269  // bigmap keys consider calling an indexer API instead.
   270  func (c *Client) ListBigmapValues(ctx context.Context, bigmap int64, id BlockID) ([]micheline.Prim, error) {
   271  	u := fmt.Sprintf("chains/main/blocks/%s/context/big_maps/%d", id, bigmap)
   272  	vals := make([]micheline.Prim, 0)
   273  	err := c.Get(ctx, u, &vals)
   274  	if err != nil {
   275  		return nil, err
   276  	}
   277  	return vals, nil
   278  }
   279  
   280  // ListBigmapValues returns at most limit values starting at offset from bigmap at block id.
   281  func (c *Client) ListBigmapValuesExt(ctx context.Context, bigmap int64, id BlockID, offset, limit int) ([]micheline.Prim, error) {
   282  	u := fmt.Sprintf("chains/main/blocks/%s/context/big_maps/%d?offset=%d&length=%d", id, bigmap, offset, limit)
   283  	vals := make([]micheline.Prim, 0)
   284  	err := c.Get(ctx, u, &vals)
   285  	if err != nil {
   286  		return nil, err
   287  	}
   288  	return vals, nil
   289  }
   290  
   291  // ListActiveBigmapValues returns all values from bigmap at block id. This call may be very SLOW for
   292  // large bigmaps and there is no means to limit the result. Use of this method is discouraged.
   293  // Instead, call the ListActiveBigmapValuesExt method below. In case you require the pre-image of
   294  // bigmap keys consider calling an indexer API instead.
   295  func (c *Client) ListActiveBigmapValues(ctx context.Context, bigmap int64, id BlockID) ([]micheline.Prim, error) {
   296  	return c.ListBigmapValues(ctx, bigmap, Head)
   297  }
   298  
   299  // ListActiveBigmapValuesExt returns at most limit values starting at offset from bigmap
   300  // at block id. In case you require the pre-image of bigmap keys consider calling an
   301  // indexer API instead.
   302  func (c *Client) ListActiveBigmapValuesExt(ctx context.Context, bigmap int64, id BlockID, offset, limit int) ([]micheline.Prim, error) {
   303  	return c.ListBigmapValuesExt(ctx, bigmap, Head, offset, limit)
   304  }
   305  
   306  type BigmapInfo struct {
   307  	KeyType    micheline.Prim `json:"key_type"`
   308  	ValueType  micheline.Prim `json:"value_type"`
   309  	TotalBytes int64          `json:"total_bytes,string"`
   310  }
   311  
   312  // GetActiveBigmapInfo returns type and content info from bigmap at current head.
   313  func (c *Client) GetActiveBigmapInfo(ctx context.Context, bigmap int64) (*BigmapInfo, error) {
   314  	return c.GetBigmapInfo(ctx, bigmap, Head)
   315  }
   316  
   317  // GetBigmapInfo returns type and content info from bigmap at block id.
   318  func (c *Client) GetBigmapInfo(ctx context.Context, bigmap int64, id BlockID) (*BigmapInfo, error) {
   319  	u := fmt.Sprintf("chains/main/blocks/%s/context/raw/json/big_maps/index/%d", id, bigmap)
   320  	info := &BigmapInfo{}
   321  	err := c.Get(ctx, u, info)
   322  	if err != nil {
   323  		return nil, err
   324  	}
   325  	return info, nil
   326  }