github.com/mavryk-network/mvgo@v1.19.9/contract/bind/marshal.go (about) 1 package bind 2 3 import ( 4 "math/big" 5 "reflect" 6 "time" 7 8 "github.com/mavryk-network/mvgo/mavryk" 9 "github.com/mavryk-network/mvgo/micheline" 10 11 "github.com/pkg/errors" 12 ) 13 14 type PrimMarshaler interface { 15 MarshalPrim(optimized bool) (micheline.Prim, error) 16 } 17 18 // MarshalPrim marshals v into a Prim by using reflection. 19 // 20 // If true, timestamps ,addresses, keys and signatures will be 21 // marshaled in their optimized format. 22 // See https://protocol.mavryk.org/active/michelson.html#differences-with-the-formal-notation. 23 func MarshalPrim(v any, optimized bool) (micheline.Prim, error) { 24 // Handle types that we can process with a type switch 25 switch t := v.(type) { 26 case micheline.Prim: 27 return t, nil 28 case PrimMarshaler: 29 return t.MarshalPrim(optimized) 30 case *big.Int: 31 return micheline.NewBig(t), nil 32 case string: 33 return micheline.NewString(t), nil 34 case bool: 35 if t { 36 return micheline.NewCode(micheline.D_TRUE), nil 37 } 38 return micheline.NewCode(micheline.D_FALSE), nil 39 case []byte: 40 return micheline.NewBytes(t), nil 41 case time.Time: 42 if optimized { 43 return micheline.NewInt64(t.Unix()), nil 44 } 45 return micheline.NewString(t.Format(time.RFC3339)), nil 46 case mavryk.Address: 47 if optimized { 48 return micheline.NewAddress(t), nil 49 } 50 return micheline.NewString(t.String()), nil 51 case mavryk.Key: 52 if optimized { 53 return micheline.NewBytes(t.Bytes()), nil 54 } 55 return micheline.NewString(t.String()), nil 56 case mavryk.Signature: 57 if optimized { 58 return micheline.NewBytes(t.Bytes()), nil 59 } 60 return micheline.NewString(t.String()), nil 61 case mavryk.ChainIdHash: 62 return micheline.NewString(t.String()), nil 63 } 64 65 // Container types 66 val := reflect.ValueOf(v) 67 if val.Kind() == reflect.Slice { 68 n := val.Len() 69 prims := make([]micheline.Prim, 0, n) 70 for i := 0; i < n; i++ { 71 prim, err := MarshalPrim(val.Index(i).Interface(), optimized) 72 if err != nil { 73 return micheline.Prim{}, err 74 } 75 prims = append(prims, prim) 76 } 77 return micheline.NewSeq(prims...), nil 78 } 79 80 return micheline.Prim{}, errors.Errorf("type not handled: %T", v) 81 } 82 83 // MarshalParams marshals the provided params into a folded Prim. 84 func MarshalParams(optimized bool, params ...any) (micheline.Prim, error) { 85 if len(params) == 0 { 86 return micheline.NewPrim(micheline.D_UNIT), nil 87 } 88 89 prims := make([]micheline.Prim, 0, len(params)) 90 for _, p := range params { 91 prim, err := MarshalPrim(p, optimized) 92 if err != nil { 93 return micheline.Prim{}, err 94 } 95 prims = append(prims, prim) 96 } 97 98 return foldRightComb(prims...), nil 99 } 100 101 // foldRightComb folds a list of prims into nested Pairs, by following the right-comb convention. 102 func foldRightComb(prims ...micheline.Prim) micheline.Prim { 103 n := len(prims) 104 switch n { 105 case 0: 106 return micheline.NewPrim(micheline.D_UNIT) 107 case 1: 108 return prims[0] 109 default: 110 return foldRightComb(append(prims[:n-2], micheline.NewPair(prims[n-2], prims[n-1]))...) 111 } 112 } 113 114 // MarshalParamsPath marshals the provided params into a Prim tree at specified paths. 115 // This function is useful to render any kind of structs (records) into a type-conform 116 // prim tree. It requires a list of tree positions in the form of paths. Both paths 117 // and params must have the same length. 118 func MarshalParamsPath(optimized bool, paths [][]int, params ...any) (micheline.Prim, error) { 119 if len(paths) != len(params) { 120 return micheline.Prim{}, errors.Errorf("invalid paths length") 121 } 122 root := emptyPair() 123 for i, p := range params { 124 prim, err := MarshalPrim(p, optimized) 125 if err != nil { 126 return micheline.Prim{}, err 127 } 128 insertPrim(&root, prim, paths[i]) 129 } 130 return root, nil 131 } 132 133 // emptyPair builds a valid pair with empty invalid children 134 func emptyPair() micheline.Prim { 135 return micheline.NewPair(micheline.Prim{}, micheline.Prim{}) 136 } 137 138 // insertPrim builds a tree of pairs in dest and inserts the src pair at specified path. 139 func insertPrim(dst *micheline.Prim, src micheline.Prim, path []int) { 140 if !dst.IsValid() { 141 *dst = emptyPair() 142 } 143 144 if len(path) == 1 { 145 dst.Args[path[0]] = src 146 return 147 } 148 149 insertPrim(&dst.Args[path[0]], src, path[1:]) 150 }