@@ -2,6 +2,7 @@ package lib
22
33import (
44 "encoding/json"
5+ "errors"
56 "fmt"
67 "reflect"
78 "strconv"
@@ -71,7 +72,7 @@ func NewMessageInfo(name string, msg any) (MessageInfo, error) {
7172
7273// NewMessageInfoFromCell attempts to decode the given cell using the provided TL-B candidates mapped by their opcodes.
7374func NewMessageInfoFromCell (t cldf.ContractType , msg * cell.Cell , tlbs map [uint64 ]interface {}) (MessageInfo , error ) {
74- typeName , m , err := DecodeJSONMapFromCell (msg , tlbs )
75+ typeName , m , err := DecodeTLBValToJSON (msg , tlbs )
7576 if err != nil {
7677 return nil , fmt .Errorf ("failed to decode message for contract %s: %w" , t , err )
7778 }
@@ -81,87 +82,140 @@ func NewMessageInfoFromCell(t cldf.ContractType, msg *cell.Cell, tlbs map[uint64
8182 return NewMessageInfo (name , m )
8283}
8384
84- // DecodeJSONMapFromCell attempts to decode the given cell using the provided TL-B candidates mapped by their opcodes.
85- func DecodeJSONMapFromCell (msg * cell.Cell , tlbs map [uint64 ]interface {}) (string , map [string ]interface {}, error ) {
86- // 1.1 Try to decode *cell.Cell as one of the TLBs type by reading the opcode
87- if msg == nil {
88- return "" , nil , & UnknownMessageError {}
89- }
85+ func DecodeTLBStructToJSON (v interface {}, tlbs map [uint64 ]interface {}) (string , map [string ]interface {}, error ) {
86+ switch t := v .(type ) {
87+ case nil :
88+ return "" , nil , errors .New ("can't decode nil as struct" )
89+ case * cell.Cell :
90+ // Try to decode *cell.Cell as one of the TLBs type by reading the opcode
91+ r := t .BeginParse ()
92+ if r .BitsLeft () == 0 {
93+ return "" , nil , & UnknownMessageError {}
94+ }
95+ opCode , err := r .PreloadUInt (32 )
96+ if err != nil {
97+ return "" , nil , fmt .Errorf ("failed to preload opcode: %w" , err )
98+ }
9099
91- r := msg .BeginParse ()
92- if r .BitsLeft () == 0 {
93- return "" , nil , & UnknownMessageError {}
94- }
95- opCode , err := r .PreloadUInt (32 )
96- if err != nil {
97- return "" , nil , fmt .Errorf ("failed to preload opcode: %w" , err )
98- }
100+ i , ok := tlbs [opCode ]
101+ if ! ok {
102+ return "" , nil , & UnknownMessageError {}
103+ }
99104
100- i , ok := tlbs [opCode ]
101- if ! ok {
102- return "" , nil , & UnknownMessageError {}
103- }
105+ // Create new instance of the candidate type
106+ rt := reflect .TypeOf (i )
107+ inst := reflect .New (rt ).Interface () // pointer to zero value
104108
105- // create new instance of the candidate type
106- rt := reflect .TypeOf (i )
107- inst := reflect .New (rt ).Interface () // pointer to zero value
109+ // Attempt decode - replace tlb.FromCell with the actual decode API you have
110+ if err = tlb .LoadFromCell (inst , r ); err != nil {
111+ return "" , nil , fmt .Errorf ("failed to decode message for opcode 0x%X: %w" , opCode , err )
112+ }
108113
109- // attempt decode - replace tlb.FromCell with the actual decode API you have
110- if err = tlb .LoadFromCell (inst , r ); err != nil {
111- return "" , nil , fmt .Errorf ("failed to decode message for opcode 0x%X: %w" , opCode , err )
112- }
114+ // Now decode loaded struct (internal *cell.Cell) fields recursively
115+ return DecodeTLBStructToJSON (inst , tlbs )
116+ default :
117+ // Iterate over the fields of the struct (reflect)
118+ rv := reflect .ValueOf (v )
119+ if rv .Kind () == reflect .Ptr {
120+ rv = rv .Elem ()
121+ }
122+ if ! rv .IsValid () {
123+ return "" , nil , fmt .Errorf ("failed to decode TLB struct - not valid value: type=%T; val=%v" , t , rv )
124+ }
113125
114- // Now decode internal *cell.Cell fields recursively
115- // 2.1. Iterate over the fields of the struct (reflect)
116- ckeys := make ([]string , 0 )
117- for i := 0 ; i < rt .NumField (); i ++ {
118- f := rt .Field (i )
119-
120- // 2.2. For each field, check if it's of type *cell.Cell
121- if f .Type == reflect .TypeOf (& cell.Cell {}) {
122- // 2.3. If so, check the json tag to determine the expected key
123- k := f .Name
124- jsonTag := f .Tag .Get ("json" )
126+ if rv .Kind () != reflect .Struct {
127+ return "" , nil , fmt .Errorf ("unable to decode as JSON map - not a structure: type=%T; val=%v" , t , rv )
128+ }
129+
130+ out := make (map [string ]interface {}, rv .NumField ())
131+ rt := rv .Type ()
132+ for i := 0 ; i < rv .NumField (); i ++ {
133+ sf := rt .Field (i )
134+ // skip unexported fields (e.g. the magic field)
135+ if sf .PkgPath != "" {
136+ continue
137+ }
138+
139+ // check the json tag to determine the expected key
140+ k := sf .Name
141+ jsonTag := sf .Tag .Get ("json" )
125142 if jsonTag != "" {
126143 k = strings .Split (jsonTag , "," )[0 ] // parse json tag options (key)
127144 }
128145
129- // 2.4. Source a set of keys that we need to decode recursively
130- ckeys = append (ckeys , k )
146+ fv := rv .Field (i )
147+ _ , decoded , err := DecodeTLBValToJSON (fv .Interface (), tlbs )
148+ if err != nil {
149+ return "" , nil , fmt .Errorf ("failed to decode TLB value: %w" , err )
150+ }
151+ out [k ] = decoded
131152 }
153+ return rt .Name (), out , nil
132154 }
155+ }
133156
134- // 3.1. Decode the struct as JSON map[string]interface{} (default *cell.Cell marshalling)
135- var rawMap map [string ]interface {}
136- rawBytes , err := json .Marshal (inst )
137- if err != nil {
138- return "" , nil , fmt .Errorf ("failed to marshal decoded message to JSON: %w" , err )
139- }
140- err = json .Unmarshal (rawBytes , & rawMap )
141- if err != nil {
142- return "" , nil , fmt .Errorf ("failed to unmarshal decoded message JSON to map: %w" , err )
143- }
144-
145- // 3.2. For each key in the sourced set, get the *cell.Cell value (decode from BOC)
146- for _ , ck := range ckeys {
147- cBOC := rawMap [ck ]
157+ func DecodeTLBValToJSON (v interface {}, tlbs map [uint64 ]interface {}) (string , interface {}, error ) {
158+ switch t := v .(type ) {
159+ case nil :
160+ return "<nil>" , nil , nil
161+ case * cell.Cell :
162+ typeName , decoded , err := DecodeTLBStructToJSON (t , tlbs )
163+ if err != nil {
164+ return "Cell" , t , nil // fallback if not a known struct
165+ }
148166
149- cVal := & cell.Cell {}
150- if err := json .Unmarshal ([]byte (strconv .Quote (cBOC .(string ))), cVal ); err != nil {
151- return "" , nil , fmt .Errorf ("failed to unmarshal BOC to cell: %s: %s: %w" , ck , cBOC , err )
167+ return typeName , decoded , nil
168+ default :
169+ // for slices/arrays/structs/maps repeat normalization recursively
170+ rv := reflect .ValueOf (t )
171+ if ! rv .IsValid () {
172+ return "<invalid>" , nil , nil
152173 }
153174
154- // 3.3. Try to decode recursively
155- _ , cMap , err := DecodeJSONMapFromCell (cVal , tlbs )
156- if err != nil {
157- // fallback to original BOC representation if fails
158- continue
175+ switch rv .Kind () {
176+ case reflect .Slice , reflect .Array :
177+ out := make ([]interface {}, rv .Len ())
178+ for i := 0 ; i < rv .Len (); i ++ {
179+ _ , decoded , err := DecodeTLBValToJSON (rv .Index (i ).Interface (), tlbs )
180+ if err != nil {
181+ return "" , nil , err
182+ }
183+ out [i ] = decoded
184+ }
185+ return rv .Type ().String (), out , nil
186+ case reflect .Map :
187+ m := map [string ]interface {}{}
188+ for _ , k := range rv .MapKeys () {
189+ keyStr := fmt .Sprint (k .Interface ())
190+ _ , decoded , err := DecodeTLBValToJSON (rv .MapIndex (k ).Interface (), tlbs )
191+ if err != nil {
192+ return "" , nil , err
193+ }
194+ m [keyStr ] = decoded
195+ }
196+ return rv .Type ().String (), m , nil
197+ case reflect .Struct :
198+ // recurse on nested struct
199+ // create pointer to struct so DecodeTLBStructToJSON can handle exported fields
200+ ptr := reflect .New (rv .Type ()).Interface ()
201+ reflect .ValueOf (ptr ).Elem ().Set (rv )
202+
203+ // if there is a json.Marshaler (either on the value or the pointer), prefer it.
204+ jmType := reflect .TypeOf ((* json .Marshaler )(nil )).Elem ()
205+ if rv .CanAddr () && rv .Addr ().Type ().Implements (jmType ) || rv .Type ().Implements (jmType ) {
206+ return "" , v , nil
207+ }
208+
209+ typeName , decoded , err := DecodeTLBStructToJSON (ptr , tlbs )
210+ if err != nil {
211+ return "" , nil , fmt .Errorf ("failed to decode TLB struct: %w; val=%v" , err , t )
212+ }
213+
214+ return typeName , decoded , nil
215+ default :
216+ return rv .Type ().Name (), t , nil
159217 }
160- rawMap [ck ] = cMap
161218 }
162-
163- return rt .Name (), rawMap , nil
164- // 4.4 Finally, marshal the final map[string]interface{} as JSON string
165219}
166220
167221func MustNewTLBMap (types []interface {}) map [uint64 ]interface {} {
@@ -177,9 +231,8 @@ func MustNewTLBMap(types []interface{}) map[uint64]interface{} {
177231func NewTLBMap (types []interface {}) (map [uint64 ]interface {}, error ) {
178232 tlbs := make (map [uint64 ]interface {})
179233 for _ , typ := range types {
180- // Use reflection to get the magic number from the type
234+ // reflect to get the magic number from the struct
181235 rt := reflect .TypeOf (typ )
182-
183236 if rt .Field (0 ).Type != reflect .TypeOf (tlb.Magic {}) {
184237 return nil , fmt .Errorf ("first field of %s is not of type Magic" , rt .Name ())
185238 }
0 commit comments