github.com/mavryk-network/mvgo@v1.19.9/contract/bind/bigmap.go (about)

     1  package bind
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strconv"
     7  
     8  	"github.com/mavryk-network/mvgo/micheline"
     9  	"github.com/mavryk-network/mvgo/rpc"
    10  	"github.com/pkg/errors"
    11  )
    12  
    13  // Bigmap is a handle to a Tezos bigmap.
    14  //
    15  // It has two type parameters, K and V, which are determined when a contract
    16  // script is parsed.
    17  //
    18  // Before using a Bigmap to Get a value, its RPC client must be set with SetRPC.
    19  type Bigmap[K, V any] struct {
    20  	id      int64
    21  	keyType *micheline.Type
    22  	rpc     RPC
    23  	m       []MapEntry[K, V]
    24  }
    25  
    26  // NewBigmap returns a new Bigmap that points to the given bigmap id.
    27  //
    28  // The type parameters must match the key and value type of the corresponding
    29  // bigmap.
    30  func NewBigmap[K, V any](id int64) Bigmap[K, V] {
    31  	return Bigmap[K, V]{id: id}
    32  }
    33  
    34  // ID returns the id of the bigmap.
    35  func (b *Bigmap[K, B]) ID() int64 {
    36  	return b.id
    37  }
    38  
    39  func (b *Bigmap[K, V]) SetContent(elt ...MapEntry[K, V]) {
    40  	b.m = elt
    41  }
    42  
    43  // SetRPC defines the client to use when getting a value from the bigmap.
    44  func (b *Bigmap[K, B]) SetRPC(client RPC) *Bigmap[K, B] {
    45  	b.rpc = client
    46  	return b
    47  }
    48  
    49  // SetKeyType forces the key micheline type to use, when marshaling a key
    50  // into an expression hash.
    51  func (b *Bigmap[K, B]) SetKeyType(keyType micheline.Type) *Bigmap[K, B] {
    52  	b.keyType = &keyType
    53  	return b
    54  }
    55  
    56  // Get the value corresponding to the given key.
    57  //
    58  // This makes a rpc call, so SetRPC must have been called before calling this.
    59  //
    60  // If the key doesn't exist in the bigmap, an ErrKeyNotFound is returned.
    61  func (b *Bigmap[K, V]) Get(ctx context.Context, key K) (v V, err error) {
    62  	if b.rpc == nil {
    63  		return v, errors.New("rpc not set in bigmap")
    64  	}
    65  
    66  	keyVal, err := MarshalPrim(key, true)
    67  	if err != nil {
    68  		return v, err
    69  	}
    70  
    71  	if b.keyType == nil {
    72  		b.SetKeyType(keyVal.BuildType())
    73  	}
    74  
    75  	k, err := micheline.NewKey(*b.keyType, keyVal)
    76  	if err != nil {
    77  		return v, err
    78  	}
    79  
    80  	keyHash := k.Hash()
    81  
    82  	prim, err := b.rpc.GetBigmapValue(ctx, b.id, keyHash, rpc.Head)
    83  	if err != nil {
    84  		var httpError rpc.HTTPError
    85  		if errors.As(err, &httpError) && httpError.StatusCode() == 404 {
    86  			return v, &ErrKeyNotFound{Key: keyHash.String()}
    87  		}
    88  		return v, err
    89  	}
    90  
    91  	if len(prim.Args) > 2 {
    92  		prim.Type = micheline.PrimSequence
    93  		prim = prim.FoldPair()
    94  	}
    95  
    96  	if err = UnmarshalPrim(prim, &v); err != nil {
    97  		return v, err
    98  	}
    99  
   100  	return v, nil
   101  }
   102  
   103  func (b Bigmap[K, V]) String() string {
   104  	return "Bigmap#" + strconv.Itoa(int(b.id))
   105  }
   106  
   107  func (b Bigmap[K, V]) MarshalPrim(optimized bool) (micheline.Prim, error) {
   108  	entries := make([]micheline.Prim, 0, len(b.m))
   109  	for _, value := range b.m {
   110  		keyPrim, err := MarshalPrim(value.Key, optimized)
   111  		if err != nil {
   112  			return micheline.Prim{}, errors.Wrap(err, "failed to marshal key")
   113  		}
   114  		valuePrim, err := MarshalPrim(value.Value, optimized)
   115  		if err != nil {
   116  			return micheline.Prim{}, errors.Wrap(err, "failed to marshal value")
   117  		}
   118  		entries = append(entries, micheline.NewMapElem(keyPrim, valuePrim))
   119  	}
   120  	return micheline.NewSeq(entries...), nil
   121  }
   122  
   123  func (b *Bigmap[K, V]) UnmarshalPrim(prim micheline.Prim) error {
   124  	if prim.Int != nil {
   125  		*b = NewBigmap[K, V](prim.Int.Int64())
   126  		return nil
   127  	}
   128  	if prim.Type != micheline.PrimSequence {
   129  		return fmt.Errorf("not supported type for unmarshall")
   130  	}
   131  	*b = NewBigmap[K, V](0)
   132  	for _, entry := range prim.Args {
   133  		if entry.OpCode != micheline.D_ELT {
   134  			return errors.Errorf("bigmap entries should be ELT, got %s", prim.OpCode)
   135  		}
   136  		if len(entry.Args) != 2 {
   137  			return errors.New("prim ELT should have 2 args")
   138  		}
   139  		var key K
   140  		var value V
   141  		if err := UnmarshalPrim(entry.Args[0], &key); err != nil {
   142  			return errors.Wrap(err, "failed to unmarshal key")
   143  		}
   144  		if err := UnmarshalPrim(entry.Args[1], &value); err != nil {
   145  			return errors.Wrap(err, "failed to unmarshal value")
   146  		}
   147  		b.m = append(b.m, MapEntry[K, V]{Key: key, Value: value})
   148  	}
   149  	return nil
   150  }
   151  
   152  type ErrKeyNotFound struct {
   153  	Key string
   154  }
   155  
   156  func (e *ErrKeyNotFound) Error() string {
   157  	return fmt.Sprintf("bigmap key not found %q", e.Key)
   158  }
   159  
   160  func (e *ErrKeyNotFound) Is(target error) bool {
   161  	other, ok := target.(*ErrKeyNotFound)
   162  	if !ok {
   163  		return false
   164  	}
   165  	return e.Key == other.Key
   166  }