@@ -71,18 +71,35 @@ func NewMessageInfo(name string, msg any) (MessageInfo, error) {
7171
7272// NewMessageInfoFromCell attempts to decode the given cell using the provided TL-B candidates mapped by their opcodes.
7373func NewMessageInfoFromCell (t cldf.ContractType , msg * cell.Cell , tlbs map [uint64 ]interface {}) (MessageInfo , error ) {
74+ typeName , m , err := DecodeJSONMapFromCell (t , msg , tlbs )
75+ if err != nil {
76+ return nil , fmt .Errorf ("failed to decode message for contract %s: %w" , t , err )
77+ }
78+
79+ name := fmt .Sprintf ("%s:%s" , t , typeName )
80+ // 4.4 Finally, marshal the final map[string]interface{} as JSON string
81+ return NewMessageInfo (name , m )
82+ }
83+
84+ // DecodeJSONMapFromCell attempts to decode the given cell using the provided TL-B candidates mapped by their opcodes.
85+ func DecodeJSONMapFromCell (t cldf.ContractType , 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+ }
90+
7491 r := msg .BeginParse ()
7592 if r .BitsLeft () == 0 {
76- return nil , & UnknownMessageError {}
93+ return "" , nil , & UnknownMessageError {}
7794 }
7895 opCode , err := r .PreloadUInt (32 )
7996 if err != nil {
80- return nil , fmt .Errorf ("failed to preload opcode: %w" , err )
97+ return "" , nil , fmt .Errorf ("failed to preload opcode: %w" , err )
8198 }
8299
83100 i , ok := tlbs [opCode ]
84101 if ! ok {
85- return nil , & UnknownMessageError {}
102+ return "" , nil , & UnknownMessageError {}
86103 }
87104
88105 // create new instance of the candidate type
@@ -91,11 +108,60 @@ func NewMessageInfoFromCell(t cldf.ContractType, msg *cell.Cell, tlbs map[uint64
91108
92109 // attempt decode - replace tlb.FromCell with the actual decode API you have
93110 if err := tlb .LoadFromCell (inst , r ); err != nil {
94- return nil , fmt .Errorf ("failed to decode OnRamp message for opcode 0x%X: %w" , opCode , err )
111+ return "" , nil , fmt .Errorf ("failed to decode message for opcode 0x%X: %w" , opCode , err )
112+ }
113+
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" )
125+ if jsonTag != "" {
126+ k = strings .Split (jsonTag , "," )[0 ] // parse json tag options (key)
127+ }
128+
129+ // 2.4. Source a set of keys that we need to decode recursively
130+ ckeys = append (ckeys , k )
131+ }
132+ }
133+
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 )
95143 }
96144
97- name := fmt .Sprintf ("%s:%s" , t , rt .Name ())
98- return NewMessageInfo (name , inst )
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 ]
148+
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 )
152+ }
153+
154+ // 3.3. Try to decode recursively using NewMessageInfoFromCell
155+ _ , cMap , err := DecodeJSONMapFromCell (t , cVal , tlbs )
156+ if err != nil {
157+ // fallback to original BOC representation if fails
158+ continue
159+ }
160+ rawMap [ck ] = cMap
161+ }
162+
163+ return rt .Name (), rawMap , nil
164+ // 4.4 Finally, marshal the final map[string]interface{} as JSON string
99165}
100166
101167func MustNewTLBMap (types []interface {}) map [uint64 ]interface {} {
@@ -106,14 +172,18 @@ func MustNewTLBMap(types []interface{}) map[uint64]interface{} {
106172 return tlbs
107173}
108174
109- // NewTLBMap creates a map of TL-B magic numbers to their corresponding types.
110- // The input is a slice of TL-B struct instances.
175+ // NewTLBMap creates a map of TL-B magic numbers (opcodes) to their corresponding types
176+ // from a set of TL-B annotated struct instances.
111177func NewTLBMap (types []interface {}) (map [uint64 ]interface {}, error ) {
112178 tlbs := make (map [uint64 ]interface {})
113179 for _ , typ := range types {
114180 // Use reflection to get the magic number from the type
115181 rt := reflect .TypeOf (typ )
116182
183+ if rt .Field (0 ).Type != reflect .TypeOf (tlb.Magic {}) {
184+ return nil , fmt .Errorf ("first field of %s is not of type Magic" , rt .Name ())
185+ }
186+
117187 magicTag := rt .Field (0 ).Tag .Get ("tlb" )
118188 magic , err := loadMagic (magicTag )
119189 if err != nil {
0 commit comments