1
1
package scalecodec
2
2
3
3
import (
4
+ "encoding/json"
5
+ "errors"
4
6
"fmt"
5
7
6
8
scaleType "github.com/itering/scale.go/types"
7
9
"github.com/itering/scale.go/types/scaleBytes"
8
10
"github.com/itering/scale.go/utiles"
11
+ "github.com/shopspring/decimal"
9
12
"golang.org/x/crypto/blake2b"
10
13
)
11
14
@@ -73,6 +76,25 @@ func (e *ExtrinsicDecoder) generateHash() string {
73
76
return utiles .BytesToHex (h )
74
77
}
75
78
79
+ type GenericExtrinsic struct {
80
+ VersionInfo string `json:"version_info"`
81
+ ExtrinsicLength int `json:"extrinsic_length"`
82
+ AddressType string `json:"address_type"`
83
+ Tip decimal.Decimal `json:"tip"`
84
+ SignedExtensions map [string ]interface {} `json:"signed_extensions"`
85
+ AccountId interface {} `json:"accountId"`
86
+ Signer interface {} `json:"signer"` // map[string]interface or string
87
+ Signature string `json:"signature"`
88
+ SignatureRaw interface {} `json:"signature_raw"` // map[string]interface or string
89
+ Nonce int `json:"nonce"`
90
+ Era string `json:"era"`
91
+ ExtrinsicHash string `json:"extrinsic_hash"`
92
+ CallModuleFunction string `json:"call_module_function"`
93
+ CallCode string `json:"call_code"`
94
+ CallModule string `json:"call_module"`
95
+ Params []ExtrinsicParam `json:"params"`
96
+ }
97
+
76
98
func (e * ExtrinsicDecoder ) Process () {
77
99
e .ExtrinsicLength = e .ProcessAndUpdateData ("Compact<u32>" ).(int )
78
100
if e .ExtrinsicLength != e .Data .GetRemainingLength () {
@@ -84,27 +106,28 @@ func (e *ExtrinsicDecoder) Process() {
84
106
85
107
e .ContainsTransaction = utiles .U256 (e .VersionInfo ).Int64 () >= 80
86
108
87
- result := map [ string ] interface {} {
88
- "extrinsic_length" : e .ExtrinsicLength ,
89
- "version_info" : e .VersionInfo ,
109
+ result := GenericExtrinsic {
110
+ ExtrinsicLength : e .ExtrinsicLength ,
111
+ VersionInfo : e .VersionInfo ,
90
112
}
113
+
91
114
if e .VersionInfo == "04" || e .VersionInfo == "84" {
92
115
if e .ContainsTransaction {
93
116
// Address
94
- address : = e .ProcessAndUpdateData (utiles .TrueOrElse (e .Metadata .MetadataVersion >= 14 && scaleType .HasReg ("ExtrinsicSigner" ), "ExtrinsicSigner" , "Address" ))
95
- switch v := address .(type ) {
117
+ result . Signer = e .ProcessAndUpdateData (utiles .TrueOrElse (e .Metadata .MetadataVersion >= 14 && scaleType .HasReg ("ExtrinsicSigner" ), "ExtrinsicSigner" , "Address" ))
118
+ switch v := result . Signer .(type ) {
96
119
case string :
97
120
e .Address = v
98
- result [ "address_type" ] = "AccountId"
121
+ result . AddressType = "AccountId"
99
122
case map [string ]interface {}:
100
123
for name , value := range v {
101
- result [ "address_type" ] = name
124
+ result . AddressType = name
102
125
e .Address = value
103
126
}
104
127
}
105
128
// ExtrinsicSignature
106
- signature : = e .ProcessAndUpdateData ("ExtrinsicSignature" )
107
- switch v := signature .(type ) {
129
+ result . SignatureRaw = e .ProcessAndUpdateData ("ExtrinsicSignature" )
130
+ switch v := result . SignatureRaw .(type ) {
108
131
case string :
109
132
e .Signature = v
110
133
case map [string ]interface {}:
@@ -116,25 +139,25 @@ func (e *ExtrinsicDecoder) Process() {
116
139
e .Nonce = int (e .ProcessAndUpdateData ("Compact<U64>" ).(uint64 ))
117
140
if e .Metadata .Extrinsic != nil {
118
141
if utiles .SliceIndex ("ChargeTransactionPayment" , e .Metadata .Extrinsic .SignedIdentifier ) != - 1 {
119
- result [ "tip" ] = e .ProcessAndUpdateData ("Compact<Balance>" )
142
+ result . Tip = e .ProcessAndUpdateData ("Compact<Balance>" ).(decimal. Decimal )
120
143
}
121
144
} else {
122
- result [ "tip" ] = e .ProcessAndUpdateData ("Compact<Balance>" )
145
+ result . Tip = e .ProcessAndUpdateData ("Compact<Balance>" ).(decimal. Decimal )
123
146
}
124
147
// spec SignedExtensions
125
148
if len (e .SignedExtensions ) > 0 {
126
149
for _ , extension := range e .SignedExtensions {
127
150
if utiles .SliceIndex (extension .Name , e .Metadata .Extrinsic .SignedIdentifier ) != - 1 {
128
151
for _ , v := range extension .AdditionalSigned {
129
- result [v .Name ] = e .ProcessAndUpdateData (v .Type )
152
+ result . SignedExtensions [v .Name ] = e .ProcessAndUpdateData (v .Type )
130
153
}
131
154
}
132
155
}
133
156
} else {
134
157
if e .Metadata .MetadataVersion >= 14 {
135
158
for _ , ext := range e .Metadata .Extrinsic .SignedExtensions {
136
159
if enable , ok := signedExts [ext .Identifier ]; ok && enable {
137
- result [ext .Identifier ] = e .ProcessAndUpdateData (ext .TypeString )
160
+ result . SignedExtensions [ext .Identifier ] = e .ProcessAndUpdateData (ext .TypeString )
138
161
}
139
162
}
140
163
}
@@ -160,21 +183,81 @@ func (e *ExtrinsicDecoder) Process() {
160
183
}
161
184
162
185
if e .ContainsTransaction {
163
- result [ "account_id" ] = e .Address
164
- result [ "signature" ] = e .Signature
165
- result [ "nonce" ] = e .Nonce
166
- result [ "era" ] = e .Era
167
- result [ "extrinsic_hash" ] = e .ExtrinsicHash
186
+ result . AccountId = e .Address
187
+ result . Signature = e .Signature
188
+ result . Nonce = e .Nonce
189
+ result . Era = e .Era
190
+ result . ExtrinsicHash = e .ExtrinsicHash
168
191
}
192
+ result .CallCode = e .CallIndex
193
+ result .CallModuleFunction = call .Call .Name
194
+ result .CallModule = call .Module .Name
195
+ result .Params = e .Params
196
+ e .Value = & result
197
+ }
198
+
199
+ /*
200
+ Encode extrinsic with option
201
+ opt.Metadata is required
202
+ return hex string, if error, return empty string
169
203
170
- if e .CallIndex != "" {
171
- result ["call_code" ] = e .CallIndex
172
- result ["call_module_function" ] = call .Call .Name
173
- result ["call_module" ] = call .Module .Name
204
+ Example:
205
+ m := scalecodec.MetadataDecoder{}
206
+ m.Init(utiles.HexToBytes(Kusama9370))
207
+ _ = m.Process()
208
+ option := types.ScaleDecoderOption{Metadata: &m.Metadata}
209
+ genericExtrinsic := scalecodec.GenericExtrinsic{
210
+ VersionInfo: "84",
211
+ CallCode: "0400",
212
+ Nonce: 0,
213
+ Era: "00",
214
+ Signer: map[string]interface{}{"Id": "0xe673cb35ffaaf7ab98c4e9268bfa9b4a74e49d41c8225121c346db7a7dd06d88"},
215
+ SignatureRaw: map[string]interface{}{"Ed25519": "0xfce9453b1442bba86c2781e755a29c8a215ccf4b65ce81eeaa5b5a04dcdb79a54525cc86969f910c71c05f84aeab9c205022ecd4aa2abb4a3c3667f09dd16e0b"},
216
+ Params: []scalecodec.ExtrinsicParam{
217
+ {Value: map[string]interface{}{"Id": "0x0770e0831a275b534f7507c8ebd9f5f982a55053c9dc672da886ef41a6b5c628"}}, {Value: "1094000000000"},
218
+ },
219
+ }
220
+ fmt.Println(genericExtrinsic.Encode(&option))
221
+ */
222
+
223
+ func (g * GenericExtrinsic ) Encode (opt * scaleType.ScaleDecoderOption ) (string , error ) {
224
+ if opt .Metadata == nil {
225
+ return "" , errors .New ("invalid metadata" )
226
+ }
227
+ data := g .VersionInfo
228
+ if g .VersionInfo == "84" {
229
+ data = data + scaleType .Encode (utiles .TrueOrElse (opt .Metadata .MetadataVersion >= 14 && scaleType .HasReg ("ExtrinsicSigner" ), "ExtrinsicSigner" , "AccountId" ), g .Signer ) // accountId
230
+ data = data + scaleType .Encode ("ExtrinsicSignature" , g .SignatureRaw ) // signature
231
+ data = data + scaleType .Encode ("EraExtrinsic" , g .Era ) // era
232
+ data = data + scaleType .Encode ("Compact<U64>" , g .Nonce ) // nonce
233
+ data = data + scaleType .Encode ("Compact<Balance>" , g .Tip ) // tip
174
234
}
175
235
176
- result ["nonce" ] = e .Nonce
177
- result ["era" ] = e .Era
178
- result ["params" ] = e .Params
179
- e .Value = result
236
+ data = data + g .CallCode
237
+ call , ok := opt .Metadata .CallIndex [g .CallCode ]
238
+
239
+ if ! ok {
240
+ return "" , fmt .Errorf ("not find Extrinsic Lookup %s, please check metadata info" , g .CallCode )
241
+ }
242
+
243
+ if len (g .Params ) != len (call .Call .Args ) {
244
+ return "" , fmt .Errorf ("extrinsic params length not match, expect %d, got %d" , len (call .Call .Args ), len (g .Params ))
245
+ }
246
+
247
+ for index , arg := range call .Call .Args {
248
+ data = data + utiles .TrimHex (scaleType .EncodeWithOpt (arg .Type , g .Params [index ].Value , opt ))
249
+ }
250
+
251
+ return scaleType .Encode ("Compact<u32>" , len (utiles .HexToBytes (data ))) + data , nil
252
+ }
253
+
254
+ // ToMap GenericExtrinsic convert to map[string]interface
255
+ func (g * GenericExtrinsic ) ToMap () map [string ]interface {} {
256
+ var r map [string ]interface {}
257
+ b , err := json .Marshal (g )
258
+ if err != nil {
259
+ return nil
260
+ }
261
+ _ = json .Unmarshal (b , & r )
262
+ return r
180
263
}
0 commit comments