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 }