├── README.md ├── base ├── base.go └── ss58-registry.json ├── client └── client.go ├── cmd └── cretetypes │ ├── file │ ├── acala.json │ ├── base.json │ ├── cring.json │ ├── crust.json │ ├── kusama.json │ ├── polkadot.json │ ├── ring.json │ └── rio.json │ └── main.go ├── expand ├── acala │ └── types.go ├── base │ └── types.go ├── calc_fee.go ├── call.go ├── chainX │ └── types.go ├── crust │ └── types.go ├── darwinia │ └── types.go ├── event_records.go ├── extrinsic_decode.go ├── metadata.go ├── ori │ └── types.go ├── polkadot │ └── types.go ├── prefix.go ├── rio │ └── types.go ├── serde.go ├── stafi │ └── types.go └── types.go ├── go.mod ├── go.sum ├── models ├── chain.go └── model.go ├── test ├── address_test.go ├── chainX_test.go ├── chain_test.go ├── decode_test.go ├── extrinsic_test.go ├── rpc_test.go ├── storage_test.go ├── tx2_test.go └── tx_test.go ├── tx ├── tx.go └── tx2.go ├── uint128 └── uint128.go └── utils └── util.go /README.md: -------------------------------------------------------------------------------- 1 | # stafi go sdk 2 | **************** 如果要使用(2021-02-23升级)polkadot,请使用https://github.com/JFJun/bifrost-go.git **************************** 3 | ## 介绍 4 | 1. 该包不仅可用于波卡系列的区块解析以及离线签名。 5 | 目前该包支持以下币种: 6 | stafi(FIS),kusama(KSM),chainX2.0(PCX),darwinia(CRING,RING) 7 | 2. 这个包其实是对github.com/JFJun/substrate-go包的升级,所以功能与两者相似,只不过这个包更简洁更稳定。 8 | 3. 发送交易建议使用新的方法,功能都是一样,进行了简单的封装,相对之前看着更加简洁,可以查看相应的测试方法: 9 | https://github.com/JFJun/stafi-substrate-go/test/tx2_test.go 10 | ## 使用 11 | ### 1. 解析区块 12 | // 初始化客户端 13 | c, err := client.New("wss://rpc.polkadot.io") 14 | if err != nil { 15 | t.Fatal(err) 16 | } 17 | //expand.SetSerDeOptions(false) 18 | // 设置地址的前缀,默认是 0x2a 19 | c.SetPrefix(ss58.StafiPrefix) 20 | resp, err := c.GetBlockByNumber(2517230) 21 | if err != nil { 22 | t.Fatal(err) 23 | } 24 | d, _ := json.Marshal(resp) 25 | fmt.Println(string(d)) 26 | 27 | ### 2. Balances.transfer转账 28 | // 1. 初始化客户端 29 | c,err:=client.New("wss://crab.darwinia.network") 30 | if err != nil { 31 | t.Fatal(err) 32 | } 33 | //2. 下面这句代码因为每个链的不同而不同,我看源代码是因为一个Indices这个module的有无决定的 34 | // 反正如果提交交易包含 MultiSignature错误什么的,就把 这个bool值设置为相反就行了 35 | expand.SetSerDeOptions(false) 36 | //3. 设置链的前缀 37 | c.SetPrefix(ss58.StafiPrefix) 38 | //4。 创建交易 39 | acc,err := c.GetAccountInfo(from) 40 | if err != nil { 41 | t.Fatal(err) 42 | } 43 | nonce := uint64(acc.Nonce) 44 | transaction:=tx.CreateTransaction(from,to,amount,nonce) 45 | //5. 设置交易签名需要的参数 46 | callIdx,err:=ed.MV.GetCallIndex("Balances","transfer") 47 | if err != nil { 48 | t.Fatal(err) 49 | } 50 | transaction.SetGenesisHashAndBlockHash(c.GetGenesisHash(),c.GetGenesisHash()) 51 | .SetSpecVersionAndCallId(uint32(c.SpecVersion),uint32(c.TransactionVersion),callIdx) 52 | .SetTip(0) //自行选择设置,可增加给矿工的手续费 53 | .SetEra(0,0) //自行选择设置,设置交易如果处于pending状态,最多能存活多少个块 54 | //6. 签名交易 55 | tt,err:=transaction.SignTransaction("私钥",crypto.Sr25519Type) 56 | if err != nil { 57 | t.Fatal(err) 58 | } 59 | // 7. 提交交易 60 | var result interface{} 61 | err = c.C.Client.Call(&result,"author_submitExtrinsic",tt) 62 | if err != nil { 63 | t.Fatal(err) 64 | } 65 | txid:=result.(string) 66 | fmt.Println(txid) 67 | 68 | ### 3. Balances.transfer_keep_alive转账 69 | *** 这个转账模式我没有试过,不过按理来说适合Balances.transfer一样的 *** 70 | 将Balances.transfer中的第5部获取callIdx改为 71 | callIdx,err:=ed.MV.GetCallIndex("Balances","transfer_keep_alive") 72 | if err != nil { 73 | t.Fatal(err) 74 | } 75 | 其他的都和Balances.transfer一样 76 | 77 | ### 4. Utility.batch转账 78 | 将Balances.transfer的第四部改为: 79 | pa:=map[string]int64{ 80 | "to1":amount1, 81 | "to2"amount2, 82 | ... 83 | } 84 | uitlityCallIdx,err:=ed.MV.GetCallIndex("Utility","batch") 85 | if err != nil { 86 | t.Fatal(err) 87 | } 88 | acc,err := c.GetAccountInfo(from) 89 | if err != nil { 90 | t.Fatal(err) 91 | } 92 | nonce := uint64(acc.Nonce) 93 | transaction:=tx.CreateUtilityBatchTransaction(from,nonce,pa,uitlityCallIdx) 94 | 95 | 其他的步骤和Balances.transfer是一样的 96 | -------------------------------------------------------------------------------- /base/ss58-registry.json: -------------------------------------------------------------------------------- 1 | { 2 | "specification": "https://github.com/paritytech/substrate/wiki/External-Address-Format-(SS58)", 3 | "schema": { 4 | "prefix": "The address prefix. Must be an integer and unique.", 5 | "network": "Unique identifier for the network that will use this prefix, string, no spaces. To integrate with CLI tools, e.g. `--network polkadot`.", 6 | "displayName": "The name of the network that will use this prefix, in a format friendly for display.", 7 | "symbols": "Array of symbols of any tokens the chain uses, usually 2-5 characters. Most chains will only have one. Chains that have multiple instances of the Balances pallet should order the array by instance.", 8 | "decimals": "Array of integers representing the number of decimals that represent a single unit to the end user. Must be same length as `symbols` to represent each token's denomination.", 9 | "standardAccount": "Signing curve for standard account. Substrate supports ed25519, sr25519, and secp256k1.", 10 | "website": "A website or Github repo associated with the network." 11 | }, 12 | "registry": [ 13 | { 14 | "prefix": 0, 15 | "network": "polkadot", 16 | "displayName": "Polkadot Relay Chain", 17 | "symbols": ["DOT"], 18 | "decimals": [10], 19 | "standardAccount": "*25519", 20 | "website": "https://polkadot.network" 21 | }, 22 | { 23 | "prefix": 1, 24 | "network": "reserved1", 25 | "displayName": "This prefix is reserved.", 26 | "symbols": null, 27 | "decimals": null, 28 | "standardAccount": null, 29 | "website": null 30 | }, 31 | { 32 | "prefix": 2, 33 | "network": "kusama", 34 | "displayName": "Kusama Relay Chain", 35 | "symbols": ["KSM"], 36 | "decimals": [12], 37 | "standardAccount": "*25519", 38 | "website": "https://kusama.network" 39 | }, 40 | { 41 | "prefix": 3, 42 | "network": "reserved3", 43 | "displayName": "This prefix is reserved.", 44 | "symbols": null, 45 | "decimals": null, 46 | "standardAccount": null, 47 | "website": null 48 | }, 49 | { 50 | "prefix": 4, 51 | "network": "katalchain", 52 | "displayName": "Katal Chain", 53 | "symbols": null, 54 | "decimals": null, 55 | "standardAccount": "*25519", 56 | "website": null 57 | }, 58 | { 59 | "prefix": 5, 60 | "network": "plasm", 61 | "displayName": "Plasm Network", 62 | "symbols": ["PLM"], 63 | "decimals": null, 64 | "standardAccount": "*25519", 65 | "website": null 66 | }, 67 | { 68 | "prefix": 6, 69 | "network": "bifrost", 70 | "displayName": "Bifrost", 71 | "symbols": ["BNC"], 72 | "decimals": [12], 73 | "standardAccount": "*25519", 74 | "website": "https://bifrost.finance/" 75 | }, 76 | { 77 | "prefix": 7, 78 | "network": "edgeware", 79 | "displayName": "Edgeware", 80 | "symbols": ["EDG"], 81 | "decimals": [18], 82 | "standardAccount": "*25519", 83 | "website": "https://edgewa.re" 84 | }, 85 | { 86 | "prefix": 8, 87 | "network": "karura", 88 | "displayName": "Acala Karura Canary", 89 | "symbols": ["KAR"], 90 | "decimals": [18], 91 | "standardAccount": "*25519", 92 | "website": "https://acala.network/" 93 | }, 94 | { 95 | "prefix": 9, 96 | "network": "reynolds", 97 | "displayName": "Laminar Reynolds Canary", 98 | "symbols": ["REY"], 99 | "decimals": [18], 100 | "standardAccount": "*25519", 101 | "website": "http://laminar.network/" 102 | }, 103 | { 104 | "prefix": 10, 105 | "network": "acala", 106 | "displayName": "Acala", 107 | "symbols": ["ACA"], 108 | "decimals": [18], 109 | "standardAccount": "*25519", 110 | "website": "https://acala.network/" 111 | }, 112 | { 113 | "prefix": 11, 114 | "network": "laminar", 115 | "displayName": "Laminar", 116 | "symbols": ["LAMI"], 117 | "decimals": [18], 118 | "standardAccount": "*25519", 119 | "website": "http://laminar.network/" 120 | }, 121 | { 122 | "prefix": 12, 123 | "network": "polymath", 124 | "displayName": "Polymath", 125 | "symbols": null, 126 | "decimals": null, 127 | "standardAccount": "*25519", 128 | "website": null 129 | }, 130 | { 131 | "prefix": 13, 132 | "network": "substratee", 133 | "displayName": "SubstraTEE", 134 | "symbols": null, 135 | "decimals": null, 136 | "standardAccount": "*25519", 137 | "website": "https://www.substratee.com" 138 | }, 139 | { 140 | "prefix": 14, 141 | "network": "totem", 142 | "displayName": "Totem", 143 | "symbols": ["XTX"], 144 | "decimals": [0], 145 | "standardAccount": "*25519", 146 | "website": "https://totemaccounting.com" 147 | }, 148 | { 149 | "prefix": 15, 150 | "network": "synesthesia", 151 | "displayName": "Synesthesia", 152 | "symbols": ["SYN"], 153 | "decimals": [12], 154 | "standardAccount": "*25519", 155 | "website": "https://synesthesia.network/" 156 | }, 157 | { 158 | "prefix": 16, 159 | "network": "kulupu", 160 | "displayName": "Kulupu", 161 | "symbols": ["KLP"], 162 | "decimals": [12], 163 | "standardAccount": "*25519", 164 | "website": "https://kulupu.network/" 165 | }, 166 | { 167 | "prefix": 17, 168 | "network": "dark", 169 | "displayName": "Dark Mainnet", 170 | "symbols": null, 171 | "decimals": null, 172 | "standardAccount": "*25519", 173 | "website": null 174 | }, 175 | { 176 | "prefix": 18, 177 | "network": "darwinia", 178 | "displayName": "Darwinia Network", 179 | "symbols": ["RING", "KTON"], 180 | "decimals": [9, 9], 181 | "standardAccount": "*25519", 182 | "website": "https://darwinia.network/" 183 | }, 184 | { 185 | "prefix": 19, 186 | "network": "geek", 187 | "displayName": "GeekCash", 188 | "symbols": ["GEEK"], 189 | "decimals": [12], 190 | "standardAccount": "*25519", 191 | "website": "https://geekcash.org" 192 | }, 193 | { 194 | "prefix": 20, 195 | "network": "stafi", 196 | "displayName": "Stafi", 197 | "symbols": ["FIS"], 198 | "decimals": [12], 199 | "standardAccount": "*25519", 200 | "website": "https://stafi.io" 201 | }, 202 | { 203 | "prefix": 21, 204 | "network": "dock-testnet", 205 | "displayName": "Dock Testnet", 206 | "symbols": ["DCK"], 207 | "decimals": [6], 208 | "standardAccount": "*25519", 209 | "website": "https://dock.io" 210 | }, 211 | { 212 | "prefix": 22, 213 | "network": "dock-mainnet", 214 | "displayName": "Dock Mainnet", 215 | "symbols": ["DCK"], 216 | "decimals": [6], 217 | "standardAccount": "*25519", 218 | "website": "https://dock.io" 219 | }, 220 | { 221 | "prefix": 23, 222 | "network": "shift", 223 | "displayName": "ShiftNrg", 224 | "symbols": null, 225 | "decimals": null, 226 | "standardAccount": "*25519", 227 | "website": null 228 | }, 229 | { 230 | "prefix": 24, 231 | "network": "zero", 232 | "displayName": "ZERO", 233 | "symbols": ["PLAY"], 234 | "decimals": [18], 235 | "standardAccount": "*25519", 236 | "website": "https://zero.io" 237 | }, 238 | { 239 | "prefix": 25, 240 | "network": "zero-alphaville", 241 | "displayName": "ZERO Alphaville", 242 | "symbols": ["PLAY"], 243 | "decimals": [18], 244 | "standardAccount": "*25519", 245 | "website": "https://zero.io" 246 | }, 247 | { 248 | "prefix": 28, 249 | "network": "subsocial", 250 | "displayName": "Subsocial", 251 | "symbols": null, 252 | "decimals": null, 253 | "standardAccount": "*25519", 254 | "website": null 255 | }, 256 | { 257 | "prefix": 30, 258 | "network": "phala", 259 | "displayName": "Phala Network", 260 | "symbols": null, 261 | "decimals": null, 262 | "standardAccount": "*25519", 263 | "website": null 264 | }, 265 | { 266 | "prefix": 32, 267 | "network": "robonomics", 268 | "displayName": "Robonomics Network", 269 | "symbols": null, 270 | "decimals": null, 271 | "standardAccount": "*25519", 272 | "website": null 273 | }, 274 | { 275 | "prefix": 33, 276 | "network": "datahighway", 277 | "displayName": "DataHighway", 278 | "symbols": null, 279 | "decimals": null, 280 | "standardAccount": "*25519", 281 | "website": null 282 | }, 283 | { 284 | "prefix": 36, 285 | "network": "centrifuge", 286 | "displayName": "Centrifuge Chain", 287 | "symbols": ["RAD"], 288 | "decimals": [18], 289 | "standardAccount": "*25519", 290 | "website": "https://centrifuge.io/" 291 | }, 292 | { 293 | "prefix": 37, 294 | "network": "nodle", 295 | "displayName": "Nodle Chain", 296 | "symbols": ["NODL"], 297 | "decimals": [18], 298 | "standardAccount": "*25519", 299 | "website": "https://nodle.io/" 300 | }, 301 | { 302 | "prefix": 39, 303 | "network": "mathchain", 304 | "displayName": "MathChain mainnet", 305 | "symbols": ["MATH"], 306 | "decimals": [18], 307 | "standardAccount": "*25519", 308 | "website": "https://mathwallet.org" 309 | }, 310 | { 311 | "prefix": 40, 312 | "network": "mathchain-testnet", 313 | "displayName": "MathChain testnet", 314 | "symbols": ["MATH"], 315 | "decimals": [18], 316 | "standardAccount": "*25519", 317 | "website": "https://mathwallet.org" 318 | }, 319 | { 320 | "prefix": 42, 321 | "network": "substrate", 322 | "displayName": "Substrate", 323 | "symbols": null, 324 | "decimals": null, 325 | "standardAccount": "*25519", 326 | "website": "https://substrate.dev/" 327 | }, 328 | { 329 | "prefix": 43, 330 | "network": "reserved43", 331 | "displayName": "This prefix is reserved.", 332 | "symbols": null, 333 | "decimals": null, 334 | "standardAccount": null, 335 | "website": null 336 | }, 337 | { 338 | "prefix": 44, 339 | "network": "chainx", 340 | "displayName": "ChainX", 341 | "symbols": ["PCX"], 342 | "decimals": [8], 343 | "standardAccount": "*25519", 344 | "website": "https://chainx.org/" 345 | }, 346 | { 347 | "prefix": 46, 348 | "network": "reserved46", 349 | "displayName": "This prefix is reserved.", 350 | "symbols": null, 351 | "decimals": null, 352 | "standardAccount": null, 353 | "website": null 354 | }, 355 | { 356 | "prefix": 47, 357 | "network": "reserved47", 358 | "displayName": "This prefix is reserved.", 359 | "symbols": null, 360 | "decimals": null, 361 | "standardAccount": null, 362 | "website": null 363 | }, 364 | { 365 | "prefix": 48, 366 | "network": "reserved48", 367 | "displayName": "All prefixes 48 and higher are reserved and cannot be allocated.", 368 | "symbols": null, 369 | "decimals": null, 370 | "standardAccount": null, 371 | "website": null 372 | } 373 | ] 374 | } -------------------------------------------------------------------------------- /client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "encoding/json" 7 | "errors" 8 | "fmt" 9 | "github.com/JFJun/go-substrate-crypto/ss58" 10 | "github.com/JFJun/stafi-substrate-go/base" 11 | "github.com/JFJun/stafi-substrate-go/expand" 12 | "github.com/JFJun/stafi-substrate-go/models" 13 | "github.com/JFJun/stafi-substrate-go/utils" 14 | gsrc "github.com/stafiprotocol/go-substrate-rpc-client" 15 | gsClient "github.com/stafiprotocol/go-substrate-rpc-client/client" 16 | "github.com/stafiprotocol/go-substrate-rpc-client/rpc" 17 | "github.com/stafiprotocol/go-substrate-rpc-client/scale" 18 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 19 | "golang.org/x/crypto/blake2b" 20 | "log" 21 | "strconv" 22 | "strings" 23 | ) 24 | 25 | type Client struct { 26 | C *gsrc.SubstrateAPI 27 | Meta *types.Metadata 28 | prefix []byte //币种的前缀 29 | ChainName string //链名字 30 | SpecVersion int 31 | TransactionVersion int 32 | genesisHash string 33 | BasicType *base.BasicTypes 34 | url string 35 | } 36 | 37 | func New(url string) (*Client, error) { 38 | c := new(Client) 39 | c.url = url 40 | var err error 41 | //注册链的基本信息 42 | c.BasicType, err = base.InitBasicTypesByHexData() 43 | if err != nil { 44 | return nil, fmt.Errorf("init base type error: %v", err) 45 | } 46 | // 初始化rpc客户端 47 | c.C, err = gsrc.NewSubstrateAPI(url) 48 | if err != nil { 49 | return nil, err 50 | } 51 | //检查当前链运行的版本 52 | err = c.checkRuntimeVersion() 53 | if err != nil { 54 | return nil, err 55 | } 56 | /* 57 | 设置prefix 58 | */ 59 | if len(c.prefix) == 0 { 60 | c.prefix, _ = c.BasicType.GetChainPrefix(c.ChainName) 61 | } 62 | //设置默认地址不需要0xff 63 | expand.SetSerDeOptions(true) 64 | return c, nil 65 | } 66 | 67 | func (c *Client) reConnectWs() (*gsrc.SubstrateAPI, error) { 68 | cl, err := gsClient.Connect(c.url) 69 | if err != nil { 70 | return nil, err 71 | } 72 | newRPC, err := rpc.NewRPC(cl) 73 | if err != nil { 74 | return nil, err 75 | } 76 | return &gsrc.SubstrateAPI{ 77 | RPC: newRPC, 78 | Client: cl, 79 | }, nil 80 | } 81 | 82 | func (c *Client) checkRuntimeVersion() error { 83 | v, err := c.C.RPC.State.GetRuntimeVersionLatest() 84 | if err != nil { 85 | if !strings.Contains(err.Error(), "tls: use of closed connection") { 86 | return fmt.Errorf("init runtime version error,err=%v", err) 87 | } 88 | // 重连处理,这是因为第三方包的问题,所以只能这样处理了了 89 | cl, err := c.reConnectWs() 90 | if err != nil { 91 | return fmt.Errorf("reconnect error: %v", err) 92 | } 93 | c.C = cl 94 | v, err = c.C.RPC.State.GetRuntimeVersionLatest() 95 | if err != nil { 96 | return fmt.Errorf("init runtime version error,aleady reconnect,err: %v", err) 97 | } 98 | } 99 | c.TransactionVersion = int(v.TransactionVersion) 100 | c.ChainName = v.SpecName 101 | specVersion := int(v.SpecVersion) 102 | //检查metadata数据是否有升级 103 | if specVersion != c.SpecVersion { 104 | c.Meta, err = c.C.RPC.State.GetMetadataLatest() 105 | if err != nil { 106 | return fmt.Errorf("init metadata error: %v", err) 107 | } 108 | c.SpecVersion = specVersion 109 | } 110 | return nil 111 | } 112 | 113 | /* 114 | 获取创世区块hash 115 | */ 116 | func (c *Client) GetGenesisHash() string { 117 | if c.genesisHash != "" { 118 | return c.genesisHash 119 | } 120 | hash, err := c.C.RPC.Chain.GetBlockHash(0) 121 | if err != nil { 122 | return "" 123 | } 124 | c.genesisHash = hash.Hex() 125 | return hash.Hex() 126 | } 127 | 128 | /* 129 | 自定义设置prefix,如果启动时加载的prefix是错误的,则需要手动配置prefix 130 | */ 131 | func (c *Client) SetPrefix(prefix []byte) { 132 | c.prefix = prefix 133 | } 134 | 135 | /* 136 | 根据height解析block,返回block是否包含交易 137 | */ 138 | func (c *Client) GetBlockByNumber(height int64) (*models.BlockResponse, error) { 139 | hash, err := c.C.RPC.Chain.GetBlockHash(uint64(height)) 140 | if err != nil { 141 | return nil, fmt.Errorf("get block hash error:%v,height:%d", err, height) 142 | } 143 | blockHash := hash.Hex() 144 | 145 | return c.GetBlockByHash(blockHash) 146 | } 147 | 148 | /* 149 | 根据blockHash解析block,返回block是否包含交易 150 | */ 151 | func (c *Client) GetBlockByHash(blockHash string) (*models.BlockResponse, error) { 152 | var ( 153 | block *models.SignedBlock 154 | err error 155 | ) 156 | err = c.checkRuntimeVersion() 157 | if err != nil { 158 | return nil, err 159 | } 160 | err = c.C.Client.Call(&block, "chain_getBlock", blockHash) 161 | if err != nil { 162 | return nil, fmt.Errorf("get block error: %v", err) 163 | } 164 | blockResp := new(models.BlockResponse) 165 | number, _ := strconv.ParseInt(utils.RemoveHex0x(block.Block.Header.Number), 16, 64) 166 | blockResp.Height = number 167 | blockResp.ParentHash = block.Block.Header.ParentHash 168 | blockResp.BlockHash = blockHash 169 | if len(block.Block.Extrinsics) > 0 { 170 | err = c.parseExtrinsicByDecode(block.Block.Extrinsics, blockResp) 171 | if err != nil { 172 | return nil, err 173 | } 174 | d, _ := json.Marshal(blockResp) 175 | fmt.Println(string(d)) 176 | err = c.parseExtrinsicByStorage(blockHash, blockResp) 177 | if err != nil { 178 | return nil, err 179 | } 180 | } 181 | return blockResp, nil 182 | } 183 | 184 | type parseBlockExtrinsicParams struct { 185 | from, to, sig, era, txid, fee string 186 | nonce int64 187 | extrinsicIdx, length int 188 | } 189 | 190 | /* 191 | 解析外部交易extrinsic 192 | */ 193 | func (c *Client) parseExtrinsicByDecode(extrinsics []string, blockResp *models.BlockResponse) error { 194 | var ( 195 | params []parseBlockExtrinsicParams 196 | timestamp int64 197 | //idx int 198 | ) 199 | defer func() { 200 | if err := recover(); err != nil { 201 | blockResp.Timestamp = timestamp 202 | blockResp.Extrinsic = []*models.ExtrinsicResponse{} 203 | log.Printf("parse %d block extrinsic error,Err=[%v]", blockResp.Height, err) 204 | } 205 | }() 206 | 207 | for i, extrinsic := range extrinsics { 208 | extrinsic = utils.Remove0X(extrinsic) 209 | data, err := hex.DecodeString(extrinsic) 210 | if err != nil { 211 | return fmt.Errorf("hex.decode extrinsic error: %v", err) 212 | } 213 | decoder := scale.NewDecoder(bytes.NewReader(data)) 214 | ed, err := expand.NewExtrinsicDecoder(c.Meta) 215 | if err != nil { 216 | return fmt.Errorf("new extrinsic decode error: %v", err) 217 | } 218 | err = ed.ProcessExtrinsicDecoder(*decoder) 219 | if err != nil { 220 | return fmt.Errorf("decode extrinsic error: %v", err) 221 | } 222 | var resp models.ExtrinsicDecodeResponse 223 | d, _ := json.Marshal(ed.Value) 224 | if len(d) == 0 { 225 | return errors.New("unknown extrinsic decode response") 226 | } 227 | err = json.Unmarshal(d, &resp) 228 | if err != nil { 229 | return fmt.Errorf("json unmarshal extrinsic decode error: %v", err) 230 | } 231 | switch resp.CallModule { 232 | case "Timestamp": 233 | for _, param := range resp.Params { 234 | if param.Name == "now" { 235 | timestamp = int64(param.Value.(float64)) 236 | } 237 | } 238 | case "Balances": 239 | if resp.CallModuleFunction == "transfer" || resp.CallModuleFunction == "transfer_keep_alive" { 240 | blockData := parseBlockExtrinsicParams{} 241 | blockData.from, _ = ss58.EncodeByPubHex(resp.AccountId, c.prefix) 242 | blockData.era = resp.Era 243 | blockData.sig = resp.Signature 244 | blockData.nonce = resp.Nonce 245 | blockData.extrinsicIdx = i 246 | blockData.fee, err = c.GetPartialFee(extrinsic, blockResp.ParentHash) 247 | 248 | blockData.txid = c.createTxHash(extrinsic) 249 | blockData.length = resp.Length 250 | for _, param := range resp.Params { 251 | if param.Name == "dest" { 252 | 253 | blockData.to, _ = ss58.EncodeByPubHex(param.Value.(string), c.prefix) 254 | } 255 | } 256 | params = append(params, blockData) 257 | } 258 | 259 | case "Utility": 260 | if resp.CallModuleFunction == "batch" { 261 | for _, param := range resp.Params { 262 | if param.Name == "calls" { 263 | 264 | switch param.Value.(type) { 265 | case []interface{}: 266 | d, _ := json.Marshal(param.Value) 267 | var values []models.UtilityParamsValue 268 | err = json.Unmarshal(d, &values) 269 | if err != nil { 270 | continue 271 | } 272 | 273 | for _, value := range values { 274 | if value.CallModule == "Balances" { 275 | 276 | if value.CallFunction == "transfer" || value.CallFunction == "transfer_keep_alive" { 277 | if len(value.CallArgs) > 0 { 278 | for _, arg := range value.CallArgs { 279 | if arg.Name == "dest" { 280 | blockData := parseBlockExtrinsicParams{} 281 | blockData.from, _ = ss58.EncodeByPubHex(resp.AccountId, c.prefix) 282 | blockData.era = resp.Era 283 | blockData.sig = resp.Signature 284 | blockData.nonce = resp.Nonce 285 | blockData.extrinsicIdx = i 286 | blockData.fee, _ = c.GetPartialFee(extrinsic, blockResp.ParentHash) 287 | blockData.txid = c.createTxHash(extrinsic) 288 | blockData.to, _ = ss58.EncodeByPubHex(arg.ValueRaw, c.prefix) 289 | params = append(params, blockData) 290 | } 291 | } 292 | } 293 | } 294 | } 295 | } 296 | default: 297 | continue 298 | } 299 | } 300 | } 301 | } 302 | default: 303 | //todo add another call_module 币种不同可能使用的call_module不一样 304 | continue 305 | } 306 | } 307 | blockResp.Timestamp = timestamp 308 | //解析params 309 | if len(params) == 0 { 310 | blockResp.Extrinsic = []*models.ExtrinsicResponse{} 311 | return nil 312 | } 313 | 314 | blockResp.Extrinsic = make([]*models.ExtrinsicResponse, len(params)) 315 | for idx, param := range params { 316 | e := new(models.ExtrinsicResponse) 317 | e.Signature = param.sig 318 | e.FromAddress = param.from 319 | e.ToAddress = param.to 320 | e.Nonce = param.nonce 321 | e.Era = param.era 322 | e.Fee = param.fee 323 | e.ExtrinsicIndex = param.extrinsicIdx 324 | e.Txid = param.txid 325 | e.ExtrinsicLength = param.length 326 | blockResp.Extrinsic[idx] = e 327 | 328 | } 329 | //utils.CheckStructData(blockResp) 330 | return nil 331 | } 332 | 333 | /* 334 | 解析当前区块的System.event 335 | */ 336 | func (c *Client) parseExtrinsicByStorage(blockHash string, blockResp *models.BlockResponse) error { 337 | var ( 338 | storage types.StorageKey 339 | err error 340 | ) 341 | defer func() { 342 | if err1 := recover(); err1 != nil { 343 | err = fmt.Errorf("panic decode event: %v", err1) 344 | } 345 | }() 346 | if len(blockResp.Extrinsic) <= 0 { 347 | //不包含交易就不处理了 348 | return nil 349 | } 350 | // 1. 先创建System.event的storageKey 351 | storage, err = types.CreateStorageKey(c.Meta, "System", "Events", nil, nil) 352 | if err != nil { 353 | return fmt.Errorf("create storage key error: %v", err) 354 | } 355 | key := storage.Hex() 356 | var result interface{} 357 | /* 358 | 根据storageKey以及blockHash获取当前区块的event信息 359 | */ 360 | err = c.C.Client.Call(&result, "state_getStorageAt", key, blockHash) 361 | if err != nil { 362 | return fmt.Errorf("get storage data error: %v", err) 363 | } 364 | //解析event信息 365 | ier, err := expand.DecodeEventRecords(c.Meta, result.(string), c.ChainName) 366 | if err != nil { 367 | return fmt.Errorf("decode event data error: %v", err) 368 | } 369 | //d,_:=json.Marshal(ier) 370 | //fmt.Println(string(d)) 371 | var res []models.EventResult 372 | failedMap := make(map[int]bool) 373 | if len(ier.GetBalancesTransfer()) > 0 { 374 | //有失败的交易 375 | for _, failed := range ier.GetSystemExtrinsicFailed() { 376 | if failed.Phase.IsApplyExtrinsic { 377 | extrinsicIdx := failed.Phase.AsApplyExtrinsic 378 | //记录到失败的map中 379 | failedMap[int(extrinsicIdx)] = true 380 | } 381 | } 382 | 383 | for _, ebt := range ier.GetBalancesTransfer() { 384 | 385 | if !ebt.Phase.IsApplyExtrinsic { 386 | continue 387 | } 388 | extrinsicIdx := int(ebt.Phase.AsApplyExtrinsic) 389 | var r models.EventResult 390 | r.ExtrinsicIdx = extrinsicIdx 391 | fromHex := hex.EncodeToString(ebt.From[:]) 392 | r.From, err = ss58.EncodeByPubHex(fromHex, c.prefix) 393 | if err != nil { 394 | r.From = "" 395 | continue 396 | } 397 | toHex := hex.EncodeToString(ebt.To[:]) 398 | 399 | r.To, err = ss58.EncodeByPubHex(toHex, c.prefix) 400 | if err != nil { 401 | r.To = "" 402 | continue 403 | } 404 | r.Amount = ebt.Value.String() 405 | //r.Weight = c.getWeight(&events, r.ExtrinsicIdx) 406 | res = append(res, r) 407 | } 408 | } 409 | for _, e := range blockResp.Extrinsic { 410 | e.Status = "fail" 411 | e.Type = "transfer" 412 | if len(res) > 0 { 413 | for _, r := range res { 414 | if e.ExtrinsicIndex == r.ExtrinsicIdx { 415 | if e.ToAddress == r.To { 416 | if failedMap[e.ExtrinsicIndex] { 417 | e.Status = "fail" 418 | } else { 419 | e.Status = "success" 420 | } 421 | e.Type = "transfer" 422 | e.Amount = r.Amount 423 | e.ToAddress = r.To 424 | //计算手续费 425 | //e.Fee = c.calcFee(&events, e.ExtrinsicIndex) 426 | } 427 | } 428 | } 429 | } 430 | } 431 | 432 | return nil 433 | } 434 | 435 | //func (c *Client) calcFee(events *types.EventRecords, extrinsicIdx int) string { 436 | // var ( 437 | // fee = decimal.Zero 438 | // ) 439 | // 440 | // for _, bd := range events.Balances_Deposit { 441 | // if bd.Phase.IsApplyExtrinsic && int(bd.Phase.AsApplyExtrinsic) == extrinsicIdx { 442 | // fee = fee.Add(decimal.NewFromInt(bd.Balance.Int64())) 443 | // } 444 | // } 445 | // for _, td := range events.Treasury_Deposit { 446 | // if td.Phase.IsApplyExtrinsic && int(td.Phase.AsApplyExtrinsic) == extrinsicIdx { 447 | // fee = fee.Add(decimal.NewFromInt(td.Deposited.Int64())) 448 | // } 449 | // } 450 | // return fee.String() 451 | //} 452 | 453 | /* 454 | 根据外部交易extrinsic创建txid 455 | */ 456 | func (c *Client) createTxHash(extrinsic string) string { 457 | data, _ := hex.DecodeString(utils.RemoveHex0x(extrinsic)) 458 | d := blake2b.Sum256(data) 459 | return "0x" + hex.EncodeToString(d[:]) 460 | } 461 | 462 | /* 463 | 根据地址获取地址的账户信息,包括nonce以及余额等 464 | */ 465 | func (c *Client) GetAccountInfo(address string) (*types.AccountInfo, error) { 466 | var ( 467 | storage types.StorageKey 468 | err error 469 | pub []byte 470 | ) 471 | defer func() { 472 | if err1 := recover(); err1 != nil { 473 | err = fmt.Errorf("panic decode event: %v", err1) 474 | } 475 | }() 476 | err = c.checkRuntimeVersion() 477 | if err != nil { 478 | return nil, err 479 | } 480 | pub, err = ss58.DecodeToPub(address) 481 | if err != nil { 482 | return nil, fmt.Errorf("ss58 decode address error: %v", err) 483 | } 484 | storage, err = types.CreateStorageKey(c.Meta, "System", "Account", pub, nil) 485 | if err != nil { 486 | return nil, fmt.Errorf("create System.Account storage error: %v", err) 487 | } 488 | var accountInfo types.AccountInfo 489 | var ok bool 490 | ok, err = c.C.RPC.State.GetStorageLatest(storage, &accountInfo) 491 | if err != nil || !ok { 492 | return nil, fmt.Errorf("get account info error: %v", err) 493 | } 494 | return &accountInfo, nil 495 | } 496 | 497 | /* 498 | 获取外部交易extrinsic的手续费 499 | */ 500 | func (c *Client) GetPartialFee(extrinsic, parentHash string) (string, error) { 501 | if !strings.HasPrefix(extrinsic, "0x") { 502 | extrinsic = "0x" + extrinsic 503 | } 504 | var result map[string]interface{} 505 | err := c.C.Client.Call(&result, "payment_queryInfo", extrinsic, parentHash) 506 | if err != nil { 507 | return "", fmt.Errorf("get payment info error: %v", err) 508 | } 509 | 510 | if result["partialFee"] == nil { 511 | return "", errors.New("result partialFee is nil ptr") 512 | } 513 | fee, ok := result["partialFee"].(string) 514 | if !ok { 515 | return "", fmt.Errorf("partialFee is not string type: %v", result["partialFee"]) 516 | } 517 | return fee, nil 518 | } 519 | -------------------------------------------------------------------------------- /cmd/cretetypes/file/cring.json: -------------------------------------------------------------------------------- 1 | { 2 | "coin_name": "cring", 3 | "chain_name": "Crab", 4 | "version": "v12", 5 | "spec_version": 28, 6 | "types": [ 7 | { 8 | "name": "Kton_Endowed", 9 | "sub_types": [ 10 | "AccountId", 11 | "Balance" 12 | ] 13 | }, 14 | { 15 | "name": "Kton_DustLost", 16 | "sub_types": [ 17 | "AccountId", 18 | "Balance" 19 | ] 20 | }, 21 | { 22 | "name": "Kton_Transfer", 23 | "sub_types": [ 24 | "AccountId", 25 | "AccountId", 26 | "Balance" 27 | ] 28 | }, 29 | { 30 | "name": "Kton_BalanceSet", 31 | "sub_types": [ 32 | "AccountId", 33 | "Balance", 34 | "Balance" 35 | ] 36 | }, 37 | { 38 | "name": "Kton_Deposit", 39 | "sub_types": [ 40 | "AccountId", 41 | "Balance" 42 | ] 43 | }, 44 | { 45 | "name": "Kton_Reserved", 46 | "sub_types": [ 47 | "AccountId", 48 | "Balance" 49 | ] 50 | }, 51 | { 52 | "name": "Kton_Unreserved", 53 | "sub_types": [ 54 | "AccountId", 55 | "Balance" 56 | ] 57 | }, 58 | { 59 | "name": "Kton_ReserveRepatriated", 60 | "sub_types": [ 61 | "AccountId", 62 | "AccountId", 63 | "Balance", 64 | "Status" 65 | ] 66 | }, 67 | { 68 | "name": "Staking_BondRing", 69 | "sub_types": [ 70 | "RingBalance", 71 | "TsInMs", 72 | "TsInMs" 73 | ] 74 | }, 75 | { 76 | "name": "Staking_BondKton", 77 | "sub_types": [ 78 | "KtonBalance" 79 | ] 80 | }, 81 | { 82 | "name": "Staking_UnbondRing", 83 | "sub_types": [ 84 | "RingBalance", 85 | "BlockNumber" 86 | ] 87 | }, 88 | { 89 | "name": "Staking_UnbondKton", 90 | "sub_types": [ 91 | "KtonBalance", 92 | "BlockNumber" 93 | ] 94 | }, 95 | { 96 | "name": "Staking_ClaimDepositsWithPunish", 97 | "sub_types": [ 98 | "AccountId", 99 | "KtonBalance" 100 | ] 101 | }, 102 | { 103 | "name": "ElectionsPhragmen_NewTerm", 104 | "sub_types": [ 105 | "Vec<(AccountId, Balance)>" 106 | ] 107 | }, 108 | { 109 | "name": "ElectionsPhragmen_EmptyTerm", 110 | "sub_types": null 111 | }, 112 | { 113 | "name": "ElectionsPhragmen_ElectionError", 114 | "sub_types": null 115 | }, 116 | { 117 | "name": "ElectionsPhragmen_MemberKicked", 118 | "sub_types": [ 119 | "AccountId" 120 | ] 121 | }, 122 | { 123 | "name": "ElectionsPhragmen_MemberRenounced", 124 | "sub_types": [ 125 | "AccountId" 126 | ] 127 | }, 128 | { 129 | "name": "ElectionsPhragmen_VoterReported", 130 | "sub_types": [ 131 | "AccountId", 132 | "AccountId", 133 | "bool" 134 | ] 135 | }, 136 | { 137 | "name": "Claims_Claimed", 138 | "sub_types": [ 139 | "AccountId", 140 | "AddressT", 141 | "RingBalance" 142 | ] 143 | }, 144 | { 145 | "name": "EthereumBacking_RedeemRing", 146 | "sub_types": [ 147 | "AccountId", 148 | "Balance", 149 | "EthereumTransactionIndex" 150 | ] 151 | }, 152 | { 153 | "name": "EthereumBacking_RedeemKton", 154 | "sub_types": [ 155 | "AccountId", 156 | "Balance", 157 | "EthereumTransactionIndex" 158 | ] 159 | }, 160 | { 161 | "name": "EthereumBacking_RedeemDeposit", 162 | "sub_types": [ 163 | "AccountId", 164 | "DepositId", 165 | "RingBalance", 166 | "EthereumTransactionIndex" 167 | ] 168 | }, 169 | { 170 | "name": "EthereumRelay_Affirmed", 171 | "sub_types": [ 172 | "AccountId", 173 | "RelayAffirmationId" 174 | ] 175 | }, 176 | { 177 | "name": "EthereumRelay_DisputedAndAffirmed", 178 | "sub_types": [ 179 | "AccountId", 180 | "RelayAffirmationId" 181 | ] 182 | }, 183 | { 184 | "name": "EthereumRelay_Extended", 185 | "sub_types": [ 186 | "AccountId", 187 | "RelayAffirmationId" 188 | ] 189 | }, 190 | { 191 | "name": "EthereumRelay_NewRound", 192 | "sub_types": [ 193 | "EthereumBlockNumber", 194 | "Vec" 195 | ] 196 | }, 197 | { 198 | "name": "EthereumRelay_GameOver", 199 | "sub_types": [ 200 | "EthereumBlockNumber" 201 | ] 202 | }, 203 | { 204 | "name": "EthereumRelay_RemoveConfirmedParcel", 205 | "sub_types": [ 206 | "EthereumBlockNumber" 207 | ] 208 | }, 209 | { 210 | "name": "EthereumRelay_VerifyReceipt", 211 | "sub_types": [ 212 | "AccountId", 213 | "EthereumReceipt", 214 | "EthereumHeader" 215 | ] 216 | }, 217 | { 218 | "name": "EthereumRelay_Pended", 219 | "sub_types": [ 220 | "EthereumBlockNumber" 221 | ] 222 | }, 223 | { 224 | "name": "EthereumRelay_GuardVoted", 225 | "sub_types": [ 226 | "EthereumBlockNumber", 227 | "bool" 228 | ] 229 | }, 230 | { 231 | "name": "EthereumRelay_PendingRelayHeaderParcelConfirmed", 232 | "sub_types": [ 233 | "EthereumBlockNumber", 234 | "Vec" 235 | ] 236 | }, 237 | { 238 | "name": "EthereumRelay_PendingRelayHeaderParcelRejected", 239 | "sub_types": [ 240 | "EthereumBlockNumber" 241 | ] 242 | }, 243 | { 244 | "name": "Treasury_DepositRing", 245 | "sub_types": [ 246 | "RingBalance" 247 | ] 248 | }, 249 | { 250 | "name": "Treasury_DepositKton", 251 | "sub_types": [ 252 | "KtonBalance" 253 | ] 254 | }, 255 | { 256 | "name": "Proxy_Announced", 257 | "sub_types": [ 258 | "AccountId", 259 | "AccountId", 260 | "Hash" 261 | ] 262 | }, 263 | { 264 | "name": "CrabIssuing_DummyEvent", 265 | "sub_types": [ 266 | "AccountId", 267 | "RingBalance", 268 | "MappedRing" 269 | ] 270 | }, 271 | { 272 | "name": "Democracy_Blacklisted", 273 | "sub_types": [ 274 | "Hash" 275 | ] 276 | } 277 | ] 278 | } -------------------------------------------------------------------------------- /cmd/cretetypes/file/crust.json: -------------------------------------------------------------------------------- 1 | { 2 | "coin_name": "cru", 3 | "chain_name": "crust", 4 | "version": "v12", 5 | "spec_version": 7, 6 | "types": [ 7 | { 8 | "name": "Staking_EraReward", 9 | "sub_types": [ 10 | "EraIndex", 11 | "Balance", 12 | "Balance" 13 | ] 14 | }, 15 | { 16 | "name": "Staking_ValidateSuccess", 17 | "sub_types": [ 18 | "AccountId", 19 | "ValidatorPrefs" 20 | ] 21 | }, 22 | { 23 | "name": "Staking_GuaranteeSuccess", 24 | "sub_types": [ 25 | "AccountId", 26 | "AccountId", 27 | "Balance" 28 | ] 29 | }, 30 | { 31 | "name": "Staking_ChillSuccess", 32 | "sub_types": [ 33 | "AccountId", 34 | "AccountId" 35 | ] 36 | }, 37 | { 38 | "name": "Staking_UpdateIdentitiesSuccess", 39 | "sub_types": [ 40 | "EraIndex" 41 | ] 42 | }, 43 | { 44 | "name": "Swork_RegisterSuccess", 45 | "sub_types": [ 46 | "AccountId", 47 | "SworkerPubKey" 48 | ] 49 | }, 50 | { 51 | "name": "Swork_WorksReportSuccess", 52 | "sub_types": [ 53 | "AccountId", 54 | "SworkerPubKey" 55 | ] 56 | }, 57 | { 58 | "name": "Swork_ABUpgradeSuccess", 59 | "sub_types": [ 60 | "AccountId", 61 | "SworkerPubKey", 62 | "SworkerPubKey" 63 | ] 64 | }, 65 | { 66 | "name": "Swork_ChillSuccess", 67 | "sub_types": [ 68 | "AccountId", 69 | "SworkerPubKey" 70 | ] 71 | }, 72 | { 73 | "name": "Swork_EnclaveUpgradeSuccess", 74 | "sub_types": [ 75 | "SworkerCode", 76 | "BlockNumber" 77 | ] 78 | }, 79 | { 80 | "name": "Market_StorageOrderSuccess", 81 | "sub_types": [ 82 | "AccountId", 83 | "SorderInfo", 84 | "SorderStatus" 85 | ] 86 | }, 87 | { 88 | "name": "Market_RegisterSuccess", 89 | "sub_types": [ 90 | "AccountId" 91 | ] 92 | }, 93 | { 94 | "name": "Market_PledgeSuccess", 95 | "sub_types": [ 96 | "AccountId" 97 | ] 98 | }, 99 | { 100 | "name": "Market_SetAliasSuccess", 101 | "sub_types": [ 102 | "AccountId", 103 | "FileAlias", 104 | "FileAlias" 105 | ] 106 | }, 107 | { 108 | "name": "Market_PaysOrderSuccess", 109 | "sub_types": [ 110 | "AccountId" 111 | ] 112 | }, 113 | { 114 | "name": "Candy_CandyIssued", 115 | "sub_types": [ 116 | "AccountId", 117 | "Balance" 118 | ] 119 | }, 120 | { 121 | "name": "Candy_CandyTransferred", 122 | "sub_types": [ 123 | "AccountId", 124 | "AccountId", 125 | "Balance" 126 | ] 127 | }, 128 | { 129 | "name": "Candy_CandyBurned", 130 | "sub_types": [ 131 | "AccountId", 132 | "Balance" 133 | ] 134 | }, 135 | { 136 | "name": "Candy_BondEthSuccess", 137 | "sub_types": [ 138 | "AccountId", 139 | "ETHAddress" 140 | ] 141 | } 142 | ] 143 | } -------------------------------------------------------------------------------- /cmd/cretetypes/file/kusama.json: -------------------------------------------------------------------------------- 1 | { 2 | "coin_name": "ksm", 3 | "chain_name": "kusama", 4 | "version": "v12", 5 | "spec_version": 2026, 6 | "types": [ 7 | { 8 | "name": "Democracy_Blacklisted", 9 | "sub_types": [ 10 | "Hash" 11 | ] 12 | }, 13 | { 14 | "name": "ElectionsPhragmen_NewTerm", 15 | "sub_types": [ 16 | "Vec<(AccountId, Balance)>" 17 | ] 18 | }, 19 | { 20 | "name": "ElectionsPhragmen_EmptyTerm", 21 | "sub_types": null 22 | }, 23 | { 24 | "name": "ElectionsPhragmen_ElectionError", 25 | "sub_types": null 26 | }, 27 | { 28 | "name": "ElectionsPhragmen_MemberKicked", 29 | "sub_types": [ 30 | "AccountId" 31 | ] 32 | }, 33 | { 34 | "name": "ElectionsPhragmen_MemberRenounced", 35 | "sub_types": [ 36 | "AccountId" 37 | ] 38 | }, 39 | { 40 | "name": "ElectionsPhragmen_VoterReported", 41 | "sub_types": [ 42 | "AccountId", 43 | "AccountId", 44 | "bool" 45 | ] 46 | }, 47 | { 48 | "name": "Claims_Claimed", 49 | "sub_types": [ 50 | "AccountId", 51 | "EthereumAddress", 52 | "Balance" 53 | ] 54 | }, 55 | { 56 | "name": "Proxy_Announced", 57 | "sub_types": [ 58 | "AccountId", 59 | "AccountId", 60 | "Hash" 61 | ] 62 | } 63 | ] 64 | } -------------------------------------------------------------------------------- /cmd/cretetypes/file/polkadot.json: -------------------------------------------------------------------------------- 1 | { 2 | "coin_name": "dot", 3 | "chain_name": "polkadot", 4 | "version": "v12", 5 | "spec_version": 26, 6 | "types": [ 7 | { 8 | "name": "Democracy_Blacklisted", 9 | "sub_types": [ 10 | "Hash" 11 | ] 12 | }, 13 | { 14 | "name": "ElectionsPhragmen_NewTerm", 15 | "sub_types": [ 16 | "Vec<(AccountId, Balance)>" 17 | ] 18 | }, 19 | { 20 | "name": "ElectionsPhragmen_EmptyTerm", 21 | "sub_types": null 22 | }, 23 | { 24 | "name": "ElectionsPhragmen_ElectionError", 25 | "sub_types": null 26 | }, 27 | { 28 | "name": "ElectionsPhragmen_MemberKicked", 29 | "sub_types": [ 30 | "AccountId" 31 | ] 32 | }, 33 | { 34 | "name": "ElectionsPhragmen_MemberRenounced", 35 | "sub_types": [ 36 | "AccountId" 37 | ] 38 | }, 39 | { 40 | "name": "ElectionsPhragmen_VoterReported", 41 | "sub_types": [ 42 | "AccountId", 43 | "AccountId", 44 | "bool" 45 | ] 46 | }, 47 | { 48 | "name": "Claims_Claimed", 49 | "sub_types": [ 50 | "AccountId", 51 | "EthereumAddress", 52 | "Balance" 53 | ] 54 | }, 55 | { 56 | "name": "Proxy_Announced", 57 | "sub_types": [ 58 | "AccountId", 59 | "AccountId", 60 | "Hash" 61 | ] 62 | } 63 | ] 64 | } -------------------------------------------------------------------------------- /cmd/cretetypes/file/ring.json: -------------------------------------------------------------------------------- 1 | { 2 | "coin_name": "ring", 3 | "chain_name": "Darwinia", 4 | "version": "v12", 5 | "spec_version": 9, 6 | "types": [ 7 | { 8 | "name": "Kton_Endowed", 9 | "sub_types": [ 10 | "AccountId", 11 | "Balance" 12 | ] 13 | }, 14 | { 15 | "name": "Kton_DustLost", 16 | "sub_types": [ 17 | "AccountId", 18 | "Balance" 19 | ] 20 | }, 21 | { 22 | "name": "Kton_Transfer", 23 | "sub_types": [ 24 | "AccountId", 25 | "AccountId", 26 | "Balance" 27 | ] 28 | }, 29 | { 30 | "name": "Kton_BalanceSet", 31 | "sub_types": [ 32 | "AccountId", 33 | "Balance", 34 | "Balance" 35 | ] 36 | }, 37 | { 38 | "name": "Kton_Deposit", 39 | "sub_types": [ 40 | "AccountId", 41 | "Balance" 42 | ] 43 | }, 44 | { 45 | "name": "Kton_Reserved", 46 | "sub_types": [ 47 | "AccountId", 48 | "Balance" 49 | ] 50 | }, 51 | { 52 | "name": "Kton_Unreserved", 53 | "sub_types": [ 54 | "AccountId", 55 | "Balance" 56 | ] 57 | }, 58 | { 59 | "name": "Kton_ReserveRepatriated", 60 | "sub_types": [ 61 | "AccountId", 62 | "AccountId", 63 | "Balance", 64 | "Status" 65 | ] 66 | }, 67 | { 68 | "name": "Staking_BondRing", 69 | "sub_types": [ 70 | "RingBalance", 71 | "TsInMs", 72 | "TsInMs" 73 | ] 74 | }, 75 | { 76 | "name": "Staking_BondKton", 77 | "sub_types": [ 78 | "KtonBalance" 79 | ] 80 | }, 81 | { 82 | "name": "Staking_UnbondRing", 83 | "sub_types": [ 84 | "RingBalance", 85 | "BlockNumber" 86 | ] 87 | }, 88 | { 89 | "name": "Staking_UnbondKton", 90 | "sub_types": [ 91 | "KtonBalance", 92 | "BlockNumber" 93 | ] 94 | }, 95 | { 96 | "name": "Staking_ClaimDepositsWithPunish", 97 | "sub_types": [ 98 | "AccountId", 99 | "KtonBalance" 100 | ] 101 | }, 102 | { 103 | "name": "ElectionsPhragmen_NewTerm", 104 | "sub_types": [ 105 | "Vec<(AccountId, Balance)>" 106 | ] 107 | }, 108 | { 109 | "name": "ElectionsPhragmen_EmptyTerm", 110 | "sub_types": null 111 | }, 112 | { 113 | "name": "ElectionsPhragmen_ElectionError", 114 | "sub_types": null 115 | }, 116 | { 117 | "name": "ElectionsPhragmen_MemberKicked", 118 | "sub_types": [ 119 | "AccountId" 120 | ] 121 | }, 122 | { 123 | "name": "ElectionsPhragmen_MemberRenounced", 124 | "sub_types": [ 125 | "AccountId" 126 | ] 127 | }, 128 | { 129 | "name": "ElectionsPhragmen_VoterReported", 130 | "sub_types": [ 131 | "AccountId", 132 | "AccountId", 133 | "bool" 134 | ] 135 | }, 136 | { 137 | "name": "Treasury_DepositRing", 138 | "sub_types": [ 139 | "RingBalance" 140 | ] 141 | }, 142 | { 143 | "name": "Treasury_DepositKton", 144 | "sub_types": [ 145 | "KtonBalance" 146 | ] 147 | }, 148 | { 149 | "name": "Proxy_Announced", 150 | "sub_types": [ 151 | "AccountId", 152 | "AccountId", 153 | "Hash" 154 | ] 155 | }, 156 | { 157 | "name": "EthereumBacking_RedeemRing", 158 | "sub_types": [ 159 | "AccountId", 160 | "Balance", 161 | "EthereumTransactionIndex" 162 | ] 163 | }, 164 | { 165 | "name": "EthereumBacking_RedeemKton", 166 | "sub_types": [ 167 | "AccountId", 168 | "Balance", 169 | "EthereumTransactionIndex" 170 | ] 171 | }, 172 | { 173 | "name": "EthereumBacking_RedeemDeposit", 174 | "sub_types": [ 175 | "AccountId", 176 | "DepositId", 177 | "RingBalance", 178 | "EthereumTransactionIndex" 179 | ] 180 | }, 181 | { 182 | "name": "EthereumRelay_Affirmed", 183 | "sub_types": [ 184 | "AccountId", 185 | "RelayAffirmationId" 186 | ] 187 | }, 188 | { 189 | "name": "EthereumRelay_DisputedAndAffirmed", 190 | "sub_types": [ 191 | "AccountId", 192 | "RelayAffirmationId" 193 | ] 194 | }, 195 | { 196 | "name": "EthereumRelay_Extended", 197 | "sub_types": [ 198 | "AccountId", 199 | "RelayAffirmationId" 200 | ] 201 | }, 202 | { 203 | "name": "EthereumRelay_NewRound", 204 | "sub_types": [ 205 | "EthereumBlockNumber", 206 | "Vec" 207 | ] 208 | }, 209 | { 210 | "name": "EthereumRelay_GameOver", 211 | "sub_types": [ 212 | "EthereumBlockNumber" 213 | ] 214 | }, 215 | { 216 | "name": "EthereumRelay_RemoveConfirmedParcel", 217 | "sub_types": [ 218 | "EthereumBlockNumber" 219 | ] 220 | }, 221 | { 222 | "name": "EthereumRelay_VerifyReceipt", 223 | "sub_types": [ 224 | "AccountId", 225 | "EthereumReceipt", 226 | "EthereumHeader" 227 | ] 228 | }, 229 | { 230 | "name": "EthereumRelay_Pended", 231 | "sub_types": [ 232 | "EthereumBlockNumber" 233 | ] 234 | }, 235 | { 236 | "name": "EthereumRelay_GuardVoted", 237 | "sub_types": [ 238 | "EthereumBlockNumber", 239 | "bool" 240 | ] 241 | }, 242 | { 243 | "name": "EthereumRelay_PendingRelayHeaderParcelConfirmed", 244 | "sub_types": [ 245 | "EthereumBlockNumber", 246 | "Vec" 247 | ] 248 | }, 249 | { 250 | "name": "EthereumRelay_PendingRelayHeaderParcelRejected", 251 | "sub_types": [ 252 | "EthereumBlockNumber" 253 | ] 254 | } 255 | ] 256 | } -------------------------------------------------------------------------------- /cmd/cretetypes/file/rio.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JFJun/stafi-substrate-go/ba9c21cc0be0c5ee40e254c7fb89e0d03f57fd36/cmd/cretetypes/file/rio.json -------------------------------------------------------------------------------- /cmd/cretetypes/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/JFJun/stafi-substrate-go/client" 7 | ) 8 | 9 | func main() { 10 | url := "" 11 | err := createTypes("fis", url) 12 | if err != nil { 13 | panic(err) 14 | } 15 | } 16 | 17 | func createBaseTypesJson(url string) error { 18 | c, err := client.New(url) 19 | if err != nil { 20 | return err 21 | } 22 | tj := new(TypesJson) 23 | tj.CoinName = "base" 24 | tj.SpecVersion = c.SpecVersion 25 | tj.ChainName = c.ChainName 26 | 27 | for _, mod := range c.Meta.AsMetadataV11.Modules { 28 | tj.Version = "v11" 29 | if mod.HasEvents { 30 | for _, event := range mod.Events { 31 | typeName := fmt.Sprintf("%s_%s", mod.Name, event.Name) 32 | if IsExist(typeName) { 33 | var t EventType 34 | t.Name = typeName 35 | for _, arg := range event.Args { 36 | t.SubTypes = append(t.SubTypes, string(arg)) 37 | } 38 | tj.Types = append(tj.Types, t) 39 | } 40 | } 41 | } 42 | } 43 | 44 | for _, mod := range c.Meta.AsMetadataV12.Modules { 45 | tj.Version = "v12" 46 | if mod.HasEvents { 47 | for _, event := range mod.Events { 48 | typeName := fmt.Sprintf("%s_%s", mod.Name, event.Name) 49 | if IsExist(typeName) { 50 | var t EventType 51 | t.Name = typeName 52 | for _, arg := range event.Args { 53 | t.SubTypes = append(t.SubTypes, string(arg)) 54 | } 55 | tj.Types = append(tj.Types, t) 56 | } 57 | } 58 | } 59 | } 60 | d, err := json.Marshal(tj) 61 | if err != nil { 62 | return err 63 | } 64 | fmt.Println(string(d)) 65 | return nil 66 | } 67 | 68 | func createTypes(coinName, url string) error { 69 | c, err := client.New(url) 70 | if err != nil { 71 | return err 72 | } 73 | tj := new(TypesJson) 74 | tj.CoinName = coinName 75 | tj.SpecVersion = c.SpecVersion 76 | tj.ChainName = c.ChainName 77 | 78 | for _, mod := range c.Meta.AsMetadataV11.Modules { 79 | tj.Version = "v11" 80 | if mod.HasEvents { 81 | for _, event := range mod.Events { 82 | typeName := fmt.Sprintf("%s_%s", mod.Name, event.Name) 83 | if !IsExist(typeName) { 84 | var t EventType 85 | t.Name = typeName 86 | for _, arg := range event.Args { 87 | t.SubTypes = append(t.SubTypes, string(arg)) 88 | } 89 | tj.Types = append(tj.Types, t) 90 | } 91 | } 92 | } 93 | } 94 | 95 | for _, mod := range c.Meta.AsMetadataV12.Modules { 96 | tj.Version = "v12" 97 | if mod.HasEvents { 98 | for _, event := range mod.Events { 99 | typeName := fmt.Sprintf("%s_%s", mod.Name, event.Name) 100 | if !IsExist(typeName) { 101 | var t EventType 102 | t.Name = typeName 103 | for _, arg := range event.Args { 104 | t.SubTypes = append(t.SubTypes, string(arg)) 105 | } 106 | tj.Types = append(tj.Types, t) 107 | } 108 | } 109 | } 110 | } 111 | 112 | d, err := json.Marshal(tj) 113 | if err != nil { 114 | return err 115 | } 116 | fmt.Println(string(d)) 117 | return nil 118 | } 119 | 120 | var existTypes = []string{ 121 | "Balances_Endowed", 122 | "Balances_DustLost", 123 | "Balances_Transfer", 124 | "Balances_BalanceSet", 125 | "Balances_Deposit", 126 | "Balances_Reserved", 127 | "Balances_Unreserved", 128 | "Balances_ReservedRepatriated", 129 | "Grandpa_NewAuthorities", 130 | "Grandpa_Paused", 131 | "Grandpa_Resumed", 132 | "ImOnline_HeartbeatReceived", 133 | "ImOnline_AllGood", 134 | "ImOnline_SomeOffline", 135 | "Indices_IndexAssigned", 136 | "Indices_IndexFreed", 137 | "Indices_IndexFrozen", 138 | "Offences_Offence", 139 | "Session_NewSession", 140 | "Staking_EraPayout", 141 | "Staking_Reward", 142 | "Staking_Slash", 143 | "Staking_OldSlashingReportDiscarded", 144 | "Staking_StakingElection", 145 | "Staking_SolutionStored", 146 | "Staking_Bonded", 147 | "Staking_Unbonded", 148 | "Staking_Withdrawn", 149 | "System_ExtrinsicSuccess", 150 | "System_ExtrinsicFailed", 151 | "System_CodeUpdated", 152 | "System_NewAccount", 153 | "System_KilledAccount", 154 | "Assets_Issued", 155 | "Assets_Transferred", 156 | "Assets_Destroyed", 157 | "Democracy_Proposed", 158 | "Democracy_Tabled", 159 | "Democracy_ExternalTabled", 160 | "Democracy_Started", 161 | "Democracy_Passed", 162 | "Democracy_NotPassed", 163 | "Democracy_Cancelled", 164 | "Democracy_Executed", 165 | "Democracy_Delegated", 166 | "Democracy_Undelegated", 167 | "Democracy_Vetoed", 168 | "Democracy_PreimageNoted", 169 | "Democracy_PreimageUsed", 170 | "Democracy_PreimageInvalid", 171 | "Democracy_PreimageMissing", 172 | "Democracy_PreimageReaped", 173 | "Democracy_Unlocked", 174 | "Council_Proposed", 175 | "Council_Voted", 176 | "Council_Approved", 177 | "Council_Disapproved", 178 | "Council_Executed", 179 | "Council_MemberExecuted", 180 | "Council_Closed", 181 | "TechnicalCommittee_Proposed", 182 | "TechnicalCommittee_Voted", 183 | "TechnicalCommittee_Approved", 184 | "TechnicalCommittee_Disapproved", 185 | "TechnicalCommittee_Executed", 186 | "TechnicalCommittee_MemberExecuted", 187 | "TechnicalCommittee_Closed", 188 | "TechnicalMembership_MemberAdded", 189 | "TechnicalMembership_MemberRemoved", 190 | "TechnicalMembership_MembersSwapped", 191 | "TechnicalMembership_MembersReset", 192 | "TechnicalMembership_KeyChanged", 193 | "Elections_NewTerm", 194 | "Elections_EmptyTerm", 195 | "Elections_MemberKicked", 196 | "Elections_MemberRenounced", 197 | "Elections_VoterReported", 198 | "Identity_IdentitySet", 199 | "Identity_IdentityCleared", 200 | "Identity_IdentityKilled", 201 | "Identity_JudgementRequested", 202 | "Identity_JudgementUnrequested", 203 | "Identity_JudgementGiven", 204 | "Identity_RegistrarAdded", 205 | "Identity_SubIdentityAdded", 206 | "Identity_SubIdentityRemoved", 207 | "Identity_SubIdentityRevoked", 208 | "Society_Founded", 209 | "Society_Bid", 210 | "Society_Vouch", 211 | "Society_AutoUnbid", 212 | "Society_Unbid", 213 | "Society_Unvouch", 214 | "Society_Inducted", 215 | "Society_SuspendedMemberJudgement", 216 | "Society_CandidateSuspended", 217 | "Society_MemberSuspended", 218 | "Society_Challenged", 219 | "Society_Vote", 220 | "Society_DefenderVote", 221 | "Society_NewMaxMembers", 222 | "Society_Unfounded", 223 | "Society_Deposit", 224 | "Recovery_RecoveryCreated", 225 | "Recovery_RecoveryInitiated", 226 | "Recovery_RecoveryVouched", 227 | "Recovery_RecoveryClosed", 228 | "Recovery_AccountRecovered", 229 | "Recovery_RecoveryRemoved", 230 | "Vesting_VestingUpdated", 231 | "Vesting_VestingCompleted", 232 | "Scheduler_Scheduled", 233 | "Scheduler_Canceled", 234 | "Scheduler_Dispatched", 235 | "Proxy_ProxyExecuted", 236 | "Proxy_AnonymousCreated", 237 | "Sudo_Sudid", 238 | "Sudo_KeyChanged", 239 | "Sudo_SudoAsDone", 240 | "Treasury_Proposed", 241 | "Treasury_Spending", 242 | "Treasury_Awarded", 243 | "Treasury_Rejected", 244 | "Treasury_Burnt", 245 | "Treasury_Rollover", 246 | "Treasury_Deposit", 247 | "Treasury_NewTip", 248 | "Treasury_TipClosing", 249 | "Treasury_TipClosed", 250 | "Treasury_TipRetracted", 251 | "Contracts_Instantiated", 252 | "Contracts_Evicted", 253 | "Contracts_Restored", 254 | "Contracts_CodeStored", 255 | "Contracts_ScheduleUpdated", 256 | "Contracts_ContractExecution", 257 | "Utility_BatchInterrupted", 258 | "Utility_BatchCompleted", 259 | "Multisig_New", 260 | "Multisig_Approval", 261 | "Multisig_Executed", 262 | "Multisig_Cancelled", 263 | "Treasury_BountyProposed", 264 | "Treasury_BountyRejected", 265 | "Treasury_BountyBecameActive", 266 | "Treasury_BountyAwarded", 267 | "Treasury_BountyClaimed", 268 | "Treasury_BountyCanceled", 269 | "Treasury_BountyExtended", 270 | "TechnicalMembership_Dummy", 271 | "Currencies_Transferred", 272 | "Currencies_BalanceUpdated", 273 | "Currencies_Deposited", 274 | "Currencies_Withdrawn", 275 | "Vesting_VestingScheduleAdded", 276 | "Vesting_Claimed", 277 | "Vesting_VestingSchedulesUpdated", 278 | "Multisig_NewMultisig", 279 | "Multisig_MultisigApproval", 280 | "Multisig_MultisigExecuted", 281 | "Multisig_MultisigCancelled", 282 | "Balances_ReserveRepatriated", 283 | "Proxy_Announced", 284 | } 285 | 286 | // 269-108 = 161 = 141+4+15 287 | func IsExist(typeName string) bool { 288 | for _, v := range existTypes { 289 | if typeName == v { 290 | return true 291 | } 292 | } 293 | return false 294 | } 295 | 296 | type TypesJson struct { 297 | CoinName string `json:"coin_name"` 298 | ChainName string `json:"chain_name"` 299 | Version string `json:"version"` 300 | SpecVersion int `json:"spec_version"` 301 | Types []EventType `json:"types"` 302 | } 303 | 304 | type EventType struct { 305 | Name string `json:"name"` 306 | SubTypes []string `json:"sub_types"` 307 | } 308 | -------------------------------------------------------------------------------- /expand/base/types.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "github.com/stafiprotocol/go-substrate-rpc-client/scale" 7 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 8 | ) 9 | 10 | type BaseEventRecords struct { 11 | types.EventRecords 12 | Treasury_BountyProposed []EventTreasuryBountyProposed 13 | Treasury_BountyRejected []EventTreasuryBountyRejected 14 | Treasury_BountyBecameActive []EventTreasuryBountyBecameActive 15 | Treasury_BountyAwarded []EventTreasuryBountyAwarded 16 | Treasury_BountyClaimed []EventTreasuryBountyClaimed 17 | Treasury_BountyCanceled []EventTreasuryBountyCanceled 18 | Treasury_BountyExtended []EventTreasuryBountyExtended 19 | TechnicalMembership_Dummy []EventTechnicalMembershipDummy 20 | 21 | Currencies_Transferred []EventCurrenciesTransferred 22 | Currencies_BalanceUpdated []EventCurrenciesBalanceUpdated 23 | Currencies_Deposited []EventCurrenciesDeposited 24 | Currencies_Withdrawn []EventCurrenciesWithdrawn 25 | 26 | Vesting_VestingScheduleAdded []EventVestingVestingScheduleAdded 27 | Vesting_Claimed []EventVestingClaimed 28 | Vesting_VestingSchedulesUpdated []EventVestingVestingSchedulesUpdated 29 | 30 | Multisig_NewMultisig []types.EventMultisigNewMultisig 31 | Multisig_MultisigApproval []types.EventMultisigApproval 32 | Multisig_MultisigExecuted []types.EventMultisigExecuted 33 | Multisig_MultisigCancelled []types.EventMultisigCancelled 34 | 35 | Balances_ReserveRepatriated []EventBalancesReserveRepatriated 36 | Proxy_Announced []EventProxyAnnounced 37 | } 38 | 39 | func (d *BaseEventRecords) GetBalancesTransfer() []types.EventBalancesTransfer { 40 | return d.Balances_Transfer 41 | } 42 | func (d *BaseEventRecords) GetSystemExtrinsicSuccess() []types.EventSystemExtrinsicSuccess { 43 | return d.System_ExtrinsicSuccess 44 | } 45 | func (d *BaseEventRecords) GetSystemExtrinsicFailed() []types.EventSystemExtrinsicFailed { 46 | return d.System_ExtrinsicFailed 47 | } 48 | 49 | type EventTreasuryBountyProposed struct { 50 | Phase types.Phase 51 | BountyIndex types.U32 52 | Topics []types.Hash 53 | } 54 | 55 | type EventTreasuryBountyRejected struct { 56 | Phase types.Phase 57 | BountyIndex types.U32 58 | Balance types.U128 59 | Topics []types.Hash 60 | } 61 | 62 | type EventTreasuryBountyBecameActive struct { 63 | Phase types.Phase 64 | BountyIndex types.U32 65 | Topics []types.Hash 66 | } 67 | 68 | type EventTreasuryBountyAwarded struct { 69 | Phase types.Phase 70 | BountyIndex types.U32 71 | Who types.AccountID 72 | Topics []types.Hash 73 | } 74 | 75 | type EventTreasuryBountyClaimed struct { 76 | Phase types.Phase 77 | BountyIndex types.U32 78 | Balance types.U128 79 | Who types.AccountID 80 | Topics []types.Hash 81 | } 82 | 83 | type EventTreasuryBountyCanceled struct { 84 | Phase types.Phase 85 | BountyIndex types.U32 86 | Topics []types.Hash 87 | } 88 | 89 | type EventTreasuryBountyExtended struct { 90 | Phase types.Phase 91 | BountyIndex types.U32 92 | Topics []types.Hash 93 | } 94 | 95 | type EventTechnicalMembershipDummy struct { 96 | Phase types.Phase 97 | PhantomData types.Null 98 | Topics []types.Hash 99 | } 100 | 101 | type EventCurrenciesTransferred struct { 102 | Phase types.Phase 103 | CurrencyId CurrencyId 104 | AccountId1 types.AccountID 105 | AccountId2 types.AccountID 106 | Balance types.U128 107 | Topics []types.Hash 108 | } 109 | 110 | type EventCurrenciesBalanceUpdated struct { 111 | Phase types.Phase 112 | CurrencyId CurrencyId 113 | AccountId types.AccountID 114 | Balance types.U128 115 | Topics []types.Hash 116 | } 117 | 118 | type EventCurrenciesDeposited struct { 119 | Phase types.Phase 120 | CurrencyId CurrencyId 121 | AccountId types.AccountID 122 | Balance types.U128 123 | Topics []types.Hash 124 | } 125 | 126 | type EventCurrenciesWithdrawn struct { 127 | Phase types.Phase 128 | CurrencyId CurrencyId 129 | AccountId types.AccountID 130 | Balance types.U128 131 | Topics []types.Hash 132 | } 133 | type EventVestingVestingScheduleAdded struct { 134 | Phase types.Phase 135 | From types.AccountID 136 | To types.AccountID 137 | VestingSchedule VestingSchedule 138 | Topics []types.Hash 139 | } 140 | 141 | type EventVestingClaimed struct { 142 | Phase types.Phase 143 | Who types.AccountID 144 | Balance types.U128 145 | Topics []types.Hash 146 | } 147 | 148 | type EventVestingVestingSchedulesUpdated struct { 149 | Phase types.Phase 150 | Who types.AccountID 151 | Topics []types.Hash 152 | } 153 | 154 | type EventBalancesReserveRepatriated struct { 155 | Phase types.Phase 156 | From types.AccountID 157 | To types.AccountID 158 | Balance types.U128 159 | Status types.BalanceStatus 160 | Topics []types.Hash 161 | } 162 | type EventProxyAnnounced struct { 163 | Phase types.Phase 164 | Who types.AccountID 165 | ID types.AccountID 166 | Hash types.Hash 167 | Topics []types.Hash 168 | } 169 | 170 | type CurrencyId types.U32 171 | 172 | /* 173 | https://github.com/polkadot-js/api/blob/95c4f03bc3709c58b159623ec5c3c9794e077d08/packages/types/src/interfaces/balances/definitions.ts 174 | */ 175 | type VestingSchedule struct { 176 | Offset types.U128 177 | PerBlock types.U128 178 | StartingBlock types.BlockNumber 179 | } 180 | 181 | func (d *VestingSchedule) Decode(decoder scale.Decoder) error { 182 | err := decoder.Decode(&d.Offset) 183 | if err != nil { 184 | return err 185 | } 186 | err = decoder.Decode(&d.PerBlock) 187 | if err != nil { 188 | return err 189 | } 190 | 191 | return decoder.Decode(&d.StartingBlock) 192 | } 193 | 194 | type OracleKey types.U32 195 | 196 | type OracleValue types.U128 197 | 198 | type VecU8L20 struct { 199 | Value string 200 | } 201 | 202 | func (d *VecU8L20) Decode(decoder scale.Decoder) error { 203 | data := make([]byte, 20) 204 | err := decoder.Read(data) 205 | if err != nil { 206 | return fmt.Errorf("U8L20 read bytes error: %v", err) 207 | } 208 | d.Value = hex.EncodeToString(data) 209 | return nil 210 | } 211 | 212 | type VecU8L256 struct { 213 | Value string 214 | } 215 | 216 | func (d *VecU8L256) Decode(decoder scale.Decoder) error { 217 | data := make([]byte, 256) 218 | err := decoder.Read(data) 219 | if err != nil { 220 | return fmt.Errorf("U8L256 read bytes error: %v", err) 221 | } 222 | d.Value = hex.EncodeToString(data) 223 | return nil 224 | } 225 | 226 | type VecU8L32 struct { 227 | Value string 228 | } 229 | 230 | func (d *VecU8L32) Decode(decoder scale.Decoder) error { 231 | data := make([]byte, 32) 232 | err := decoder.Read(data) 233 | if err != nil { 234 | return fmt.Errorf("U8L32 read bytes error: %v", err) 235 | } 236 | d.Value = hex.EncodeToString(data) 237 | return nil 238 | } 239 | 240 | /* 241 | https://github.com/polkadot-js/api/blob/2906b02e413050be04980f472abb69a6991ad5e5/packages/types/src/interfaces/runtime/definitions.ts 242 | */ 243 | 244 | type FixedU128 types.U128 245 | 246 | /* 247 | https://github.com/open-web3-stack/open-web3.js/blob/2409235b00c03d0adf22258bb972a76d7aa57b4c/packages/orml-type-definitions/src/authority.ts 248 | */ 249 | type ScheduleTaskIndex types.U32 250 | 251 | /* 252 | https://github.com/polkadot-js/api/blob/2906b02e413050be04980f472abb69a6991ad5e5/packages/types/src/primitive/StorageKey.ts 253 | */ 254 | type StorageKey types.Bytes 255 | 256 | /* 257 | https://github.com/open-web3-stack/open-web3.js/blob/d6f3095f79999e9fc1d39f09a52215684398090c/packages/orml-type-definitions/src/graduallyUpdates.ts 258 | */ 259 | type StorageValue types.Bytes 260 | 261 | /* 262 | https://github.com/polkadot-js/api/blob/master/packages/types/src/interfaces/evm/types.ts 263 | */ 264 | type Log struct { 265 | Address types.H160 266 | Topics []types.H256 267 | Data types.Bytes 268 | } 269 | 270 | func (d *Log) Decode(decoder scale.Decoder) error { 271 | err := decoder.Decode(&d.Address) 272 | if err != nil { 273 | return err 274 | } 275 | err = decoder.Decode(&d.Topics) 276 | if err != nil { 277 | return err 278 | } 279 | return decoder.Decode(&d.Data) 280 | } 281 | 282 | /* 283 | https://github.com/polkadot-js/api/blob/master/packages/types/src/interfaces/collective/types.ts 284 | */ 285 | type MemberCount types.U32 286 | 287 | type Perbill types.U32 288 | -------------------------------------------------------------------------------- /expand/calc_fee.go: -------------------------------------------------------------------------------- 1 | package expand 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "github.com/stafiprotocol/go-substrate-rpc-client/scale" 7 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 8 | "math" 9 | ) 10 | 11 | /* 12 | 计算手续费 13 | */ 14 | 15 | type CalcFee struct { 16 | polynomial *WeightToFeeCoefficient 17 | multiplier int64 18 | per_byte_fee int64 19 | base_fee int64 20 | adjust_len_fee bool 21 | } 22 | 23 | func NewFee(meta *types.Metadata) (*CalcFee, error) { 24 | cf := new(CalcFee) 25 | md, err := NewMetadataExpand(meta) 26 | if err != nil { 27 | return nil, fmt.Errorf("new metadate expand error:%v", err) 28 | } 29 | _, tbfBytes, err := md.MV.GetConstants("TransactionPayment", "TransactionByteFee") 30 | if err != nil { 31 | return nil, fmt.Errorf("get TransactionByteFee constants error: %v", err) 32 | } 33 | var tbf types.U128 34 | decoder := scale.NewDecoder(bytes.NewReader(tbfBytes)) 35 | err = decoder.Decode(&tbf) 36 | if err != nil { 37 | return nil, fmt.Errorf("decoder TransactionByteFee error: %v", err) 38 | } 39 | cf.per_byte_fee = int64(tbf.Uint64()) 40 | 41 | _, wtfBytes, err := md.MV.GetConstants("TransactionPayment", "WeightToFee") 42 | if err != nil { 43 | return nil, fmt.Errorf("get WeightToFee constants error: %v", err) 44 | } 45 | decoder3 := scale.NewDecoder(bytes.NewReader(wtfBytes)) 46 | vec := new(Vec) 47 | var wtfc WeightToFeeCoefficient 48 | err = vec.ProcessVec(*decoder3, wtfc) 49 | if err != nil { 50 | return nil, fmt.Errorf("decode WeightToFee error: %v", err) 51 | } 52 | vv := vec.Value[0] 53 | cf.polynomial = vv.(*WeightToFeeCoefficient) 54 | 55 | _, ebwBytes, err := md.MV.GetConstants("System", "ExtrinsicBaseWeight") 56 | if err != nil { 57 | return nil, fmt.Errorf("get ExtrinsicBaseWeight constants error: %v", err) 58 | } 59 | 60 | var w types.U32 61 | decoder2 := scale.NewDecoder(bytes.NewReader(ebwBytes)) 62 | err = decoder2.Decode(&w) 63 | if err != nil { 64 | return nil, fmt.Errorf("decode ExtrinsicBaseWeight error: %v", err) 65 | } 66 | extrinsicBaseWeight := int64(w) 67 | cf.base_fee = cf.weight_to_fee(extrinsicBaseWeight) 68 | cf.adjust_len_fee = false //use V2 69 | return cf, nil 70 | } 71 | 72 | func NewCalcFee(polynomial *WeightToFeeCoefficient, extrinsic_base_weight, per_byte_fee, multiplier int64) *CalcFee { 73 | cf := new(CalcFee) 74 | cf.multiplier = multiplier 75 | cf.polynomial = polynomial 76 | cf.per_byte_fee = per_byte_fee 77 | cf.base_fee = cf.weight_to_fee(extrinsic_base_weight) 78 | cf.adjust_len_fee = false //v2-->false 79 | return cf 80 | } 81 | 82 | func (cf *CalcFee) SetMultiplier(multiplier int64) { 83 | cf.multiplier = multiplier 84 | } 85 | 86 | func (cf *CalcFee) CalcPartialFee(weight, len int64) int64 { 87 | unadjusted_len_fee := cf.per_byte_fee * len 88 | unadjusted_weight_fee := cf.weight_to_fee(weight) 89 | var ( 90 | len_fee, adjustable_fee int64 91 | ) 92 | if cf.adjust_len_fee { 93 | len_fee = 0 94 | adjustable_fee = unadjusted_len_fee + unadjusted_weight_fee 95 | } else { 96 | len_fee = unadjusted_len_fee 97 | adjustable_fee = unadjusted_weight_fee 98 | } 99 | fmt.Println("adjustable_fee: ", adjustable_fee) 100 | adjusted_fee := cf.calc(adjustable_fee) 101 | var result int64 102 | result += cf.base_fee 103 | result += len_fee 104 | result += adjusted_fee 105 | fmt.Println("BaseFee: ", cf.base_fee) 106 | fmt.Println("LengthFee: ", len_fee) 107 | fmt.Println("AdjuestFee: ", adjusted_fee) 108 | return result 109 | } 110 | 111 | func (cf *CalcFee) calc(adjustable_fee int64) int64 { 112 | //m:=big.NewInt(cf.multiplier) 113 | //af:=big.NewInt(adjustable_fee) 114 | //x:=m.Mod(m,af) 115 | // 116 | return int64(math.Mod(float64(cf.multiplier), float64(adjustable_fee))) 117 | } 118 | func (cf *CalcFee) weight_to_fee(weight int64) int64 { 119 | if cf.polynomial == nil { 120 | return 0 121 | } 122 | weight = int64(math.Pow(float64(weight), float64(cf.polynomial.Degree))) 123 | frac := int64(cf.polynomial.CoeffFrac.Value) * weight 124 | integer := cf.polynomial.CoeffInteger.Int64() * weight 125 | var acc int64 126 | if cf.polynomial.Negative { 127 | acc -= frac 128 | acc -= integer 129 | } else { 130 | acc += frac 131 | acc += integer 132 | } 133 | return acc 134 | } 135 | -------------------------------------------------------------------------------- /expand/call.go: -------------------------------------------------------------------------------- 1 | package expand 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 7 | ) 8 | 9 | /* 10 | 扩展: 创建一个新的Call方法 11 | */ 12 | func NewCall(callIdx string, args ...interface{}) (types.Call, error) { 13 | if len(callIdx) != 4 { 14 | return types.Call{}, fmt.Errorf("callIdx length is not equal 4,len=%d", len(callIdx)) 15 | } 16 | m, err := hex.DecodeString(callIdx[:2]) 17 | if err != nil { 18 | return types.Call{}, err 19 | } 20 | n, err := hex.DecodeString(callIdx[2:]) 21 | if err != nil { 22 | return types.Call{}, err 23 | } 24 | c := types.CallIndex{SectionIndex: m[0], MethodIndex: n[0]} 25 | var a []byte 26 | for _, arg := range args { 27 | e, err := types.EncodeToBytes(arg) 28 | if err != nil { 29 | return types.Call{}, err 30 | } 31 | a = append(a, e...) 32 | } 33 | return types.Call{c, a}, nil 34 | } 35 | -------------------------------------------------------------------------------- /expand/crust/types.go: -------------------------------------------------------------------------------- 1 | package crust 2 | 3 | import ( 4 | "fmt" 5 | "github.com/JFJun/stafi-substrate-go/expand/base" 6 | "github.com/stafiprotocol/go-substrate-rpc-client/scale" 7 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 8 | ) 9 | 10 | type CRustEventRecords struct { 11 | base.BaseEventRecords 12 | Offences_Offence []EventOffencesOffence 13 | //Treasury_BountyProposed []EventTreasuryBountyProposed 14 | //Treasury_BountyRejected []EventTreasuryBountyRejected 15 | //Treasury_BountyBecameActive []EventTreasuryBountyBecameActive 16 | //Treasury_BountyAwarded []EventTreasuryBountyAwarded 17 | //Treasury_BountyClaimed []EventTreasuryBountyClaimed 18 | //Treasury_BountyCanceled []EventTreasuryBountyCanceled 19 | //Treasury_BountyExtended []EventTreasuryBountyExtended 20 | //TechnicalMembership_Dummy []EventTechnicalMembershipDummy 21 | Swork_RegisterSuccess []EventSworkRegisterSuccess 22 | Swork_WorksReportSuccess []EventSworkWorksReportSuccess 23 | Swork_ABUpgradeSuccess []EventSworkABUpgradeSuccess 24 | Swork_ChillSuccess []EventSworkChillSuccess 25 | Swork_EnclaveUpgradeSuccess []EventSworkEnclaveUpgradeSuccess 26 | Market_StorageOrderSuccess []EventMarketStorageOrderSuccess 27 | Market_RegisterSuccess []EventMarketRegisterSuccess 28 | Market_PledgeSuccess []EventMarketPledgeSuccess 29 | Market_SetAliasSuccess []EventMarketSetAliasSuccess 30 | Market_PaysOrderSuccess []EventMarketPaysOrderSuccess 31 | Candy_CandyIssued []EventCandyCandyIssued 32 | Candy_CandyTransferred []EventCandyCandyTransferred 33 | Candy_CandyBurned []EventCandyCandyBurned 34 | Candy_BondEthSuccess []EventCandyBondEthSuccess 35 | } 36 | 37 | type EventOffencesOffence struct { 38 | Phase types.Phase 39 | Kind types.Bytes16 40 | OpaqueTimeSlot types.Bytes 41 | Bool bool 42 | Topics []types.Hash 43 | } 44 | 45 | //type EventTreasuryBountyProposed struct { 46 | // Phase types.Phase 47 | // BountyIndex types.U32 48 | // Topics []types.Hash 49 | //} 50 | // 51 | //type EventTreasuryBountyRejected struct { 52 | // Phase types.Phase 53 | // BountyIndex types.U32 54 | // Balance types.U128 55 | // Topics []types.Hash 56 | //} 57 | // 58 | //type EventTreasuryBountyBecameActive struct { 59 | // Phase types.Phase 60 | // BountyIndex types.U32 61 | // Topics []types.Hash 62 | //} 63 | // 64 | //type EventTreasuryBountyAwarded struct { 65 | // Phase types.Phase 66 | // BountyIndex types.U32 67 | // Who types.AccountID 68 | // Topics []types.Hash 69 | //} 70 | // 71 | //type EventTreasuryBountyClaimed struct { 72 | // Phase types.Phase 73 | // BountyIndex types.U32 74 | // Balance types.U128 75 | // Who types.AccountID 76 | // Topics []types.Hash 77 | //} 78 | // 79 | //type EventTreasuryBountyCanceled struct { 80 | // Phase types.Phase 81 | // BountyIndex types.U32 82 | // Topics []types.Hash 83 | //} 84 | // 85 | //type EventTreasuryBountyExtended struct { 86 | // Phase types.Phase 87 | // BountyIndex types.U32 88 | // Topics []types.Hash 89 | //} 90 | // 91 | //type EventTechnicalMembershipDummy struct { 92 | // Phase types.Phase 93 | // PhantomData types.Null 94 | // Topics []types.Hash 95 | //} 96 | 97 | type EventSworkRegisterSuccess struct { 98 | Phase types.Phase 99 | Who types.AccountID 100 | SworkerPubKey SworkerPubKey 101 | Topics []types.Hash 102 | } 103 | 104 | type EventSworkWorksReportSuccess struct { 105 | Phase types.Phase 106 | Who types.AccountID 107 | SworkerPubKey SworkerPubKey 108 | Topics []types.Hash 109 | } 110 | 111 | type EventSworkABUpgradeSuccess struct { 112 | Phase types.Phase 113 | Who types.AccountID 114 | SworkerPubKey1 SworkerPubKey 115 | SworkerPubKey2 SworkerPubKey 116 | Topics []types.Hash 117 | } 118 | 119 | type EventSworkChillSuccess struct { 120 | Phase types.Phase 121 | Who types.AccountID 122 | SworkerPubKey SworkerPubKey 123 | Topics []types.Hash 124 | } 125 | 126 | type EventSworkEnclaveUpgradeSuccess struct { 127 | Phase types.Phase 128 | Who types.AccountID 129 | SworkerPubKey SworkerPubKey 130 | Topics []types.Hash 131 | } 132 | 133 | type EventMarketStorageOrderSuccess struct { 134 | Phase types.Phase 135 | Who types.AccountID 136 | SorderInfo struct { 137 | Who types.AccountID 138 | Balance types.U128 139 | } 140 | SorderStatus SorderStatus 141 | Topics []types.Hash 142 | } 143 | 144 | type EventMarketRegisterSuccess struct { 145 | Phase types.Phase 146 | Who types.AccountID 147 | Topics []types.Hash 148 | } 149 | 150 | type EventMarketPledgeSuccess struct { 151 | Phase types.Phase 152 | Who types.AccountID 153 | Topics []types.Hash 154 | } 155 | 156 | type EventMarketSetAliasSuccess struct { 157 | Phase types.Phase 158 | Who types.AccountID 159 | FileAlias1 FileAlias 160 | FileAlias2 FileAlias 161 | Topics []types.Hash 162 | } 163 | 164 | type EventMarketPaysOrderSuccess struct { 165 | Phase types.Phase 166 | Who types.AccountID 167 | Topics []types.Hash 168 | } 169 | 170 | type EventCandyCandyIssued struct { 171 | Phase types.Phase 172 | Who types.AccountID 173 | Balance types.U128 174 | Topics []types.Hash 175 | } 176 | 177 | type EventCandyCandyTransferred struct { 178 | Phase types.Phase 179 | From types.AccountID 180 | To types.AccountID 181 | Balance types.U128 182 | Topics []types.Hash 183 | } 184 | 185 | type EventCandyCandyBurned struct { 186 | Phase types.Phase 187 | Who types.AccountID 188 | Balance types.U128 189 | Topics []types.Hash 190 | } 191 | 192 | type EventCandyBondEthSuccess struct { 193 | Phase types.Phase 194 | Who types.AccountID 195 | EthAddress EthAddress 196 | Topics []types.Hash 197 | } 198 | 199 | //---------------------- 200 | /* 201 | https://github.com/crustio/crust-api/blob/4a0d5c49dc3d07d0ef8d838d8e5bed42c923e68f/src/services/index.ts 202 | */ 203 | 204 | type EthAddress types.Bytes 205 | type FileAlias types.Bytes 206 | type SworkerPubKey types.Bytes 207 | 208 | type OrderStatus struct { 209 | Value string 210 | } 211 | 212 | func (d *OrderStatus) Decode(decoder scale.Decoder) error { 213 | b, err := decoder.ReadOneByte() 214 | if err != nil { 215 | return err 216 | } 217 | if b == 0 { 218 | d.Value = "Success" 219 | return nil 220 | } 221 | if b == 0 { 222 | d.Value = "Failed" 223 | return nil 224 | } 225 | if b == 0 { 226 | d.Value = "Pending" 227 | return nil 228 | } 229 | return fmt.Errorf("unkown order status index: %d", b) 230 | } 231 | 232 | type SorderStatus struct { 233 | CompletedOn types.BlockNumber 234 | ExpiredOn types.BlockNumber 235 | Status OrderStatus 236 | ClaimedAt types.BlockNumber 237 | } 238 | 239 | func (d *SorderStatus) Decode(decoder scale.Decoder) error { 240 | err := decoder.Decode(&d.CompletedOn) 241 | if err != nil { 242 | return err 243 | } 244 | err = decoder.Decode(&d.ExpiredOn) 245 | if err != nil { 246 | return err 247 | } 248 | err = decoder.Decode(&d.Status) 249 | if err != nil { 250 | return err 251 | } 252 | return decoder.Decode(&d.ClaimedAt) 253 | } 254 | -------------------------------------------------------------------------------- /expand/darwinia/types.go: -------------------------------------------------------------------------------- 1 | package darwinia 2 | 3 | import ( 4 | "github.com/JFJun/stafi-substrate-go/expand/base" 5 | "github.com/stafiprotocol/go-substrate-rpc-client/scale" 6 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 7 | ) 8 | 9 | type DarwiniaEventRecords struct { 10 | base.BaseEventRecords 11 | Kton_Endowed []EventKtonEndowed 12 | Kton_DustLost []EventKtonDustLost 13 | Kton_Transfer []EventKtonTransfer 14 | Kton_BalanceSet []EventKtonBalanceSet 15 | Kton_Deposit []EventKtonDeposit 16 | Kton_Reserved []EventKtonReserved 17 | Kton_Unreserved []EventKtonUnreserved 18 | Kton_ReserveRepatriated []EventKtonReserveRepatriated 19 | Staking_Slash []EventStakingSlash 20 | Staking_BondRing []EventStakingBondRing 21 | Staking_BondKton []EventStakingBondKton 22 | Staking_UnbondRing []EventStakingUnbondRing 23 | Staking_UnbondKton []EventStakingUnbondKton 24 | Staking_ClaimDepositsWithPunish []EventStakingClaimDepositsWithPunish 25 | ElectionsPhragmen_NewTerm []EventElectionsPhragmenNewTerm 26 | ElectionsPhragmen_EmptyTerm []EventElectionsPhragmenEmptyTerm 27 | ElectionsPhragmen_ElectionError []EventElectionsPhragmenElectionError 28 | ElectionsPhragmen_MemberKicked []EventElectionsPhragmenMemberKicked 29 | ElectionsPhragmen_MemberRenounced []EventElectionsPhragmenMemberRenounced 30 | ElectionsPhragmen_VoterReported []EventElectionsPhragmenVoterReported 31 | Claims_Claimed []EventClaimsClaimed 32 | EthereumBacking_RedeemRing []EventEthereumBackingRedeemRing 33 | EthereumBacking_RedeemKton []EventEthereumBackingRedeemKton 34 | EthereumBacking_RedeemDeposit []EventEthereumBackingRedeemDeposit 35 | EthereumRelay_Affirmed []EventEthereumRelayAffirmed 36 | EthereumRelay_DisputedAndAffirmed []EventEthereumRelayDisputedAndAffirmed 37 | EthereumRelay_Extended []EventEthereumRelayExtended 38 | EthereumRelay_NewRound []EventEthereumRelayNewRound 39 | EthereumRelay_GameOver []EventEthereumRelayGameOver 40 | EthereumRelay_RemoveConfirmedParcel []EventEthereumRelayRemoveConfirmedParcel 41 | EthereumRelay_VerifyReceipt []EventEthereumRelayVerifyReceipt 42 | EthereumRelay_Pended []EventEthereumRelayPended 43 | EthereumRelay_GuardVoted []EventEthereumRelayGuardVoted 44 | EthereumRelay_PendingRelayHeaderParcelConfirmed []EventEthereumRelayPendingRelayHeaderParcelConfirmed 45 | EthereumRelay_PendingRelayHeaderParcelRejected []EventEthereumRelayPendingRelayHeaderParcelRejected 46 | Treasury_Awarded []EventTreasuryAwarded 47 | Treasury_Rejected []EventTreasuryRejected 48 | Treasury_Burnt []EventTreasuryBurnt 49 | Treasury_Rollover []EventTreasuryRollover 50 | Treasury_DepositRing []EventTreasuryDepositRing 51 | Treasury_DepositKton []EventTreasuryDepositKton 52 | Treasury_BountyProposed []EventTreasuryBountyProposed 53 | Treasury_BountyRejected []EventTreasuryBountyRejected 54 | Treasury_BountyBecameActive []EventTreasuryBountyBecameActive 55 | Treasury_BountyAwarded []EventTreasuryBountyAwarded 56 | Treasury_BountyClaimed []EventTreasuryBountyClaimed 57 | Treasury_BountyCanceled []EventTreasuryBountyCanceled 58 | Treasury_BountyExtended []EventTreasuryBountyExtended 59 | Proxy_Announced []EventProxyAnnounced 60 | Multisig_NewMultisig []types.EventMultisigNewMultisig 61 | Multisig_MultisigApproval []types.EventMultisigApproval 62 | Multisig_MultisigExecuted []types.EventMultisigExecuted 63 | Multisig_MultisigCancelled []types.EventMultisigCancelled 64 | CrabIssuing_DummyEvent []EventCrabIssuingDummyEvent 65 | } 66 | 67 | func (d DarwiniaEventRecords) GetBalancesTransfer() []types.EventBalancesTransfer { 68 | return d.Balances_Transfer 69 | } 70 | 71 | func (d DarwiniaEventRecords) GetSystemExtrinsicSuccess() []types.EventSystemExtrinsicSuccess { 72 | return d.System_ExtrinsicSuccess 73 | } 74 | 75 | func (d DarwiniaEventRecords) GetSystemExtrinsicFailed() []types.EventSystemExtrinsicFailed { 76 | return d.System_ExtrinsicFailed 77 | } 78 | 79 | type EventKtonEndowed struct { 80 | Phase types.Phase 81 | Who types.AccountID 82 | Balance types.U128 83 | Topics []types.Hash 84 | } 85 | type EventKtonDustLost struct { 86 | Phase types.Phase 87 | Who types.AccountID 88 | Balance types.U128 89 | Topics []types.Hash 90 | } 91 | 92 | type EventKtonTransfer struct { 93 | Phase types.Phase 94 | From types.AccountID 95 | To types.AccountID 96 | Value types.U128 97 | Topics []types.Hash 98 | } 99 | type EventKtonBalanceSet struct { 100 | Phase types.Phase 101 | Who types.AccountID 102 | Free types.U128 103 | Reserved types.U128 104 | Topics []types.Hash 105 | } 106 | type EventKtonDeposit struct { 107 | Phase types.Phase 108 | Who types.AccountID 109 | Balance types.U128 110 | Topics []types.Hash 111 | } 112 | type EventKtonReserved struct { 113 | Phase types.Phase 114 | Who types.AccountID 115 | Balance types.U128 116 | Topics []types.Hash 117 | } 118 | type EventKtonUnreserved struct { 119 | Phase types.Phase 120 | Who types.AccountID 121 | Balance types.U128 122 | Topics []types.Hash 123 | } 124 | type EventKtonReserveRepatriated struct { 125 | Phase types.Phase 126 | From types.AccountID 127 | To types.AccountID 128 | Balance types.U128 129 | DestinationStatus types.BalanceStatus 130 | Topics []types.Hash 131 | } 132 | 133 | // EventStakingSlash is emitted when one validator (and its nominators) has been slashed by the given amount 134 | type EventStakingSlash struct { 135 | Phase types.Phase 136 | AccountID types.AccountID 137 | RingBalance types.U128 138 | KtonBalance types.U128 139 | Topics []types.Hash 140 | } 141 | 142 | type EventStakingBondRing struct { 143 | Phase types.Phase 144 | RingBalance types.U128 145 | TsInMs1 types.U64 146 | TsInMs2 types.U64 147 | Topics []types.Hash 148 | } 149 | 150 | type EventStakingBondKton struct { 151 | Phase types.Phase 152 | KtonBalance types.U128 153 | Topics []types.Hash 154 | } 155 | 156 | type EventStakingUnbondRing struct { 157 | Phase types.Phase 158 | RingBalance types.U128 159 | BlockNumber types.BlockNumber 160 | Topics []types.Hash 161 | } 162 | 163 | type EventStakingUnbondKton struct { 164 | Phase types.Phase 165 | KtonBalance types.U128 166 | BlockNumber types.BlockNumber 167 | Topics []types.Hash 168 | } 169 | 170 | type EventStakingClaimDepositsWithPunish struct { 171 | Phase types.Phase 172 | Who types.AccountID 173 | KtonBalance types.U128 174 | Topics []types.Hash 175 | } 176 | type EventElectionsPhragmenNewTerm struct { 177 | Phase types.Phase 178 | ABs []AccountBalance 179 | Topics []types.Hash 180 | } 181 | 182 | type EventElectionsPhragmenEmptyTerm struct { 183 | Phase types.Phase 184 | 185 | Topics []types.Hash 186 | } 187 | 188 | type EventElectionsPhragmenElectionError struct { 189 | Phase types.Phase 190 | 191 | Topics []types.Hash 192 | } 193 | 194 | type EventElectionsPhragmenMemberKicked struct { 195 | Phase types.Phase 196 | Who types.AccountID 197 | Topics []types.Hash 198 | } 199 | 200 | type EventElectionsPhragmenMemberRenounced struct { 201 | Phase types.Phase 202 | Who types.AccountID 203 | Topics []types.Hash 204 | } 205 | 206 | type EventElectionsPhragmenVoterReported struct { 207 | Phase types.Phase 208 | Who types.AccountID 209 | ID types.AccountID 210 | Bool bool 211 | Topics []types.Hash 212 | } 213 | 214 | type EventClaimsClaimed struct { 215 | Phase types.Phase 216 | Who types.AccountID 217 | AddressT base.VecU8L20 //[u8;20] 218 | RingBalance types.U128 219 | Topics []types.Hash 220 | } 221 | 222 | type EventEthereumBackingRedeemRing struct { 223 | Phase types.Phase 224 | Who types.AccountID 225 | Balance types.U128 226 | EthereumTransactionIndex types.H256 227 | Topics []types.Hash 228 | } 229 | 230 | type EventEthereumBackingRedeemKton struct { 231 | Phase types.Phase 232 | Who types.AccountID 233 | Balance types.U128 234 | EthereumTransactionIndex types.H256 235 | Topics []types.Hash 236 | } 237 | 238 | type EventEthereumBackingRedeemDeposit struct { 239 | Phase types.Phase 240 | Who types.AccountID 241 | DepositId types.U256 242 | RingBalance types.U128 243 | EthereumTransactionIndex types.H256 244 | Topics []types.Hash 245 | } 246 | 247 | type EventEthereumRelayAffirmed struct { 248 | Phase types.Phase 249 | Who types.AccountID 250 | RelayAffirmationId RelayAffirmationId 251 | Topics []types.Hash 252 | } 253 | 254 | type EventEthereumRelayDisputedAndAffirmed struct { 255 | Phase types.Phase 256 | Who types.AccountID 257 | RelayAffirmationId RelayAffirmationId 258 | Topics []types.Hash 259 | } 260 | 261 | type EventEthereumRelayExtended struct { 262 | Phase types.Phase 263 | Who types.AccountID 264 | RelayAffirmationId RelayAffirmationId 265 | Topics []types.Hash 266 | } 267 | 268 | type EventEthereumRelayNewRound struct { 269 | Phase types.Phase 270 | EthereumBlockNumber types.U64 271 | EthereumBlockNumbers []types.U64 272 | Topics []types.Hash 273 | } 274 | 275 | type EventEthereumRelayGameOver struct { 276 | Phase types.Phase 277 | EthereumBlockNumber types.U64 278 | Topics []types.Hash 279 | } 280 | 281 | type EventEthereumRelayRemoveConfirmedParcel struct { 282 | Phase types.Phase 283 | EthereumBlockNumber types.U64 284 | Topics []types.Hash 285 | } 286 | 287 | type EventEthereumRelayVerifyReceipt struct { 288 | Phase types.Phase 289 | Who types.AccountID 290 | EthereumReceipt EthereumReceipt 291 | EthereumBlockNumber types.U64 292 | Topics []types.Hash 293 | } 294 | 295 | type EventEthereumRelayPended struct { 296 | Phase types.Phase 297 | EthereumBlockNumber types.U64 298 | Topics []types.Hash 299 | } 300 | 301 | type EventEthereumRelayGuardVoted struct { 302 | Phase types.Phase 303 | EthereumBlockNumber types.U64 304 | Bool bool 305 | Topics []types.Hash 306 | } 307 | 308 | type EventEthereumRelayPendingRelayHeaderParcelConfirmed struct { 309 | Phase types.Phase 310 | EthereumBlockNumber types.U64 311 | Data types.Bytes 312 | Topics []types.Hash 313 | } 314 | 315 | type EventEthereumRelayPendingRelayHeaderParcelRejected struct { 316 | Phase types.Phase 317 | EthereumBlockNumber types.U64 318 | Topics []types.Hash 319 | } 320 | 321 | type EventTreasuryAwarded struct { 322 | Phase types.Phase 323 | ProposalIndex types.U32 324 | RingBalance types.U128 325 | KtonBalance types.U128 326 | Who types.AccountID 327 | Topics []types.Hash 328 | } 329 | 330 | type EventTreasuryRejected struct { 331 | Phase types.Phase 332 | ProposalIndex types.U32 333 | RingBalance types.U128 334 | KtonBalance types.U128 335 | Topics []types.Hash 336 | } 337 | 338 | type EventTreasuryBurnt struct { 339 | Phase types.Phase 340 | RingBalance types.U128 341 | KtonBalance types.U128 342 | Topics []types.Hash 343 | } 344 | 345 | type EventTreasuryRollover struct { 346 | Phase types.Phase 347 | RingBalance types.U128 348 | KtonBalance types.U128 349 | Topics []types.Hash 350 | } 351 | 352 | type EventTreasuryDepositRing struct { 353 | Phase types.Phase 354 | RingBalance types.U128 355 | Topics []types.Hash 356 | } 357 | 358 | type EventTreasuryDepositKton struct { 359 | Phase types.Phase 360 | KtonBalance types.U128 361 | Topics []types.Hash 362 | } 363 | 364 | type EventTreasuryBountyProposed struct { 365 | Phase types.Phase 366 | BountyIndex BountyIndex 367 | Topics []types.Hash 368 | } 369 | 370 | type EventTreasuryBountyRejected struct { 371 | Phase types.Phase 372 | BountyIndex BountyIndex 373 | RingBalance types.U128 374 | Topics []types.Hash 375 | } 376 | 377 | type EventTreasuryBountyBecameActive struct { 378 | Phase types.Phase 379 | BountyIndex BountyIndex 380 | Topics []types.Hash 381 | } 382 | 383 | type EventTreasuryBountyAwarded struct { 384 | Phase types.Phase 385 | BountyIndex BountyIndex 386 | Who types.AccountID 387 | Topics []types.Hash 388 | } 389 | 390 | type EventTreasuryBountyClaimed struct { 391 | Phase types.Phase 392 | BountyIndex BountyIndex 393 | RingBalance types.U128 394 | Who types.AccountID 395 | Topics []types.Hash 396 | } 397 | 398 | type EventTreasuryBountyCanceled struct { 399 | Phase types.Phase 400 | BountyIndex BountyIndex 401 | Topics []types.Hash 402 | } 403 | 404 | type EventTreasuryBountyExtended struct { 405 | Phase types.Phase 406 | BountyIndex BountyIndex 407 | Topics []types.Hash 408 | } 409 | 410 | type EventProxyAnnounced struct { 411 | Phase types.Phase 412 | Who types.AccountID 413 | ID types.AccountID 414 | Hash types.Hash 415 | Topics []types.Hash 416 | } 417 | 418 | type EventCrabIssuingDummyEvent struct { 419 | Phase types.Phase 420 | Who types.AccountID 421 | RingBalance types.U128 422 | MappedRing types.U128 423 | Topics []types.Hash 424 | } 425 | 426 | //--------------------- 427 | type AccountBalance struct { 428 | Who types.AccountID 429 | Balance types.U128 430 | } 431 | 432 | type EthereumReceipt struct { 433 | GasUsed types.U256 434 | LogBloom base.VecU8L256 435 | Logs []types.Null 436 | Outcome types.Null 437 | } 438 | 439 | func (d *EthereumReceipt) Decode(decoder scale.Decoder) error { 440 | err := decoder.Decode(&d.GasUsed) 441 | if err != nil { 442 | return err 443 | } 444 | err = decoder.Decode(&d.LogBloom) 445 | if err != nil { 446 | return err 447 | } 448 | err = decoder.Decode(&d.Logs) 449 | if err != nil { 450 | return err 451 | } 452 | return decoder.Decode(&d.Outcome) 453 | } 454 | 455 | type RelayAffirmationId struct { 456 | RelayHeaderId types.U64 457 | Round types.U32 458 | Index types.U32 459 | } 460 | 461 | func (d *RelayAffirmationId) Decode(decoder scale.Decoder) error { 462 | err := decoder.Decode(&d.RelayHeaderId) 463 | if err != nil { 464 | return err 465 | } 466 | err = decoder.Decode(&d.Round) 467 | if err != nil { 468 | return err 469 | } 470 | return decoder.Decode(&d.Index) 471 | } 472 | 473 | type BountyIndex types.U32 474 | -------------------------------------------------------------------------------- /expand/event_records.go: -------------------------------------------------------------------------------- 1 | package expand 2 | 3 | import ( 4 | "github.com/JFJun/stafi-substrate-go/expand/acala" 5 | "github.com/JFJun/stafi-substrate-go/expand/base" 6 | "github.com/JFJun/stafi-substrate-go/expand/chainX" 7 | "github.com/JFJun/stafi-substrate-go/expand/crust" 8 | "github.com/JFJun/stafi-substrate-go/expand/darwinia" 9 | "github.com/JFJun/stafi-substrate-go/expand/ori" 10 | "github.com/JFJun/stafi-substrate-go/expand/polkadot" 11 | "github.com/JFJun/stafi-substrate-go/expand/stafi" 12 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 13 | "strings" 14 | ) 15 | 16 | type IEventRecords interface { 17 | GetBalancesTransfer() []types.EventBalancesTransfer 18 | GetSystemExtrinsicSuccess() []types.EventSystemExtrinsicSuccess 19 | GetSystemExtrinsicFailed() []types.EventSystemExtrinsicFailed 20 | } 21 | 22 | /* 23 | 扩展: 解析event 24 | */ 25 | func DecodeEventRecords(meta *types.Metadata, rawData string, chainName string) (IEventRecords, error) { 26 | e := types.EventRecordsRaw(types.MustHexDecodeString(rawData)) 27 | var ier IEventRecords 28 | switch strings.ToLower(chainName) { 29 | case "chainx": 30 | var events chainX.ChainXEventRecords 31 | err := e.DecodeEventRecords(meta, &events) 32 | if err != nil { 33 | return nil, err 34 | } 35 | ier = &events 36 | case "crab", "darwinia": 37 | var events darwinia.DarwiniaEventRecords 38 | err := e.DecodeEventRecords(meta, &events) 39 | if err != nil { 40 | return nil, err 41 | } 42 | ier = &events 43 | case "crust": 44 | var events crust.CRustEventRecords 45 | err := e.DecodeEventRecords(meta, &events) 46 | if err != nil { 47 | return nil, err 48 | } 49 | ier = &events 50 | case "mandala": // acala mandala 网络 51 | var events acala.AcalaEventRecords 52 | err := e.DecodeEventRecords(meta, &events) 53 | if err != nil { 54 | return nil, err 55 | } 56 | ier = &events 57 | case "node": //stafi 58 | var events stafi.StafiEventRecords 59 | err := e.DecodeEventRecords(meta, &events) 60 | if err != nil { 61 | return nil, err 62 | } 63 | ier = &events 64 | case "orion": 65 | var events ori.OrionEventRecords 66 | err := e.DecodeEventRecords(meta, &events) 67 | if err != nil { 68 | return nil, err 69 | } 70 | ier = &events 71 | case "polkadot": 72 | var events polkadot.PolkadotEventRecords 73 | err := e.DecodeEventRecords(meta, &events) 74 | if err != nil { 75 | return nil, err 76 | } 77 | ier = &events 78 | default: 79 | var events base.BaseEventRecords 80 | err := e.DecodeEventRecords(meta, &events) 81 | if err != nil { 82 | return nil, err 83 | } 84 | ier = &events 85 | } 86 | return ier, nil 87 | } 88 | -------------------------------------------------------------------------------- /expand/extrinsic_decode.go: -------------------------------------------------------------------------------- 1 | package expand 2 | 3 | /* 4 | 扩展:解析extrinsic 5 | substrate2.0的extrinsic都是这样,所以这里的变动其实很小 6 | 这里编写都是为了与github.com/JFJun/substrate-go保持一制,所以会显得有点混乱 7 | */ 8 | import ( 9 | "fmt" 10 | "github.com/JFJun/stafi-substrate-go/utils" 11 | "github.com/huandu/xstrings" 12 | "github.com/stafiprotocol/go-substrate-rpc-client/scale" 13 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 14 | ) 15 | 16 | type ExtrinsicDecoder struct { 17 | ExtrinsicLength int `json:"extrinsic_length"` 18 | VersionInfo string `json:"version_info"` 19 | ContainsTransaction bool `json:"contains_transaction"` 20 | Address string `json:"address"` 21 | Signature string `json:"signature"` 22 | SignatureVersion int `json:"signature_version"` 23 | Nonce int `json:"nonce"` 24 | Era string `json:"era"` 25 | Tip string `json:"tip"` 26 | CallIndex string `json:"call_index"` 27 | CallModule string `json:"call_module"` 28 | CallModuleFunction string `json:"call_module_function"` 29 | Params []ExtrinsicParam `json:"params"` 30 | me *MetadataExpand 31 | Value interface{} 32 | } 33 | 34 | type ExtrinsicParam struct { 35 | Name string `json:"name"` 36 | Type string `json:"type"` 37 | Value interface{} `json:"value"` 38 | ValueRaw string `json:"value_raw"` 39 | } 40 | 41 | func NewExtrinsicDecoder(meta *types.Metadata) (*ExtrinsicDecoder, error) { 42 | ed := new(ExtrinsicDecoder) 43 | var err error 44 | ed.me, err = NewMetadataExpand(meta) 45 | if err != nil { 46 | return nil, err 47 | } 48 | return ed, nil 49 | } 50 | 51 | func (ed *ExtrinsicDecoder) ProcessExtrinsicDecoder(decoder scale.Decoder) error { 52 | var length types.UCompact 53 | err := decoder.Decode(&length) 54 | if err != nil { 55 | return fmt.Errorf("decode extrinsic: length error: %v", err) 56 | } 57 | ed.ExtrinsicLength = int(utils.UCompactToBigInt(length).Int64()) 58 | vi, err := decoder.ReadOneByte() 59 | if err != nil { 60 | return fmt.Errorf("decode extrinsic: read version info error: %v", err) 61 | } 62 | ed.VersionInfo = utils.BytesToHex([]byte{vi}) 63 | ed.ContainsTransaction = utils.U256(ed.VersionInfo).Int64() >= 80 64 | //大多数都是84了,所以只处理84 65 | if ed.VersionInfo == "04" || ed.VersionInfo == "84" { 66 | if ed.ContainsTransaction { 67 | // 1. 解析from地址 68 | var address Address 69 | err = decoder.Decode(&address) 70 | if err != nil { 71 | return fmt.Errorf("decode extrinsic: decode address error: %v", err) 72 | } 73 | ed.Address = address.Value 74 | //2。解析签名版本 75 | var sv types.U8 76 | err = decoder.Decode(&sv) 77 | if err != nil { 78 | return fmt.Errorf("decode extrinsic: decode signature version error: %v", err) 79 | } 80 | ed.SignatureVersion = int(sv) 81 | // 3。 解析签名 82 | if ed.SignatureVersion == 2 { 83 | //解析 ecdsa signature 84 | sig := make([]byte, 65) 85 | err = decoder.Read(sig) 86 | if err != nil { 87 | return fmt.Errorf("decode extrinsic: decode ecdsa signature error: %v", err) 88 | } 89 | ed.Signature = utils.BytesToHex(sig) 90 | } else { 91 | // 解析 sr25519 signature 92 | var sig types.Signature 93 | err = decoder.Decode(&sig) 94 | if err != nil { 95 | return fmt.Errorf("decode extrinsic: decode sr25519 signature error: %v", err) 96 | } 97 | ed.Signature = sig.Hex() 98 | } 99 | // 4. 解析era 100 | var era types.ExtrinsicEra 101 | err = decoder.Decode(&era) 102 | if err != nil { 103 | return fmt.Errorf("decode extrinsic: decode era error: %v", err) 104 | } 105 | if era.IsMortalEra { 106 | eraBytes := []byte{era.AsMortalEra.First, era.AsMortalEra.Second} 107 | ed.Era = utils.BytesToHex(eraBytes) 108 | } 109 | //5. 解析nonce 110 | var nonce types.UCompact 111 | err = decoder.Decode(&nonce) 112 | if err != nil { 113 | return fmt.Errorf("decode extrinsic: decode nonce error: %v", err) 114 | } 115 | //new 116 | 117 | ed.Nonce = int(utils.UCompactToBigInt(nonce).Int64()) 118 | // 6.解析tip 119 | var tip types.UCompact 120 | 121 | err = decoder.Decode(&tip) 122 | if err != nil { 123 | return fmt.Errorf("decode tip error: %v", err) 124 | } 125 | ed.Tip = fmt.Sprintf("%d", utils.UCompactToBigInt(tip).Int64()) 126 | } 127 | //处理callIndex 128 | callIndex := make([]byte, 2) 129 | err = decoder.Read(callIndex) 130 | if err != nil { 131 | return fmt.Errorf("decode extrinsic: read call index bytes error: %v", err) 132 | } 133 | ed.CallIndex = xstrings.RightJustify(utils.IntToHex(callIndex[0]), 2, "0") + 134 | xstrings.RightJustify(utils.IntToHex(callIndex[1]), 2, "0") 135 | } else { 136 | return fmt.Errorf("extrinsics version %s is not support", ed.VersionInfo) 137 | } 138 | if ed.CallIndex != "" { 139 | _ = ed.decodeCallIndex(decoder) 140 | //fmt.Println(err) 141 | //if err != nil { 142 | // return err 143 | //} 144 | } 145 | result := map[string]interface{}{ 146 | "extrinsic_length": ed.ExtrinsicLength, 147 | "version_info": ed.VersionInfo, 148 | } 149 | if ed.ContainsTransaction { 150 | result["account_id"] = ed.Address 151 | result["signature"] = ed.Signature 152 | result["nonce"] = ed.Nonce 153 | result["era"] = ed.Era 154 | } 155 | if ed.CallIndex != "" { 156 | result["call_code"] = ed.CallIndex 157 | result["call_module_function"] = ed.CallModuleFunction 158 | result["call_module"] = ed.CallModule 159 | } 160 | result["nonce"] = ed.Nonce 161 | result["era"] = ed.Era 162 | result["tip"] = ed.Tip 163 | result["params"] = ed.Params 164 | result["length"] = ed.ExtrinsicLength 165 | ed.Value = result 166 | return nil 167 | } 168 | 169 | func (ed *ExtrinsicDecoder) decodeCallIndex(decoder scale.Decoder) error { 170 | var err error 171 | //避免指针为空 172 | defer func() { 173 | if errs := recover(); errs != nil { 174 | err = fmt.Errorf("decode call catch panic ,err=%v", errs) 175 | } 176 | }() 177 | // 解析 call index 178 | // 这里只能硬编码了,因为decode函数的原因,无法动态的根据type name去解析 179 | // 这里我只解析自己想要的,比如说Timestamp,Balance.transfer,Utility.batch 180 | modName, callName, err := ed.me.MV.FindNameByCallIndex(ed.CallIndex) 181 | if err != nil { 182 | return fmt.Errorf("decode call: %v", err) 183 | } 184 | ed.CallModule = modName 185 | ed.CallModuleFunction = callName 186 | switch modName { 187 | case "Timestamp": 188 | if callName == "set" { 189 | //Compact 190 | var u types.UCompact 191 | err = decoder.Decode(&u) 192 | if err != nil { 193 | return fmt.Errorf("decode call: decode Timestamp.set error: %v", err) 194 | } 195 | 196 | ed.Params = append(ed.Params, 197 | ExtrinsicParam{ 198 | Name: "now", 199 | Type: "Compact", 200 | Value: utils.UCompactToBigInt(u).Int64(), 201 | }) 202 | } 203 | case "Balances": 204 | if callName == "transfer" || callName == "transfer_keep_alive" { 205 | // 0 ---> Address 206 | var addrValue string 207 | var address Address 208 | err = decoder.Decode(&address) 209 | if err != nil { 210 | return fmt.Errorf("decode call: decode Balances.transfer.Address error: %v", err) 211 | } 212 | addrValue = address.Value 213 | 214 | ed.Params = append(ed.Params, 215 | ExtrinsicParam{ 216 | Name: "dest", 217 | Type: "Address", 218 | Value: addrValue, 219 | ValueRaw: addrValue, 220 | }) 221 | // 1 ----> Compact 222 | var b types.UCompact 223 | err = decoder.Decode(&b) 224 | if err != nil { 225 | return fmt.Errorf("decode call: decode Balances.transfer.Compact error: %v", err) 226 | } 227 | 228 | ed.Params = append(ed.Params, 229 | ExtrinsicParam{ 230 | Name: "value", 231 | Type: "Compact", 232 | Value: utils.UCompactToBigInt(b).Int64(), 233 | }) 234 | } 235 | case "Utility": 236 | if callName == "batch" { 237 | 238 | // 0--> calls Vec 239 | var tc TransferCall 240 | vec := new(Vec) 241 | _ = vec.ProcessVec(decoder, tc) 242 | //if err != nil { 243 | // return fmt.Errorf("decode call: decode Utility.batch error: %v", err) 244 | //} 245 | //utils.CheckStructData(vec.Value) 246 | ep := ExtrinsicParam{} 247 | ep.Name = "calls" 248 | ep.Type = "Vec" 249 | var result []interface{} 250 | 251 | for _, value := range vec.Value { 252 | tcv := value.(*TransferCall) 253 | //检查一下是否为BalanceTransfer 254 | data := tcv.Value.(map[string]interface{}) 255 | callIndex := data["call_index"].(string) 256 | btCallIdx, err := ed.me.MV.GetCallIndex("Balances", "transfer") 257 | if err != nil { 258 | return fmt.Errorf("decode Utility.batch: get Balances.transfer call index error: %v", err) 259 | } 260 | btkaCallIdx, err := ed.me.MV.GetCallIndex("Balances", "transfer_keep_alive") 261 | if err != nil { 262 | return fmt.Errorf("decode Utility.batch: get Balances.transfer_keep_alive call index error: %v", err) 263 | } 264 | if callIndex == btCallIdx || callIndex == btkaCallIdx { 265 | mn, cn, err := ed.me.MV.FindNameByCallIndex(callIndex) 266 | if err != nil { 267 | return fmt.Errorf("decode Utility.batch: get call index error: %v", err) 268 | } 269 | if mn != "Balances" { 270 | return fmt.Errorf("decode Utility.batch: call module name is not 'Balances' ,NAME=%s", mn) 271 | } 272 | data["call_function"] = cn 273 | data["call_module"] = mn 274 | result = append(result, data) 275 | } 276 | } 277 | ep.Value = result 278 | ed.Params = append(ed.Params, ep) 279 | } 280 | default: 281 | // unsopport 282 | return nil 283 | 284 | } 285 | return nil 286 | } 287 | 288 | //----------support for bifrost 289 | -------------------------------------------------------------------------------- /expand/metadata.go: -------------------------------------------------------------------------------- 1 | package expand 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | "fmt" 7 | "github.com/JFJun/stafi-substrate-go/utils" 8 | "github.com/huandu/xstrings" 9 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 10 | ) 11 | 12 | /* 13 | 对metadata进行扩展,添加一些实用的功能 14 | 由于大多数的波卡链都升级到了v11和v12,所以只对大于v11的链处理 15 | */ 16 | type MetadataExpand struct { 17 | meta *types.Metadata 18 | MV iMetaVersion 19 | } 20 | type iMetaVersion interface { 21 | GetCallIndex(moduleName, fn string) (callIdx string, err error) 22 | FindNameByCallIndex(callIdx string) (moduleName, fn string, err error) 23 | GetConstants(modName, constantsName string) (constantsType string, constantsValue []byte, err error) 24 | } 25 | 26 | func NewMetadataExpand(meta *types.Metadata) (*MetadataExpand, error) { 27 | me := new(MetadataExpand) 28 | me.meta = meta 29 | if meta.IsMetadataV11 { 30 | me.MV = newV11(meta.AsMetadataV11.Modules) 31 | } else if meta.IsMetadataV12 { 32 | me.MV = newV12(meta.AsMetadataV12.Modules) 33 | } else { 34 | return nil, errors.New("metadata version is not v11 or v12") 35 | } 36 | return me, nil 37 | } 38 | 39 | type v11 struct { 40 | module []types.ModuleMetadataV10 41 | } 42 | 43 | func (v v11) GetCallIndex(moduleName, fn string) (callIdx string, err error) { 44 | //避免指针为空 45 | defer func() { 46 | if errs := recover(); errs != nil { 47 | callIdx = "" 48 | err = fmt.Errorf("catch panic ,err=%v", errs) 49 | } 50 | }() 51 | mi := uint8(0) 52 | for _, mod := range v.module { 53 | if !mod.HasCalls { 54 | continue 55 | } 56 | if string(mod.Name) != moduleName { 57 | mi++ 58 | continue 59 | } 60 | for ci, f := range mod.Calls { 61 | if string(f.Name) == fn { 62 | return xstrings.RightJustify(utils.IntToHex(mi), 2, "0") + xstrings.RightJustify(utils.IntToHex(ci), 2, "0"), nil 63 | } 64 | } 65 | } 66 | return "", fmt.Errorf("do not find this call index") 67 | } 68 | 69 | func (v v11) FindNameByCallIndex(callIdx string) (moduleName, fn string, err error) { 70 | if len(callIdx) != 4 { 71 | return "", "", fmt.Errorf("call index length is not equal 4: length: %d", len(callIdx)) 72 | } 73 | data, err := hex.DecodeString(callIdx) 74 | if err != nil { 75 | return "", "", fmt.Errorf("call index is not hex string") 76 | } 77 | mi := int(data[0]) 78 | ci := int(data[1]) 79 | for i, mod := range v.module { 80 | if !mod.HasCalls { 81 | continue 82 | } 83 | if i == int(mi) { 84 | 85 | for j, call := range mod.Calls { 86 | if j == int(ci) { 87 | moduleName = string(mod.Name) 88 | fn = string(call.Name) 89 | return 90 | } 91 | } 92 | } 93 | } 94 | return "", "", fmt.Errorf("do not find this callInx info: %s", callIdx) 95 | } 96 | 97 | func (v v11) GetConstants(modName, constantsName string) (constantsType string, constantsValue []byte, err error) { 98 | defer func() { 99 | if errs := recover(); errs != nil { 100 | err = fmt.Errorf("catch panic ,err=%v", errs) 101 | } 102 | }() 103 | for _, mod := range v.module { 104 | if modName == string(mod.Name) { 105 | for _, constants := range mod.Constants { 106 | if string(constants.Name) == constantsName { 107 | constantsType = string(constants.Type) 108 | constantsValue = constants.Value 109 | return constantsType, constantsValue, nil 110 | } 111 | } 112 | } 113 | } 114 | return "", nil, fmt.Errorf("do not find this constants,moduleName=%s,"+ 115 | "constantsName=%s", modName, constantsName) 116 | } 117 | 118 | func newV11(module []types.ModuleMetadataV10) *v11 { 119 | v := new(v11) 120 | v.module = module 121 | return v 122 | } 123 | 124 | type v12 struct { 125 | module []types.ModuleMetadataV12 126 | } 127 | 128 | func (v v12) FindNameByCallIndex(callIdx string) (moduleName, fn string, err error) { 129 | if len(callIdx) != 4 { 130 | return "", "", fmt.Errorf("call index length is not equal 4: length: %d", len(callIdx)) 131 | } 132 | data, err := hex.DecodeString(callIdx) 133 | if err != nil { 134 | return "", "", fmt.Errorf("call index is not hex string") 135 | } 136 | for _, mod := range v.module { 137 | if !mod.HasCalls { 138 | continue 139 | } 140 | if mod.Index == data[0] { 141 | 142 | for j, call := range mod.Calls { 143 | if j == int(data[1]) { 144 | moduleName = string(mod.Name) 145 | fn = string(call.Name) 146 | return 147 | } 148 | } 149 | } 150 | } 151 | return "", "", fmt.Errorf("do not find this callInx info: %s", callIdx) 152 | } 153 | 154 | func (v v12) GetCallIndex(moduleName, fn string) (callIdx string, err error) { 155 | //避免指针为空 156 | defer func() { 157 | if errs := recover(); errs != nil { 158 | callIdx = "" 159 | err = fmt.Errorf("catch panic ,err=%v", errs) 160 | } 161 | }() 162 | for _, mod := range v.module { 163 | if !mod.HasCalls { 164 | continue 165 | } 166 | if string(mod.Name) != moduleName { 167 | 168 | continue 169 | } 170 | for ci, f := range mod.Calls { 171 | if string(f.Name) == fn { 172 | return xstrings.RightJustify(utils.IntToHex(mod.Index), 2, "0") + xstrings.RightJustify(utils.IntToHex(ci), 2, "0"), nil 173 | } 174 | } 175 | } 176 | return "", fmt.Errorf("do not find this call index") 177 | } 178 | func (v v12) GetConstants(modName, constantsName string) (constantsType string, constantsValue []byte, err error) { 179 | defer func() { 180 | if errs := recover(); errs != nil { 181 | err = fmt.Errorf("catch panic ,err=%v", errs) 182 | } 183 | }() 184 | for _, mod := range v.module { 185 | if modName == string(mod.Name) { 186 | for _, constants := range mod.Constants { 187 | if string(constants.Name) == constantsName { 188 | constantsType = string(constants.Type) 189 | constantsValue = constants.Value 190 | return constantsType, constantsValue, nil 191 | } 192 | } 193 | } 194 | } 195 | return "", nil, fmt.Errorf("do not find this constants,moduleName=%s,"+ 196 | "constantsName=%s", modName, constantsName) 197 | } 198 | 199 | func newV12(module []types.ModuleMetadataV12) *v12 { 200 | v := new(v12) 201 | v.module = module 202 | return v 203 | } 204 | 205 | /* 206 | Balances.transfer 207 | */ 208 | func (e *MetadataExpand) BalanceTransferCall(to string, amount uint64) (types.Call, error) { 209 | var ( 210 | call types.Call 211 | ) 212 | callIdx, err := e.MV.GetCallIndex("Balances", "transfer") 213 | if err != nil { 214 | return call, err 215 | } 216 | recipientPubkey := utils.AddressToPublicKey(to) 217 | return NewCall(callIdx, types.NewAddressFromAccountID(types.MustHexDecodeString(recipientPubkey)), 218 | types.NewUCompactFromUInt(amount)) 219 | 220 | } 221 | 222 | /* 223 | Balances.transfer_keep_alive 224 | */ 225 | func (e *MetadataExpand) BalanceTransferKeepAliveCall(to string, amount uint64) (types.Call, error) { 226 | var ( 227 | call types.Call 228 | ) 229 | callIdx, err := e.MV.GetCallIndex("Balances", "transfer_keep_alive") 230 | if err != nil { 231 | return call, err 232 | } 233 | recipientPubkey := utils.AddressToPublicKey(to) 234 | return NewCall(callIdx, types.NewAddressFromAccountID(types.MustHexDecodeString(recipientPubkey)), 235 | types.NewUCompactFromUInt(amount)) 236 | 237 | } 238 | 239 | /* 240 | Utility.batch 241 | keepAlive: true->Balances.transfer_keep_alive false->Balances.transfer 242 | */ 243 | func (e *MetadataExpand) UtilityBatchTxCall(toAmount map[string]uint64, keepAlive bool) (types.Call, error) { 244 | var ( 245 | call types.Call 246 | err error 247 | ) 248 | if len(toAmount) == 0 { 249 | return call, errors.New("toAmount is null") 250 | } 251 | var calls []types.Call 252 | for to, amount := range toAmount { 253 | var ( 254 | btCall types.Call 255 | ) 256 | if keepAlive { 257 | btCall, err = e.BalanceTransferKeepAliveCall(to, amount) 258 | } else { 259 | btCall, err = e.BalanceTransferCall(to, amount) 260 | } 261 | if err != nil { 262 | return call, err 263 | } 264 | calls = append(calls, btCall) 265 | } 266 | callIdx, err := e.MV.GetCallIndex("Utility", "batch") 267 | if err != nil { 268 | return call, err 269 | } 270 | return NewCall(callIdx, calls) 271 | } 272 | 273 | /* 274 | transfer with memo 275 | */ 276 | func (e *MetadataExpand) UtilityBatchTxWithMemo(to, memo string, amount uint64) (types.Call, error) { 277 | var ( 278 | call types.Call 279 | ) 280 | btCall, err := e.BalanceTransferCall(to, amount) 281 | if err != nil { 282 | return call, err 283 | } 284 | smCallIdx, err := e.MV.GetCallIndex("System", "remark") 285 | if err != nil { 286 | return call, err 287 | } 288 | smCall, err := NewCall(smCallIdx, memo) 289 | ubCallIdx, err := e.MV.GetCallIndex("Utility", "batch") 290 | if err != nil { 291 | return call, err 292 | } 293 | return NewCall(ubCallIdx, btCall, smCall) 294 | } 295 | -------------------------------------------------------------------------------- /expand/ori/types.go: -------------------------------------------------------------------------------- 1 | package ori 2 | 3 | import ( 4 | "github.com/JFJun/stafi-substrate-go/expand/base" 5 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 6 | ) 7 | 8 | type OrionEventRecords struct { 9 | base.BaseEventRecords 10 | EVM_Log []EventEVMLog 11 | EVM_Created []EventEVMCreated 12 | EVM_CreatedFailed []EventEVMCreatedFailed 13 | EVM_Executed []EventEVMExecuted 14 | EVM_ExecutedFailed []EventEVMExecutedFailed 15 | EVM_BalanceDeposit []EventEVMBalanceDeposit 16 | EVM_BalanceWithdraw []EventEVMBalanceWithdraw 17 | Issue_AddStage []EventIssueAddStage 18 | Issue_ChangeOperate []EventIssueChangeOperate 19 | Gov_OpenProposal []EventGovOpenProposal 20 | Gov_CloseProposal []EventGovCloseProposal 21 | Gov_Voted []EventGovVoted 22 | } 23 | type EventGovVoted struct { 24 | Phase types.Phase 25 | U32 types.U32 26 | AccountId types.AccountID 27 | Bool types.Bool 28 | Balance types.U128 29 | Topics []types.Hash 30 | } 31 | type EventGovCloseProposal struct { 32 | Phase types.Phase 33 | U32 types.U32 34 | Bool types.Bool 35 | Topics []types.Hash 36 | } 37 | type EventGovOpenProposal struct { 38 | Phase types.Phase 39 | U32 types.U32 40 | Topics []types.Hash 41 | } 42 | type EventIssueChangeOperate struct { 43 | Phase types.Phase 44 | Data1 types.Bytes 45 | Data2 types.Bytes 46 | Topics []types.Hash 47 | } 48 | type EventIssueAddStage struct { 49 | Phase types.Phase 50 | Data types.Bytes 51 | Balance types.U128 52 | Topics []types.Hash 53 | } 54 | type EventEVMBalanceWithdraw struct { 55 | Phase types.Phase 56 | AccountId types.AccountID 57 | H160 types.H160 58 | U256 types.U256 59 | Topics []types.Hash 60 | } 61 | 62 | type EventEVMBalanceDeposit struct { 63 | Phase types.Phase 64 | AccountId types.AccountID 65 | H160 types.H160 66 | U256 types.U256 67 | Topics []types.Hash 68 | } 69 | type EventEVMExecutedFailed struct { 70 | Phase types.Phase 71 | H160 types.H160 72 | Topics []types.Hash 73 | } 74 | type EventEVMExecuted struct { 75 | Phase types.Phase 76 | H160 types.H160 77 | Topics []types.Hash 78 | } 79 | type EventEVMCreatedFailed struct { 80 | Phase types.Phase 81 | H160 types.H160 82 | Topics []types.Hash 83 | } 84 | type EventEVMCreated struct { 85 | Phase types.Phase 86 | H160 types.H160 87 | Topics []types.Hash 88 | } 89 | type EventEVMLog struct { 90 | Phase types.Phase 91 | Log base.Log 92 | Topics []types.Hash 93 | } 94 | -------------------------------------------------------------------------------- /expand/polkadot/types.go: -------------------------------------------------------------------------------- 1 | package polkadot 2 | 3 | import ( 4 | "github.com/JFJun/stafi-substrate-go/expand/base" 5 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 6 | ) 7 | 8 | type PolkadotEventRecords struct { 9 | types.EventRecords 10 | Claims_Claimed []EventClaimsClaimed 11 | ElectionsPhragmen_VoterReported []EventElectionsPhragmenVoterReported 12 | ElectionsPhragmen_MemberRenounced []EventElectionsPhragmenMemberRenounced 13 | ElectionsPhragmen_MemberKicked []EventElectionsPhragmenMemberKicked 14 | ElectionsPhragmen_ElectionError []EventElectionsPhragmenElectionError 15 | ElectionsPhragmen_EmptyTerm []EventElectionsPhragmenEmptyTerm 16 | //ElectionsPhragmen_NewTerm []EventElectionsPhragmenNewTerm 暂不支持解析 17 | Democracy_Blacklisted []EventDemocracyBlacklisted 18 | } 19 | 20 | func (p PolkadotEventRecords) GetBalancesTransfer() []types.EventBalancesTransfer { 21 | return p.Balances_Transfer 22 | } 23 | 24 | func (p PolkadotEventRecords) GetSystemExtrinsicSuccess() []types.EventSystemExtrinsicSuccess { 25 | return p.System_ExtrinsicSuccess 26 | } 27 | 28 | func (p PolkadotEventRecords) GetSystemExtrinsicFailed() []types.EventSystemExtrinsicFailed { 29 | return p.System_ExtrinsicFailed 30 | } 31 | 32 | type EventDemocracyBlacklisted struct { 33 | Phase types.Phase 34 | Hash types.Hash 35 | Topics []types.Hash 36 | } 37 | 38 | //type EventElectionsPhragmenNewTerm struct { 39 | // Phase types.Phase 40 | // Vec 41 | // Topics []types.Hash 42 | //} 43 | type EventElectionsPhragmenEmptyTerm struct { 44 | Phase types.Phase 45 | 46 | Topics []types.Hash 47 | } 48 | type EventElectionsPhragmenElectionError struct { 49 | Phase types.Phase 50 | Topics []types.Hash 51 | } 52 | type EventElectionsPhragmenMemberKicked struct { 53 | Phase types.Phase 54 | AccountId types.AccountID 55 | Topics []types.Hash 56 | } 57 | type EventElectionsPhragmenMemberRenounced struct { 58 | Phase types.Phase 59 | AccountId types.AccountID 60 | Topics []types.Hash 61 | } 62 | type EventElectionsPhragmenVoterReported struct { 63 | Phase types.Phase 64 | Who1 types.AccountID 65 | Who2 types.AccountID 66 | Bool types.Bool 67 | Topics []types.Hash 68 | } 69 | type EventClaimsClaimed struct { 70 | Phase types.Phase 71 | AccountId types.AccountID 72 | EthereumAddress base.VecU8L20 73 | Balance types.U128 74 | Topics []types.Hash 75 | } 76 | -------------------------------------------------------------------------------- /expand/prefix.go: -------------------------------------------------------------------------------- 1 | package expand 2 | 3 | import ( 4 | "github.com/JFJun/go-substrate-crypto/ss58" 5 | "strings" 6 | ) 7 | 8 | func GetPrefix(chainName string) []byte { 9 | switch strings.ToLower(chainName) { 10 | case "polkadot": 11 | return ss58.PolkadotPrefix 12 | case "chainx": 13 | return ss58.ChainXPrefix 14 | case "kusama": 15 | return ss58.KsmPrefix 16 | case "crab": 17 | return ss58.SubstratePrefix 18 | case "darwinia": 19 | return ss58.DarwiniaPrefix 20 | default: 21 | return ss58.SubstratePrefix 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /expand/rio/types.go: -------------------------------------------------------------------------------- 1 | package rio 2 | 3 | // 4 | //import ( 5 | // "fmt" 6 | // "github.com/JFJun/stafi-substrate-go/expand/base" 7 | // "github.com/stafiprotocol/go-substrate-rpc-client/scale" 8 | // "github.com/stafiprotocol/go-substrate-rpc-client/types" 9 | //) 10 | // 11 | ////todo 目前还无法对接这个币,许多的types官方包里面都没有说明 12 | // 13 | //type RioEventRecords struct { 14 | // base.BaseEventRecords 15 | // Oracle_NewFeedData []EventOracleNewFeedData 16 | // RioAssets_Transferred []EventRioAssetsTransferred 17 | // RioAssets_Created []EventRioAssetsCreated 18 | // RioAssets_UpdateAssetRestriction []EventRioAssetsUpdateAssetRestriction 19 | // RioAssets_Revoke []EventRioAssetsRevoke 20 | // RioAssetsExt_Holder []EventRioAssetsExtHolder 21 | // RioPaymentFee_AccountChanged []EventRioPaymentFeeAccountChanged 22 | // RioPaymentFee_FeeDeposit []EventRioPaymentFeeFeeDeposit 23 | // RioGateway_AuthChanged []EventRioGatewayAuthChanged 24 | // RioGateway_SupportedAssetAdded []EventRioGatewaySupportedAssetAdded 25 | // RioGateway_SupportedAssetRemoved []EventRioGatewaySupportedAssetRemoved 26 | // RioGateway_WithdrawaFeeSetted []EventRioGatewayWithdrawaFeeSetted 27 | // RioGateway_NewDepositAddrInfoOfAssetId []EventRioGatewayNewDepositAddrInfoOfAssetId 28 | // RioGateway_NewDepositIndex []EventRioGatewayNewDepositIndex 29 | // RioGateway_MaxDepositCountSetted []EventRioGatewayMaxDepositCountSetted 30 | // RioGateway_NewDepositRecord []EventRioGatewayNewDepositRecord 31 | // RioGateway_NewPendingWithdrawRecord []EventRioGatewayNewPendingWithdrawRecord 32 | // RioGateway_WithdrawRebroadcasted []EventRioGatewayWithdrawRebroadcasted 33 | // RioGateway_WithdrawStatusChanged []EventRioGatewayWithdrawStatusChanged 34 | // RioGateway_UnsafeSetWithdrawState []EventRioGatewayUnsafeSetWithdrawState 35 | // RioGateway_UnsafeRemoveWithdrawRecord []EventRioGatewayUnsafeRemoveWithdrawRecord 36 | // RioPrices_LockPrice []EventRioPricesLockPrice 37 | // RioPrices_UnlockPrice []EventRioPricesUnlockPrice 38 | // RioRoot_ModifyManager []EventRioRootModifyManager 39 | // RioRoot_LockedRFuelIssued []EventRioRootLockedRFuelIssued 40 | //} 41 | // 42 | // 43 | // 44 | // 45 | // 46 | //type EventOracleNewFeedData struct { 47 | // Phase types.Phase 48 | // Who types.AccountID 49 | // Value []struct{ 50 | // OracleKey OracleKey 51 | // OracleValue OracleValue 52 | // } 53 | // Topics []types.Hash 54 | //} 55 | //type EventRioAssetsTransferred struct { 56 | // Phase types.Phase 57 | // CurrencyId base.CurrencyId 58 | // From types.AccountID 59 | // To types.AccountID 60 | // Balance types.U128 61 | // Topics []types.Hash 62 | //} 63 | // 64 | //type EventRioAssetsCreated struct { 65 | // Phase types.Phase 66 | // CurrencyId base.CurrencyId 67 | // Topics []types.Hash 68 | //} 69 | // 70 | //type EventRioAssetsUpdateAssetRestriction struct { 71 | // Phase types.Phase 72 | // CurrencyId base.CurrencyId 73 | // Restrictions Restrictions 74 | // Topics []types.Hash 75 | //} 76 | //type EventRioAssetsRevoke struct { 77 | // Phase types.Phase 78 | // CurrencyId base.CurrencyId 79 | // Topics []types.Hash 80 | //} 81 | // 82 | //type EventRioAssetsExtHolder struct { 83 | // Phase types.Phase 84 | // Who types.AccountID 85 | // Topics []types.Hash 86 | //} 87 | //type EventRioPaymentFeeAccountChanged struct { 88 | // Phase types.Phase 89 | // Who types.AccountID 90 | // Topics []types.Hash 91 | //} 92 | // 93 | //type EventRioPaymentFeeFeeDeposit struct { 94 | // Phase types.Phase 95 | // Balance types.U128 96 | // Topics []types.Hash 97 | //} 98 | // 99 | //type EventRioGatewayAuthChanged struct { 100 | // Phase types.Phase 101 | // Who types.AccountID 102 | // Auths Auths 103 | // Topics []types.Hash 104 | //} 105 | // 106 | //type EventRioGatewaySupportedAssetAdded struct { 107 | // Phase types.Phase 108 | // Who types.AccountID 109 | // CurrencyId base.CurrencyId 110 | // Balance types.U128 111 | // Topics []types.Hash 112 | //} 113 | //type EventRioGatewaySupportedAssetRemoved struct { 114 | // Phase types.Phase 115 | // Who types.AccountID 116 | // CurrencyId base.CurrencyId 117 | // Topics []types.Hash 118 | //} 119 | // 120 | //type EventRioGatewayWithdrawaFeeSetted struct { 121 | // Phase types.Phase 122 | // Who types.AccountID 123 | // CurrencyId base.CurrencyId 124 | // Balance types.U128 125 | // Topics []types.Hash 126 | //} 127 | // 128 | //type EventRioGatewayNewDepositAddrInfoOfAssetId struct { 129 | // Phase types.Phase 130 | // CurrencyId base.CurrencyId 131 | // DepositAddrInfo DepositAddrInfo 132 | // Topics []types.Hash 133 | //} 134 | //type EventRioGatewayNewDepositIndex struct { 135 | // Phase types.Phase 136 | // Who types.AccountID 137 | // U64 types.U64 138 | // Topics []types.Hash 139 | //} 140 | //type EventRioGatewayMaxDepositCountSetted struct { 141 | // Phase types.Phase 142 | // U64 types.U64 143 | // Topics []types.Hash 144 | //} 145 | // 146 | //type EventRioGatewayNewDepositRecord struct { 147 | // Phase types.Phase 148 | // CurrencyId base.CurrencyId 149 | // Who types.AccountID 150 | // Balance types.U128 151 | // TxHash TxHash 152 | // Topics []types.Hash 153 | //} 154 | //type EventRioGatewayNewPendingWithdrawRecord struct { 155 | // Phase types.Phase 156 | // U64 types.U64 157 | // CurrencyId base.CurrencyId 158 | // Who types.AccountID 159 | // Balance types.Bytes128 160 | // Topics []types.Hash 161 | //} 162 | // 163 | //type EventRioGatewayWithdrawRebroadcasted struct { 164 | // Phase types.Phase 165 | // U64 types.U64 166 | // Who types.AccountID 167 | // WithdrawState WithdrawState 168 | // Topics []types.Hash 169 | //} 170 | // 171 | //type EventRioGatewayWithdrawStatusChanged struct { 172 | // Phase types.Phase 173 | // U64 types.U64 174 | // Who types.AccountID 175 | // WithdrawState1 WithdrawState 176 | // WithdrawState2 WithdrawState 177 | // 178 | // Topics []types.Hash 179 | //} 180 | //type EventRioGatewayUnsafeSetWithdrawState struct { 181 | // Phase types.Phase 182 | // U64 types.U64 183 | // WithdrawState WithdrawState 184 | // Topics []types.Hash 185 | //} 186 | // 187 | //type EventRioGatewayUnsafeRemoveWithdrawRecord struct { 188 | // Phase types.Phase 189 | // U64 types.U64 190 | // Topics []types.Hash 191 | //} 192 | // 193 | //type EventRioPricesLockPrice struct { 194 | // Phase types.Phase 195 | // CurrencyId base.CurrencyId 196 | // Price Price 197 | // Topics []types.Hash 198 | //} 199 | // 200 | //type EventRioPricesUnlockPrice struct { 201 | // Phase types.Phase 202 | // CurrencyId base.CurrencyId 203 | // Topics []types.Hash 204 | //} 205 | // 206 | //type EventRioRootModifyManager struct { 207 | // Phase types.Phase 208 | // Who types.AccountID 209 | // Bool bool 210 | // Topics []types.Hash 211 | //} 212 | //type EventRioRootLockedRFuelIssued struct { 213 | // Phase types.Phase 214 | // Who types.AccountID 215 | // Balance types.U128 216 | // Topics []types.Hash 217 | //} 218 | ////--------------------------------- 219 | // 220 | // 221 | ///* 222 | //https://github.com/RioDefi/riochain 223 | //*/ 224 | //type Auths struct { 225 | // Value string 226 | //} 227 | // 228 | //func (d *Auths)Decode(decoder scale.Decoder)error{ 229 | // b,err := decoder.ReadOneByte() 230 | // if err != nil { 231 | // return err 232 | // } 233 | // if b==0 { 234 | // d.Value = "All" 235 | // return nil 236 | // } 237 | // if b==1 { 238 | // d.Value = "Deposit" 239 | // return nil 240 | // } 241 | // if b==2 { 242 | // d.Value = "Withdraw" 243 | // return nil 244 | // } 245 | // if b==3 { 246 | // d.Value = "Refund" 247 | // return nil 248 | // } 249 | // if b==4 { 250 | // d.Value = "Mark" 251 | // return nil 252 | // } 253 | // return fmt.Errorf("unknown type index %d",b) 254 | //} 255 | // 256 | //type TxHash types.H256 257 | //type Price types.U128 258 | -------------------------------------------------------------------------------- /expand/serde.go: -------------------------------------------------------------------------------- 1 | package expand 2 | 3 | import ( 4 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 5 | "sync" 6 | ) 7 | 8 | type SerdeOptions struct { 9 | SerDe types.SerDeOptions 10 | } 11 | 12 | var defaultSerDeOptions = SerdeOptions{} 13 | 14 | var mu sync.RWMutex 15 | 16 | // SetSerDeOptions overrides default serialise and deserialize options 17 | func SetSerDeOptions(noPalletIndices bool) { 18 | defer mu.Unlock() 19 | mu.Lock() 20 | tsdo := types.SerDeOptions{ 21 | NoPalletIndices: noPalletIndices, 22 | } 23 | types.SetSerDeOptions(tsdo) 24 | defaultSerDeOptions = SerdeOptions{ 25 | SerDe: tsdo, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /expand/stafi/types.go: -------------------------------------------------------------------------------- 1 | package stafi 2 | 3 | import ( 4 | "fmt" 5 | "github.com/JFJun/stafi-substrate-go/expand/base" 6 | "github.com/stafiprotocol/go-substrate-rpc-client/scale" 7 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 8 | ) 9 | 10 | type StafiEventRecords struct { 11 | base.BaseEventRecords 12 | BridgeCommon_ChainWhitelisted []EventBridgeCommonChainWhitelisted 13 | BridgeCommon_FungibleTransfer []EventBridgeCommonFungibleTransfer 14 | BridgeCommon_ChainFeesSet []EventBridgeCommonChainFeesSet 15 | RBalances_Transfer []EventRBalancesTransfer 16 | RBalances_Minted []EventRBalancesMinted 17 | RBalances_Burned []EventRBalancesBurned 18 | RTokenRate_RateSet []EventRTokenRateRateSet 19 | RFis_NewPool []EventRFisNewPool 20 | RFis_CommissionUpdated []EventRFisCommissionUpdated 21 | RFis_MaxValidatorCommissionUpdated []EventRFisMaxValidatorCommissionUpdated 22 | RFis_PoolBalanceLimitUpdated []EventRFisPoolBalanceLimitUpdated 23 | RFis_LiquidityBond []EventRFisLiquidityBond 24 | RFis_LiquidityUnBond []EventRFisLiquidityUnBond 25 | RFis_LiquidityWithdrawUnBond []EventRFisLiquidityWithdrawUnBond 26 | RFis_ValidatorOnboard []EventRFisValidatorOnboard 27 | RFis_ValidatorOffboard []EventRFisValidatorOffboard 28 | RFis_TotalBondedBeforePayout []EventRFisTotalBondedBeforePayout 29 | RFis_TotalBondedAfterPayout []EventRFisTotalBondedAfterPayout 30 | RFis_NominateSwitchToggle []EventRFisNominateSwitchToggle 31 | RFis_MinNominationNumSet []EventRFisMinNominationNumSet 32 | RFis_MaxNominationNumSet []EventRFisMaxNominationNumSet 33 | RFis_NominationUpdated []EventRFisNominationUpdated 34 | BridgeCommon_RelayerThresholdChanged []EventBridgeCommonRelayerThresholdChanged 35 | BridgeCommon_RelayerAdded []EventBridgeCommonRelayerAdded 36 | BridgeCommon_RelayerRemoved []EventBridgeCommonRelayerRemoved 37 | BridgeCommon_ChainRemoved []EventBridgeCommonChainRemoved 38 | BridgeCommon_VoteFor []EventBridgeCommonVoteFor 39 | BridgeCommon_VoteAgainst []EventBridgeCommonVoteAgainst 40 | BridgeCommon_ProposalPassed []EventBridgeCommonProposalPassed 41 | BridgeCommon_ProposalCancelled []EventBridgeCommonProposalCancelled 42 | BridgeCommon_ProposalExecuted []EventBridgeCommonProposalExecuted 43 | RFis_ValidatorPaidout []EventRFisValidatorPaidout 44 | } 45 | type EventRFisValidatorPaidout struct { 46 | Phase types.Phase 47 | EraIndex EraIndex 48 | AccountId1 types.AccountID 49 | AccountId2 types.AccountID 50 | Bool types.Bool 51 | Topics []types.Hash 52 | } 53 | type EventBridgeCommonProposalExecuted struct { 54 | Phase types.Phase 55 | ChainId ChainId 56 | DepositNonce DepositNonce 57 | Topics []types.Hash 58 | } 59 | type EventBridgeCommonProposalCancelled struct { 60 | Phase types.Phase 61 | ChainId ChainId 62 | DepositNonce DepositNonce 63 | Topics []types.Hash 64 | } 65 | type EventBridgeCommonProposalPassed struct { 66 | Phase types.Phase 67 | ChainId ChainId 68 | DepositNonce DepositNonce 69 | Topics []types.Hash 70 | } 71 | type EventBridgeCommonVoteAgainst struct { 72 | Phase types.Phase 73 | ChainId ChainId 74 | DepositNonce DepositNonce 75 | AccountId types.AccountID 76 | Topics []types.Hash 77 | } 78 | type EventBridgeCommonVoteFor struct { 79 | Phase types.Phase 80 | ChainId ChainId 81 | DepositNonce DepositNonce 82 | AccountId types.AccountID 83 | Topics []types.Hash 84 | } 85 | type EventBridgeCommonChainRemoved struct { 86 | Phase types.Phase 87 | ChainId ChainId 88 | Topics []types.Hash 89 | } 90 | type EventBridgeCommonRelayerRemoved struct { 91 | Phase types.Phase 92 | AccountId types.AccountID 93 | Topics []types.Hash 94 | } 95 | type EventBridgeCommonRelayerAdded struct { 96 | Phase types.Phase 97 | AccountId types.AccountID 98 | Topics []types.Hash 99 | } 100 | type EventBridgeCommonRelayerThresholdChanged struct { 101 | Phase types.Phase 102 | U32 types.U32 103 | Topics []types.Hash 104 | } 105 | type EventRFisNominationUpdated struct { 106 | Phase types.Phase 107 | EraIndex EraIndex 108 | AccountIds []types.AccountID 109 | AccountId1 types.AccountID 110 | Topics []types.Hash 111 | } 112 | type EventRFisMaxNominationNumSet struct { 113 | Phase types.Phase 114 | U8 types.U8 115 | Topics []types.Hash 116 | } 117 | type EventRFisMinNominationNumSet struct { 118 | Phase types.Phase 119 | U8 types.U8 120 | Topics []types.Hash 121 | } 122 | type EventRFisNominateSwitchToggle struct { 123 | Phase types.Phase 124 | Bool types.Bool 125 | Topics []types.Hash 126 | } 127 | type EventRFisTotalBondedAfterPayout struct { 128 | Phase types.Phase 129 | EraIndex EraIndex 130 | Balance types.U128 131 | Topics []types.Hash 132 | } 133 | type EventRFisTotalBondedBeforePayout struct { 134 | Phase types.Phase 135 | EraIndex EraIndex 136 | Balance types.U128 137 | Topics []types.Hash 138 | } 139 | type EventRFisValidatorOffboard struct { 140 | Phase types.Phase 141 | AccountId1 types.AccountID 142 | AccountId2 types.AccountID 143 | 144 | Topics []types.Hash 145 | } 146 | type EventRFisValidatorOnboard struct { 147 | Phase types.Phase 148 | AccountId1 types.AccountID 149 | AccountId2 types.AccountID 150 | Topics []types.Hash 151 | } 152 | type EventRFisLiquidityWithdrawUnBond struct { 153 | Phase types.Phase 154 | AccountId1 types.AccountID 155 | AccountId2 types.AccountID 156 | Balance types.U128 157 | Topics []types.Hash 158 | } 159 | type EventRFisLiquidityUnBond struct { 160 | Phase types.Phase 161 | AccountId1 types.AccountID 162 | AccountId2 types.AccountID 163 | U1281 types.U128 164 | U1282 types.U128 165 | Balance1 types.U128 166 | Topics []types.Hash 167 | } 168 | type EventRFisLiquidityBond struct { 169 | Phase types.Phase 170 | AccountId1 types.AccountID 171 | AccountId2 types.AccountID 172 | Balance1 types.U128 173 | U128 types.U128 174 | Topics []types.Hash 175 | } 176 | type EventRFisPoolBalanceLimitUpdated struct { 177 | Phase types.Phase 178 | Balance1 types.U128 179 | Balance2 types.U128 180 | Topics []types.Hash 181 | } 182 | type EventRFisMaxValidatorCommissionUpdated struct { 183 | Phase types.Phase 184 | Perbill1 base.Perbill 185 | Perbill2 base.Perbill 186 | Topics []types.Hash 187 | } 188 | type EventRFisCommissionUpdated struct { 189 | Phase types.Phase 190 | Perbill1 base.Perbill 191 | Perbill2 base.Perbill 192 | Topics []types.Hash 193 | } 194 | type EventRFisNewPool struct { 195 | Phase types.Phase 196 | VecU8 types.Bytes 197 | AccountId types.AccountID 198 | Topics []types.Hash 199 | } 200 | type EventRTokenRateRateSet struct { 201 | Phase types.Phase 202 | RateType RateType 203 | Topics []types.Hash 204 | } 205 | type EventRBalancesBurned struct { 206 | Phase types.Phase 207 | AccountId types.AccountID 208 | RSymbol RSymbol 209 | U128 types.U128 210 | Topics []types.Hash 211 | } 212 | type EventRBalancesMinted struct { 213 | Phase types.Phase 214 | AccountId types.AccountID 215 | RSymbol RSymbol 216 | U128 types.U128 217 | Topics []types.Hash 218 | } 219 | type EventRBalancesTransfer struct { 220 | Phase types.Phase 221 | AccountId1 types.AccountID 222 | AccountId2 types.AccountID 223 | RSymbol RSymbol 224 | U128 types.U128 225 | Topics []types.Hash 226 | } 227 | type EventBridgeCommonChainFeesSet struct { 228 | Phase types.Phase 229 | ChainId ChainId 230 | Balance types.U128 231 | Topics []types.Hash 232 | } 233 | 234 | type EventBridgeCommonChainWhitelisted struct { 235 | Phase types.Phase 236 | ChainId ChainId 237 | Topics []types.Hash 238 | } 239 | type EventBridgeCommonFungibleTransfer struct { 240 | Phase types.Phase 241 | Who types.AccountID 242 | ChainId ChainId 243 | DepositNonce DepositNonce 244 | ResourceId base.VecU8L32 245 | U256 types.U256 246 | Data types.Bytes 247 | Topics []types.Hash 248 | } 249 | 250 | type ChainId types.U8 251 | type DepositNonce types.U64 252 | type RateType types.U64 253 | type EraIndex types.U32 254 | 255 | /* 256 | https://github.com/stafiprotocol/stafi-bootstrap/blob/master/types.json 257 | */ 258 | type RSymbol struct { 259 | Value string 260 | } 261 | 262 | func (d *RSymbol) Decode(decoder scale.Decoder) error { 263 | b, err := decoder.ReadOneByte() 264 | if err != nil { 265 | return err 266 | } 267 | if b == 0 { 268 | d.Value = "RFIS" 269 | } 270 | return fmt.Errorf("unKnow enum index: %d", b) 271 | } 272 | -------------------------------------------------------------------------------- /expand/types.go: -------------------------------------------------------------------------------- 1 | package expand 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "fmt" 8 | "github.com/JFJun/stafi-substrate-go/uint128" 9 | "github.com/JFJun/stafi-substrate-go/utils" 10 | "github.com/huandu/xstrings" 11 | "github.com/shopspring/decimal" 12 | "github.com/stafiprotocol/go-substrate-rpc-client/scale" 13 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 14 | "io" 15 | "reflect" 16 | ) 17 | 18 | //type Compact struct { 19 | // CompactLength int `json:"compact_length"` 20 | // CompactBytes []byte `json:"compact_bytes"` 21 | //} 22 | ///* 23 | //subType must be ptr 24 | //*/ 25 | //func (c *Compact) ProcessCompactBytes(decoder scale.Decoder,subType interface{}) error { 26 | // if subType==nil { 27 | // return errors.New("decode compact : sub type is nil") 28 | // } 29 | // b,err := decoder.ReadOneByte() 30 | // if err != nil { 31 | // return fmt.Errorf("decode compact : read first one bytes error: %v",err) 32 | // } 33 | // var byteMod = 0 34 | // byteMod = int(b) % 4 35 | // if byteMod == 0 { 36 | // c.CompactLength = 1 37 | // } else if byteMod == 1 { 38 | // c.CompactLength = 2 39 | // } else if byteMod == 2 { 40 | // c.CompactLength = 4 41 | // } else { 42 | // c.CompactLength = 5 + ((int(b) - 3) / 4) 43 | // } 44 | // cb:=[]byte{b} 45 | // if c.CompactLength == 1 { 46 | // c.CompactBytes = cb 47 | // } else if utils.IntInSlice(c.CompactLength, []int{2, 4}) { 48 | // nb:=make([]byte,c.CompactLength-1) 49 | // err = decoder.Read(nb) 50 | // if err != nil { 51 | // return fmt.Errorf("decode compact: read bytes error: %v",err) 52 | // } 53 | // c.CompactBytes = append(cb[:], nb...) 54 | // } else { 55 | // nb:=make([]byte,c.CompactLength-1) 56 | // err = decoder.Read(nb) 57 | // c.CompactBytes = nb 58 | // } 59 | // if c.CompactLength<=4 { 60 | // switch v:=by { 61 | // 62 | // } 63 | // } 64 | // newDecoder:=scale.NewDecoder(bytes.NewReader(c.CompactBytes)) 65 | // return newDecoder.Decode(subType) 66 | //} 67 | 68 | type Balance struct { 69 | Reader io.Reader 70 | Value decimal.Decimal 71 | } 72 | 73 | func (b *Balance) Decode(decoder scale.Decoder) error { 74 | buf := &bytes.Buffer{} 75 | b.Reader = buf 76 | data := make([]byte, 16) 77 | err := decoder.Read(data) 78 | if err != nil { 79 | return fmt.Errorf("decode balance: read bytes error: %v", err) 80 | } 81 | buf.Write(data) 82 | c := make([]byte, 16) 83 | if utils.BytesToHex(c) == "ffffffffffffffffffffffffffffffff" { 84 | b.Value = decimal.Zero 85 | return nil 86 | } 87 | 88 | b.Value = decimal.NewFromBigInt(uint128.FromBytes(c).Big(), 0) 89 | return nil 90 | } 91 | 92 | type Vec struct { 93 | Value []interface{} 94 | } 95 | 96 | /* 97 | sub type must be struct,not ptr 98 | */ 99 | func (v *Vec) ProcessVec(decoder scale.Decoder, subType interface{}) error { 100 | var u types.UCompact 101 | err := decoder.Decode(&u) 102 | if err != nil { 103 | return fmt.Errorf("decode Vec: get length error: %v", err) 104 | } 105 | length := int(utils.UCompactToBigInt(u).Int64()) 106 | if length > 5000 { 107 | return fmt.Errorf("vec length %d exceeds %d", length, 1000) 108 | } 109 | for i := 0; i < length; i++ { 110 | st := reflect.TypeOf(subType) 111 | if st.Kind() != reflect.Struct { 112 | return errors.New("decode Vec: struct type is not struct") 113 | } 114 | tmp := reflect.New(st) 115 | subType := tmp.Interface() 116 | err = decoder.Decode(subType) 117 | if err != nil { 118 | return fmt.Errorf("decode Vec: decoder subtype error: %v", err) 119 | } 120 | v.Value = append(v.Value, subType) 121 | } 122 | return nil 123 | } 124 | 125 | /* 126 | 解码包的问题,所以这里只能根据需求写死 127 | */ 128 | type TransferCall struct { 129 | Value interface{} 130 | } 131 | 132 | func (t *TransferCall) Decode(decoder scale.Decoder) error { 133 | //1. 先获取callidx 134 | b := make([]byte, 2) 135 | err := decoder.Read(b) 136 | if err != nil { 137 | return fmt.Errorf("deode transfer call: read callIdx bytes error: %v", err) 138 | } 139 | callIdx := xstrings.RightJustify(utils.IntToHex(b[0]), 2, "0") + xstrings.RightJustify(utils.IntToHex(b[1]), 2, "0") 140 | result := map[string]interface{}{ 141 | "call_index": callIdx, 142 | } 143 | var param []ExtrinsicParam 144 | // 0 ---> Address 145 | var address Address 146 | err = decoder.Decode(&address) 147 | if err != nil { 148 | return fmt.Errorf("decode call: decode Balances.transfer.Address error: %v", err) 149 | } 150 | param = append(param, 151 | ExtrinsicParam{ 152 | Name: "dest", 153 | Type: "Address", 154 | Value: address.Value, 155 | ValueRaw: address.Value, 156 | }) 157 | // 1 ----> Compact 158 | var bb types.UCompact 159 | 160 | err = decoder.Decode(&bb) 161 | if err != nil { 162 | return fmt.Errorf("decode call: decode Balances.transfer.Compact error: %v", err) 163 | } 164 | v := utils.UCompactToBigInt(bb).Int64() 165 | param = append(param, 166 | ExtrinsicParam{ 167 | Name: "value", 168 | Type: "Compact", 169 | Value: v, 170 | }) 171 | result["call_args"] = param 172 | t.Value = result 173 | return nil 174 | } 175 | 176 | type Address struct { 177 | AccountLength string `json:"account_length"` 178 | Value string 179 | } 180 | 181 | func (a *Address) Decode(decoder scale.Decoder) error { 182 | al, err := decoder.ReadOneByte() 183 | if err != nil { 184 | return fmt.Errorf("decode address: get account length error: %v", err) 185 | } 186 | a.AccountLength = utils.BytesToHex([]byte{al}) 187 | if a.AccountLength == "ff" && defaultSerDeOptions.SerDe.NoPalletIndices == false { 188 | data := make([]byte, 32) 189 | err = decoder.Read(data) 190 | if err != nil { 191 | return fmt.Errorf("decode address: get address 32 bytes error: %v", err) 192 | } 193 | a.Value = utils.BytesToHex(data) 194 | return nil 195 | } 196 | d := make([]byte, 31) 197 | err = decoder.Read(d) 198 | if err != nil { 199 | return fmt.Errorf("decode address: get address 31 bytes error: %v", err) 200 | } 201 | a.Value = utils.BytesToHex(append([]byte{al}, d...)) 202 | return nil 203 | } 204 | 205 | // 206 | type U32 struct { 207 | Value uint32 208 | } 209 | 210 | func (u *U32) Decode(decoder scale.Decoder) error { 211 | data := make([]byte, 4) 212 | err := decoder.Read(data) 213 | if err != nil { 214 | return fmt.Errorf("decode u32 : read 4 bytes error: %v", err) 215 | } 216 | u.Value = binary.LittleEndian.Uint32(data) 217 | return nil 218 | } 219 | 220 | // AccountInfo contains information of an account 221 | type StafiAccountInfo struct { 222 | Nonce types.U32 223 | RefCount types.U8 224 | Data struct { 225 | Free types.U128 226 | Reserved types.U128 227 | MiscFrozen types.U128 228 | FreeFrozen types.U128 229 | } 230 | } 231 | 232 | type WeightToFeeCoefficient struct { 233 | CoeffInteger types.U128 234 | CoeffFrac U32 235 | Negative bool 236 | Degree types.U8 237 | } 238 | 239 | func (d *WeightToFeeCoefficient) Decode(decoder scale.Decoder) error { 240 | err := decoder.Decode(&d.CoeffInteger) 241 | if err != nil { 242 | return fmt.Errorf("decode WeightToFeeCoefficient: decode CoeffInteger error: %v", err) 243 | } 244 | err = decoder.Decode(&d.CoeffFrac) 245 | if err != nil { 246 | return fmt.Errorf("decode WeightToFeeCoefficient: decode CoeffFrac error: %v", err) 247 | } 248 | err = decoder.Decode(&d.Negative) 249 | if err != nil { 250 | return fmt.Errorf("decode WeightToFeeCoefficient: decode Negative error: %v", err) 251 | } 252 | err = decoder.Decode(&d.Degree) 253 | if err != nil { 254 | return fmt.Errorf("decode WeightToFeeCoefficient: decode Degree error: %v", err) 255 | } 256 | return nil 257 | } 258 | 259 | /* 260 | https://github.com/polkadot-js/api/blob/0e52e8fe23ed029b8101cf8bf82deccd5aa7a790/packages/types/src/generic/MultiAddress.ts 261 | */ 262 | 263 | type MultiAddress struct { 264 | GenericMultiAddress 265 | } 266 | type GenericMultiAddress struct { 267 | AccountId types.AccountID 268 | Index types.UCompact 269 | Raw types.Bytes 270 | Address32 types.H256 271 | Address20 types.H160 272 | types int 273 | } 274 | 275 | func (d *GenericMultiAddress) Decode(decoder scale.Decoder) error { 276 | b, err := decoder.ReadOneByte() 277 | if err != nil { 278 | return fmt.Errorf("generic MultiAddress read on bytes error: %v", err) 279 | } 280 | switch int(b) { 281 | case 0: 282 | err = decoder.Decode(&d.AccountId) 283 | case 1: 284 | err = decoder.Decode(&d.Index) 285 | case 2: 286 | err = decoder.Decode(&d.Address32) 287 | case 3: 288 | err = decoder.Decode(&d.Address20) 289 | default: 290 | err = fmt.Errorf("generic MultiAddress unsupport type=%d ", b) 291 | } 292 | if err != nil { 293 | return err 294 | } 295 | d.types = int(b) 296 | return nil 297 | } 298 | 299 | func (d GenericMultiAddress) Encode(encoder scale.Encoder) error { 300 | t := types.NewU8(uint8(d.types)) 301 | err := encoder.Encode(t) 302 | if err != nil { 303 | return err 304 | } 305 | switch d.types { 306 | case 0: 307 | if &d.AccountId == nil { 308 | err = fmt.Errorf("generic MultiAddress id is null:%v", d.AccountId) 309 | } 310 | err = encoder.Encode(d.AccountId) 311 | case 1: 312 | if &d.Index == nil { 313 | err = fmt.Errorf("generic MultiAddress index is null:%v", d.Index) 314 | } 315 | err = encoder.Encode(d.Index) 316 | case 2: 317 | if &d.Address32 == nil { 318 | err = fmt.Errorf("generic MultiAddress address32 is null:%v", d.Address32) 319 | } 320 | err = encoder.Encode(d.Address32) 321 | case 3: 322 | if &d.Address20 == nil { 323 | err = fmt.Errorf("generic MultiAddress address20 is null:%v", d.Address20) 324 | } 325 | err = encoder.Encode(d.Address20) 326 | default: 327 | err = fmt.Errorf("generic MultiAddress unsupport this types: %d", d.types) 328 | } 329 | if err != nil { 330 | return err 331 | } 332 | return nil 333 | } 334 | 335 | /* 336 | 没办法,底层解析就是这样做的,只能这样写了,虽然很不友好 337 | */ 338 | func (d *GenericMultiAddress) GetTypes() int { 339 | return d.types 340 | } 341 | func (d *GenericMultiAddress) SetTypes(types int) { 342 | d.types = types 343 | } 344 | func (d *GenericMultiAddress) GetAccountId() types.AccountID { 345 | return d.AccountId 346 | } 347 | func (d *GenericMultiAddress) GetIndex() types.UCompact { 348 | return d.Index 349 | } 350 | func (d *GenericMultiAddress) GetAddress32() types.H256 { 351 | return d.Address32 352 | } 353 | func (d *GenericMultiAddress) GetAddress20() types.H160 { 354 | return d.Address20 355 | } 356 | 357 | func (d *GenericMultiAddress) ToAddress() types.Address { 358 | if d.types != 0 { 359 | return types.Address{} 360 | } 361 | var ai []byte 362 | ai = append([]byte{0x00}, d.AccountId[:]...) 363 | return types.NewAddressFromAccountID(ai) 364 | } 365 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/JFJun/stafi-substrate-go 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/JFJun/go-substrate-crypto v1.0.1 7 | github.com/huandu/xstrings v1.3.2 8 | github.com/shopspring/decimal v1.2.0 9 | github.com/stafiprotocol/go-substrate-rpc-client v1.0.3 10 | golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a 11 | ) 12 | -------------------------------------------------------------------------------- /models/chain.go: -------------------------------------------------------------------------------- 1 | package models 2 | -------------------------------------------------------------------------------- /models/model.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type Bytes []byte 4 | type SignedBlock struct { 5 | Block Block `json:"block"` 6 | Justification Bytes `json:"justification"` 7 | } 8 | 9 | type Block struct { 10 | Extrinsics []string `json:"extrinsics"` 11 | Header Header `json:"header"` 12 | } 13 | 14 | type Header struct { 15 | ParentHash string `json:"parentHash"` 16 | Number string `json:"number"` 17 | StateRoot string `json:"stateRoot"` 18 | ExtrinsicsRoot string `json:"extrinsicsRoot"` 19 | Digest interface{} `json:"digest"` 20 | } 21 | 22 | type BlockResponse struct { 23 | Height int64 `json:"height"` 24 | ParentHash string `json:"parent_hash"` 25 | BlockHash string `json:"block_hash"` 26 | Timestamp int64 `json:"timestamp"` 27 | Extrinsic []*ExtrinsicResponse `json:"extrinsic"` 28 | } 29 | 30 | type ExtrinsicResponse struct { 31 | Type string `json:"type"` //Transfer or another 32 | Status string `json:"status"` //success or fail 33 | Txid string `json:"txid"` 34 | FromAddress string `json:"from_address"` 35 | ToAddress string `json:"to_address"` 36 | Amount string `json:"amount"` 37 | Fee string `json:"fee"` 38 | Signature string `json:"signature"` 39 | Nonce int64 `json:"nonce"` 40 | Era string `json:"era"` 41 | ExtrinsicIndex int `json:"extrinsic_index"` 42 | EventIndex int `json:"event_index"` 43 | ExtrinsicLength int `json:"extrinsic_length"` 44 | } 45 | 46 | type EventResult struct { 47 | From string `json:"from"` 48 | To string `json:"to"` 49 | Amount string `json:"amount"` 50 | ExtrinsicIdx int `json:"extrinsic_idx"` 51 | EventIdx int `json:"event_idx"` 52 | Status string `json:"status"` 53 | Weight int64 `json:"weight"` //权重 54 | } 55 | 56 | type ExtrinsicDecodeResponse struct { 57 | AccountId string `json:"account_id"` 58 | CallCode string `json:"call_code"` 59 | CallModule string `json:"call_module"` 60 | Era string `json:"era"` 61 | Nonce int64 `json:"nonce"` 62 | VersionInfo string `json:"version_info"` 63 | Signature string `json:"signature"` 64 | Params []ExtrinsicDecodeParam `json:"params"` 65 | CallModuleFunction string `json:"call_module_function"` 66 | Length int `json:"length"` 67 | } 68 | 69 | type ExtrinsicDecodeParam struct { 70 | Name string `json:"name"` 71 | Type string `json:"type"` 72 | Value interface{} `json:"value"` 73 | ValueRaw string `json:"value_raw"` 74 | } 75 | 76 | type UtilityParamsValue struct { 77 | CallModule string `json:"call_module"` 78 | CallFunction string `json:"call_function"` 79 | CallIndex string `json:"call_index"` 80 | CallArgs []UtilityParamsValueArg `json:"call_args"` 81 | } 82 | 83 | type UtilityParamsValueArg struct { 84 | Name string `json:"name"` 85 | Type string `json:"type"` 86 | Value interface{} `json:"value"` 87 | ValueRaw string `json:"value_raw"` 88 | } 89 | -------------------------------------------------------------------------------- /test/address_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/JFJun/go-substrate-crypto/ss58" 6 | "testing" 7 | ) 8 | 9 | func Test_Address(t *testing.T) { 10 | pub := "6e4f8120ba0ce9fa1d34bbb604860b6dee2873c0303a7aa6453902c6e9268462" 11 | address, err := ss58.EncodeByPubHex(pub, ss58.KsmPrefix) 12 | if err != nil { 13 | t.Fatal(err) 14 | } 15 | fmt.Println(address) 16 | } 17 | -------------------------------------------------------------------------------- /test/chainX_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "encoding/hex" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/JFJun/go-substrate-crypto/crypto" 8 | "github.com/JFJun/go-substrate-crypto/ss58" 9 | "github.com/JFJun/stafi-substrate-go/client" 10 | "github.com/JFJun/stafi-substrate-go/expand" 11 | "github.com/JFJun/stafi-substrate-go/models" 12 | "github.com/JFJun/stafi-substrate-go/tx" 13 | "testing" 14 | ) 15 | 16 | func Test_Chain_GetBlockByNumber(t *testing.T) { 17 | c, err := client.New("wss://rpc.polkadot.io") 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | //dd, _ := json.Marshal(c.Meta.AsMetadataV12.Modules) 22 | //fmt.Println(string(dd)) 23 | //fmt.Println(c.ChainName) 24 | for _, mod := range c.Meta.AsMetadataV12.Modules { 25 | if mod.HasEvents { 26 | for _, event := range mod.Events { 27 | fmt.Printf("%s_%s\n", mod.Name, event.Name) 28 | fmt.Printf("type Event%s%s struct { \n Phase types.Phase\n %v\n Topics []Hash\n}\n", mod.Name, event.Name, event.Args) 29 | //fmt.Println(event.Args) 30 | fmt.Println("------------------------------------------------") 31 | } 32 | } 33 | } 34 | //c.SetPrefix(ss58.SubstratePrefix) 35 | //block, err := c.GetBlockByNumber(60165) 36 | //if err != nil { 37 | // t.Fatal(err) 38 | //} 39 | //d, _ := json.Marshal(block) 40 | //fmt.Println(string(d)) 41 | } 42 | 43 | func Test_ChainXTX(t *testing.T) { 44 | from := "5Fq9MpKxdjzCWEHHtqZ6rdYkKUtW4qwmJV4VHwKBan2hxRyL" 45 | to := "5E2dFRZoSbXE4at8QjHPxfx8eWA9mvLFbH64ZE3wTAsEwVFu" 46 | nonce := uint64(0) 47 | amount := uint64(123456) 48 | c, err := client.New("wss://testnet-1.chainx.org/ws") 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | v, err := c.C.RPC.State.GetRuntimeVersionLatest() 53 | if err != nil { 54 | t.Fatal(err) 55 | } 56 | fmt.Println(v.TransactionVersion) 57 | fmt.Println(v.SpecVersion) 58 | //meta,err:=c.C.RPC.State.GetMetadataLatest() 59 | //if err != nil { 60 | // t.Fatal(err) 61 | //} 62 | //types.SerDeOptionsFromMetadata(meta) 63 | 64 | //types.SetSerDeOptions(types.SerDeOptions{NoPalletIndices: true}) 65 | transaction := tx.CreateTransaction(from, to, amount, nonce) 66 | transaction.SetGenesisHashAndBlockHash(c.GetGenesisHash(), 67 | c.GetGenesisHash()) 68 | ed, err := expand.NewMetadataExpand(c.Meta) 69 | if err != nil { 70 | t.Fatal(err) 71 | } 72 | callIdx, err := ed.MV.GetCallIndex("Balances", "transfer") 73 | if err != nil { 74 | t.Fatal(err) 75 | } 76 | fmt.Println(callIdx) 77 | transaction.SetSpecVersionAndCallId(uint32(v.SpecVersion), uint32(v.TransactionVersion), callIdx) 78 | tt, err := transaction.SignTransaction("", crypto.Sr25519Type) 79 | if err != nil { 80 | t.Fatal(err) 81 | } 82 | fmt.Println(tt) 83 | var result interface{} 84 | err = c.C.Client.Call(&result, "author_submitExtrinsic", tt) 85 | if err != nil { 86 | t.Fatal(err) 87 | } 88 | fmt.Println(result) 89 | d, _ := json.Marshal(result) 90 | fmt.Println(string(d)) 91 | } 92 | 93 | func Test_GetChainXBlock(t *testing.T) { 94 | c, err := client.New("wss://testnet-1.chainx.org/ws") 95 | if err != nil { 96 | t.Fatal(err) 97 | } 98 | var block models.SignedBlock 99 | err = c.C.Client.Call(&block, "chain_getBlock", "0x8a98b97126880b930b938d69e05fed10b170be06aa15f68318f2e9efd24e490d") 100 | if err != nil { 101 | t.Fatal(err) 102 | } 103 | d, _ := json.Marshal(block) 104 | fmt.Println(string(d)) 105 | h, err := c.C.RPC.Chain.GetFinalizedHead() 106 | fmt.Println(h.Hex()) 107 | //0x3d02 108 | //84 109 | //ff 64fe111943aa50763968bc260dc5a0eacfc0348e7f5eca8e98902749ecbb645c 110 | //01 763970418e96e1c64512f3e720afa73d35fef3f802b69f43001adf447a559f02 111 | // 38bbe79a2d0da20019650a503933f1a66d12dbb092524c4af71f70bd42a4a181 112 | // c502cc000603 113 | // ff56e2771f82b7845270c4f57990435dbacb44982a33ba1a202218a567ae39580d0300943577 114 | } 115 | 116 | func Test_CreateAddress(t *testing.T) { 117 | pub := "a69958eee5de0cb8fb250eba9c4b4ab1675468e68e49a5ebcac22fa9340fe938" 118 | pubBytes, _ := hex.DecodeString(pub) 119 | //pubBytes = append([]byte{0xff}, pubBytes...) 120 | address, err := ss58.Encode(pubBytes, ss58.SubstratePrefix) 121 | if err != nil { 122 | t.Fatal(err) 123 | } 124 | fmt.Println(address) 125 | //fmt.Println(base58.Decode("5UwyMPdTkRN2Va6DYWMJY3TYVcxgqgsUEN9Fsey8LR7hsgUn")) 126 | } 127 | 128 | func Test_GetChainXAccountInfo(t *testing.T) { 129 | c, err := client.New("wss://testnet-1.chainx.org/ws") 130 | if err != nil { 131 | t.Fatal(err) 132 | } 133 | acc, err := c.GetAccountInfo("5Fq9MpKxdjzCWEHHtqZ6rdYkKUtW4qwmJV4VHwKBan2hxRyL") 134 | if err != nil { 135 | t.Fatal(err) 136 | } 137 | fmt.Println(acc.Nonce) 138 | } 139 | -------------------------------------------------------------------------------- /test/chain_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/JFJun/stafi-substrate-go/client" 6 | "testing" 7 | ) 8 | 9 | func Test_Chain(t *testing.T) { 10 | c, err := client.New("http://ksm.rylink.io:30933") 11 | if err != nil { 12 | t.Fatal(err) 13 | } 14 | fmt.Println(c.ChainName) 15 | for _, mod := range c.Meta.AsMetadataV12.Modules { 16 | if mod.HasEvents { 17 | for _, event := range mod.Events { 18 | typeName := fmt.Sprintf("%s_%s", mod.Name, event.Name) 19 | if IsExist(typeName) { 20 | continue 21 | } 22 | fmt.Printf("%s []Event%s%s\n", typeName, mod.Name, event.Name) 23 | if len(event.Args) == 0 { 24 | fmt.Printf("type Event%s%s struct { \n Phase types.Phase\n \n Topics []types.Hash\n}\n", mod.Name, event.Name) 25 | } else { 26 | fmt.Printf("type Event%s%s struct { \n Phase types.Phase\n %v\n Topics []types.Hash\n}\n", mod.Name, event.Name, event.Args) 27 | } 28 | 29 | //fmt.Println(event.Args) 30 | fmt.Println("------------------------------------------------") 31 | } 32 | } 33 | } 34 | } 35 | 36 | var existTypes = []string{ 37 | "Balances_Endowed", 38 | "Balances_DustLost", 39 | "Balances_Transfer", 40 | "Balances_BalanceSet", 41 | "Balances_Deposit", 42 | "Balances_Reserved", 43 | "Balances_Unreserved", 44 | "Balances_ReservedRepatriated", 45 | "Grandpa_NewAuthorities", 46 | "Grandpa_Paused", 47 | "Grandpa_Resumed", 48 | "ImOnline_HeartbeatReceived", 49 | "ImOnline_AllGood", 50 | "ImOnline_SomeOffline", 51 | "Indices_IndexAssigned", 52 | "Indices_IndexFreed", 53 | "Indices_IndexFrozen", 54 | "Offences_Offence", 55 | "Session_NewSession", 56 | "Staking_EraPayout", 57 | "Staking_Reward", 58 | "Staking_Slash", 59 | "Staking_OldSlashingReportDiscarded", 60 | "Staking_StakingElection", 61 | "Staking_SolutionStored", 62 | "Staking_Bonded", 63 | "Staking_Unbonded", 64 | "Staking_Withdrawn", 65 | "System_ExtrinsicSuccess", 66 | "System_ExtrinsicFailed", 67 | "System_CodeUpdated", 68 | "System_NewAccount", 69 | "System_KilledAccount", 70 | "Assets_Issued", 71 | "Assets_Transferred", 72 | "Assets_Destroyed", 73 | "Democracy_Proposed", 74 | "Democracy_Tabled", 75 | "Democracy_ExternalTabled", 76 | "Democracy_Started", 77 | "Democracy_Passed", 78 | "Democracy_NotPassed", 79 | "Democracy_Cancelled", 80 | "Democracy_Executed", 81 | "Democracy_Delegated", 82 | "Democracy_Undelegated", 83 | "Democracy_Vetoed", 84 | "Democracy_PreimageNoted", 85 | "Democracy_PreimageUsed", 86 | "Democracy_PreimageInvalid", 87 | "Democracy_PreimageMissing", 88 | "Democracy_PreimageReaped", 89 | "Democracy_Unlocked", 90 | "Council_Proposed", 91 | "Council_Voted", 92 | "Council_Approved", 93 | "Council_Disapproved", 94 | "Council_Executed", 95 | "Council_MemberExecuted", 96 | "Council_Closed", 97 | "TechnicalCommittee_Proposed", 98 | "TechnicalCommittee_Voted", 99 | "TechnicalCommittee_Approved", 100 | "TechnicalCommittee_Disapproved", 101 | "TechnicalCommittee_Executed", 102 | "TechnicalCommittee_MemberExecuted", 103 | "TechnicalCommittee_Closed", 104 | "TechnicalMembership_MemberAdded", 105 | "TechnicalMembership_MemberRemoved", 106 | "TechnicalMembership_MembersSwapped", 107 | "TechnicalMembership_MembersReset", 108 | "TechnicalMembership_KeyChanged", 109 | "Elections_NewTerm", 110 | "Elections_EmptyTerm", 111 | "Elections_MemberKicked", 112 | "Elections_MemberRenounced", 113 | "Elections_VoterReported", 114 | "Identity_IdentitySet", 115 | "Identity_IdentityCleared", 116 | "Identity_IdentityKilled", 117 | "Identity_JudgementRequested", 118 | "Identity_JudgementUnrequested", 119 | "Identity_JudgementGiven", 120 | "Identity_RegistrarAdded", 121 | "Identity_SubIdentityAdded", 122 | "Identity_SubIdentityRemoved", 123 | "Identity_SubIdentityRevoked", 124 | "Society_Founded", 125 | "Society_Bid", 126 | "Society_Vouch", 127 | "Society_AutoUnbid", 128 | "Society_Unbid", 129 | "Society_Unvouch", 130 | "Society_Inducted", 131 | "Society_SuspendedMemberJudgement", 132 | "Society_CandidateSuspended", 133 | "Society_MemberSuspended", 134 | "Society_Challenged", 135 | "Society_Vote", 136 | "Society_DefenderVote", 137 | "Society_NewMaxMembers", 138 | "Society_Unfounded", 139 | "Society_Deposit", 140 | "Recovery_RecoveryCreated", 141 | "Recovery_RecoveryInitiated", 142 | "Recovery_RecoveryVouched", 143 | "Recovery_RecoveryClosed", 144 | "Recovery_AccountRecovered", 145 | "Recovery_RecoveryRemoved", 146 | "Vesting_VestingUpdated", 147 | "Vesting_VestingCompleted", 148 | "Scheduler_Scheduled", 149 | "Scheduler_Canceled", 150 | "Scheduler_Dispatched", 151 | "Proxy_ProxyExecuted", 152 | "Proxy_AnonymousCreated", 153 | "Sudo_Sudid", 154 | "Sudo_KeyChanged", 155 | "Sudo_SudoAsDone", 156 | "Treasury_Proposed", 157 | "Treasury_Spending", 158 | "Treasury_Awarded", 159 | "Treasury_Rejected", 160 | "Treasury_Burnt", 161 | "Treasury_Rollover", 162 | "Treasury_Deposit", 163 | "Treasury_NewTip", 164 | "Treasury_TipClosing", 165 | "Treasury_TipClosed", 166 | "Treasury_TipRetracted", 167 | "Contracts_Instantiated", 168 | "Contracts_Evicted", 169 | "Contracts_Restored", 170 | "Contracts_CodeStored", 171 | "Contracts_ScheduleUpdated", 172 | "Contracts_ContractExecution", 173 | "Utility_BatchInterrupted", 174 | "Utility_BatchCompleted", 175 | "Multisig_New", 176 | "Multisig_Approval", 177 | "Multisig_Executed", 178 | "Multisig_Cancelled", 179 | "Treasury_BountyProposed", 180 | "Treasury_BountyRejected", 181 | "Treasury_BountyBecameActive", 182 | "Treasury_BountyAwarded", 183 | "Treasury_BountyClaimed", 184 | "Treasury_BountyCanceled", 185 | "Treasury_BountyExtended", 186 | "TechnicalMembership_Dummy", 187 | "Currencies_Transferred", 188 | "Currencies_BalanceUpdated", 189 | "Currencies_Deposited", 190 | "Currencies_Withdrawn", 191 | "Vesting_VestingScheduleAdded", 192 | "Vesting_Claimed", 193 | "Vesting_VestingSchedulesUpdated", 194 | "Multisig_NewMultisig", 195 | "Multisig_MultisigApproval", 196 | "Multisig_MultisigExecuted", 197 | "Multisig_MultisigCancelled", 198 | "Balances_ReserveRepatriated", 199 | "Proxy_Announced", 200 | } 201 | 202 | func IsExist(typeName string) bool { 203 | for _, v := range existTypes { 204 | if typeName == v { 205 | return true 206 | } 207 | } 208 | return false 209 | } 210 | 211 | func Test_GetMetadata(t *testing.T) { 212 | c, err := client.New("wss://node-6714447553211260928.rz.onfinality.io/ws") 213 | if err != nil { 214 | t.Fatal(err) 215 | } 216 | fmt.Println(c.ChainName) 217 | 218 | } 219 | -------------------------------------------------------------------------------- /test/decode_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "github.com/stafiprotocol/go-substrate-rpc-client/scale" 7 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 8 | "testing" 9 | ) 10 | 11 | func Test_decode(t *testing.T) { 12 | fmt.Println(len("60020000000000000100000080b8dc3c030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")) 13 | d := "80b8dc3c030000000000000000000000" 14 | decoder := scale.NewDecoder(bytes.NewReader(types.MustHexDecodeString(d))) 15 | var target types.U128 16 | err := decoder.Decode(&target) 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | fmt.Println(target) 21 | 22 | } 23 | 24 | // 60020000 25 | //00000000 26 | //01000000 27 | //80b8dc3c030000000000000000000000 28 | //00000000000000000000000000000000 29 | //00000000000000000000000000000000 30 | //00000000000000000000000000000000 31 | -------------------------------------------------------------------------------- /test/extrinsic_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "encoding/json" 7 | "fmt" 8 | "github.com/JFJun/stafi-substrate-go/client" 9 | "github.com/JFJun/stafi-substrate-go/expand" 10 | "github.com/JFJun/stafi-substrate-go/utils" 11 | "github.com/stafiprotocol/go-substrate-rpc-client/scale" 12 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 13 | "testing" 14 | ) 15 | 16 | func Test_ExtrinsicDecode(t *testing.T) { 17 | c, err := client.New("wss://rpc.polkadot.io") 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | extrinsic := "450284ffe2d53f597d6e05231b2791346473e8ae6ffa6936f0db3ff74691ca19918dbd230170fd81ee207df240c09be80bbd07834dfbe056f242a82af0129cfc436c0f2521f8a91aefd5242d3d1405ceb6e7c58c27bbb104a41f3679b4b025c0de1ee3488f150010000600ffb001b764098de6caf2de4fc7bdf2c30dc6306e405d52b620112dff0eb6a823670b87689703ab4f" 22 | data, _ := hex.DecodeString(extrinsic) 23 | decoder := scale.NewDecoder(bytes.NewReader(data)) 24 | meta, err := c.C.RPC.State.GetMetadataLatest() 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | ed, err := expand.NewExtrinsicDecoder(meta) 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | err = ed.ProcessExtrinsicDecoder(*decoder) 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | fmt.Println(ed.Value) 37 | d, _ := json.Marshal(ed.Value) 38 | fmt.Println(string(d)) 39 | } 40 | func Test_Compact(t *testing.T) { 41 | d := "001a" 42 | data, _ := hex.DecodeString(d) 43 | decoder := scale.NewDecoder(bytes.NewReader(data)) 44 | var u types.UCompact 45 | err := decoder.Decode(&u) 46 | if err != nil { 47 | t.Fatal(err) 48 | } 49 | fmt.Println(utils.UCompactToBigInt(u).Int64()) 50 | b, err := decoder.ReadOneByte() 51 | if err != nil { 52 | t.Fatal(err) 53 | } 54 | fmt.Println(b) 55 | 56 | } 57 | 58 | // 5502841cf326c5aaa5af9f0e2791e66310fe8f044faadaf12567eaa0976959d1f7731f01946d2b5a9f138a76c96fc6e34358b4236e097cf9c46e44ac05874f195a78ea133f211b89f2b531fd0982cd4e97e63d0c44a2cafa1d89d9c972badcc2b5122889 59 | // 60 | //f502 61 | //ce160200 001a 0004 0500 e464f6458fb3d4b5e040fa9acda4cab9972a38281342e33462546580343daa6a0b00fe4b811704 62 | 63 | //0x5102841cf326c5aaa5af9f0e2791e66310fe8f044faadaf12567eaa0976959d1f7731f015cb2e117826ad322bb7c4e84ac9fb6b5eebf6077fdb0f2d5dfc6ac753f25f44aee92da074ef4d648e0f323a1da4569adf2dee42b2181a1367b9fe4d3cb24a481b50276170200 64 | //001a 65 | // 0004 0500 a4622c0902547c3abd943d50ad4942f787c9c9cbfbb534a51ae6deba7bae0b1c07004894294f 66 | 67 | //0x3d028456bab61e017ef136908f318444ce2c73603fcd6cd985c91dc5ae019c0a6ad706013c21dc38c5df6f17da9922a0115d202a40f77572e790827dfc0aae702206ee26f543fdf2e79ff743c3493fbdb792518cf71c56f67120953b66aec1961402f787f5000000 68 | // 0500 56daf75bd0f09c11d798263bc79baeb77c4b4af1dbd372bbe532b1f8702b2a7e0bc0d01e3b0402 69 | -------------------------------------------------------------------------------- /test/rpc_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "encoding/json" 7 | "fmt" 8 | "github.com/JFJun/go-substrate-crypto/ss58" 9 | "github.com/JFJun/stafi-substrate-go/client" 10 | "github.com/JFJun/stafi-substrate-go/expand" 11 | "github.com/JFJun/stafi-substrate-go/models" 12 | "github.com/stafiprotocol/go-substrate-rpc-client/scale" 13 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 14 | "testing" 15 | ) 16 | 17 | func Test_RpcClient(t *testing.T) { 18 | c, err := client.New("wss://mainnet-rpc.stafi.io") 19 | if err != nil { 20 | t.Fatal(err) 21 | } 22 | var block models.SignedBlock 23 | err = c.C.Client.Call(&block, "chain_getBlock", "0x9832ff45a5d135a37518459cbab3331331e6ae30a0aa6298be4e03ea6c42f71b") 24 | if err != nil { 25 | t.Fatal(err) 26 | } 27 | d, _ := json.Marshal(block) 28 | fmt.Println(string(d)) 29 | h, err := c.C.RPC.Chain.GetFinalizedHead() 30 | fmt.Println(h.Hex()) 31 | 32 | } 33 | 34 | func Test_Zstring(t *testing.T) { 35 | c, err := client.New("wss://rpc.polkadot.io") 36 | if err != nil { 37 | t.Fatal(err) 38 | } 39 | meta, err := c.C.RPC.State.GetMetadataLatest() 40 | if err != nil { 41 | t.Fatal(err) 42 | } 43 | //callIdx,err:=meta.FindCallIndex("Utility.batch") 44 | //if err != nil { 45 | // t.Fatal(err) 46 | //} 47 | //fmt.Println(callIdx) 48 | d, _ := json.Marshal(meta.AsMetadataV12.Modules) 49 | fmt.Println(string(d)) 50 | me, err := expand.NewMetadataExpand(meta) 51 | if err != nil { 52 | t.Fatal(err) 53 | } 54 | callIdx, err := me.MV.GetCallIndex("Utility", "batch") 55 | if err != nil { 56 | t.Fatal(err) 57 | } 58 | fmt.Println(callIdx) 59 | m, cc, err := me.MV.FindNameByCallIndex(callIdx) 60 | if err != nil { 61 | t.Fatal(err) 62 | } 63 | fmt.Println(m, cc) 64 | } 65 | 66 | func Test_GetAccountInfo(t *testing.T) { 67 | c, err := client.New("ws://fis.rylink.io:31844") 68 | //c, err := client.New("wss://rpc.polkadot.io") 69 | c.SetPrefix(ss58.StafiPrefix) 70 | if err != nil { 71 | t.Fatal(err) 72 | } 73 | //c.SetPrefix(ss58.StafiPrefix) 74 | ai, err := c.GetAccountInfo("34R7rV86nwK478QbBWjvCrwdR6UVpBJuvbDwy7MDN328HaWs") 75 | if err != nil { 76 | t.Fatal(err) 77 | } 78 | d, _ := json.Marshal(ai) 79 | fmt.Println(string(d)) 80 | //fmt.Println(uint32(ai.Nonce)) 81 | //address:="32ZWhveKAYJp1CKbP7TZQUTqtcdDdGXcEYnsfDwdZ6Y3qMB3" 82 | ////address = "12KkURmLnQcQQRvVNm5cj5uaBtUL5LVQBXUwmQ6oBHBMLwkG" 83 | //pub, err := ss58.DecodeToPub(address) 84 | //if err != nil { 85 | // t.Fatal(err) 86 | //} 87 | //storage, err := types.CreateStorageKey(c.Meta, "System", "Account", pub, nil) 88 | //if err != nil { 89 | // t.Fatal(err) 90 | //} 91 | //key:=storage.Hex() 92 | //var res interface{} 93 | //err = c.C.Client.Call(&res,"state_getStorageAt", key) 94 | //if err != nil { 95 | // t.Fatal(err) 96 | //} 97 | //fmt.Println(res.(string)) 98 | //decoder:=scale.NewDecoder(bytes.NewReader(types.MustHexDecodeString(res.(string)))) 99 | //var ai types.AccountInfo 100 | //err = decoder.Decode(&ai) 101 | //if err != nil { 102 | // t.Fatal(err) 103 | //} 104 | //d,_:=json.Marshal(ai) 105 | //fmt.Println(string(d)) 106 | //fis: 0x 30000000 00 fdff24a8131000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 107 | //dot: 0x 12000000 02000000 f571d256491b000000000000000000000 0000000000000000000000000000000 00e057eb481b00000000000000000000 00e057eb481b00000000000000000000 108 | } 109 | 110 | func Test_GetBlockByNumber(t *testing.T) { 111 | c, err := client.New("wss://mainnet.chainx.org/ws") 112 | //c, err := client.New("") 113 | if err != nil { 114 | t.Fatal(err) 115 | } 116 | c.SetPrefix(ss58.ChainXPrefix) 117 | expand.SetSerDeOptions(false) 118 | resp, err := c.GetBlockByNumber(1753086) 119 | if err != nil { 120 | t.Fatal(err) 121 | } 122 | d, _ := json.Marshal(resp) 123 | fmt.Println(string(d)) 124 | } 125 | 126 | func Test_GetGenesisHash(t *testing.T) { 127 | c, err := client.New("wss://testnet-1.chainx.org/ws") 128 | if err != nil { 129 | t.Fatal(err) 130 | } 131 | fmt.Println(c.GetGenesisHash()) 132 | } 133 | 134 | func Test_CalcFee(t *testing.T) { 135 | c, err := client.New("wss://mainnet-rpc.stafi.io") 136 | //c, err := client.New("wss://rpc.polkadot.io") 137 | c.SetPrefix(ss58.StafiPrefix) 138 | if err != nil { 139 | t.Fatal(err) 140 | } 141 | md, err := expand.NewMetadataExpand(c.Meta) 142 | if err != nil { 143 | t.Fatal(err) 144 | } 145 | _, value, err := md.MV.GetConstants("TransactionPayment", "TransactionByteFee") 146 | if err != nil { 147 | t.Fatal(err) 148 | } 149 | var tbf types.U128 150 | decoder := scale.NewDecoder(bytes.NewReader(value)) 151 | err = decoder.Decode(&tbf) 152 | if err != nil { 153 | t.Fatal(err) 154 | } 155 | transactionByteFee := tbf.Uint64() 156 | fmt.Println("pre_Bytes:", transactionByteFee) 157 | 158 | _, value2, err := md.MV.GetConstants("System", "ExtrinsicBaseWeight") 159 | var w types.U32 160 | decoder2 := scale.NewDecoder(bytes.NewReader(value2)) 161 | err = decoder2.Decode(&w) 162 | if err != nil { 163 | t.Fatal(err) 164 | } 165 | extrinsicBaseWeight := int64(w) 166 | fmt.Println("extrinsicWeight:", extrinsicBaseWeight) 167 | 168 | storage, err := types.CreateStorageKey(c.Meta, "TransactionPayment", "NextFeeMultiplier", nil, nil) 169 | if err != nil { 170 | t.Fatal(err) 171 | } 172 | var weight types.U128 173 | data, _ := hex.DecodeString("db78f4f0026651e6d85e32d9601a92571e107b7c907d85ea606d3cc12a7285bf") 174 | prehash := types.NewHash(data) 175 | ok, err := c.C.RPC.State.GetStorage(storage, &weight, prehash) 176 | if err != nil { 177 | t.Fatal(err) 178 | } 179 | //var r interface{} 180 | //err = c.C.Client.Call(&r,"state_getStorageAt",storage.Hex(),"0x822ad83fdad1b40ed35159b1e32c8b5d32d3e034b08bf2e9ebde18f3141004b9") 181 | //if err != nil { 182 | // t.Fatal(err) 183 | //} 184 | //fmt.Println("R: ",r) 185 | //rH:=r.(string) 186 | //rH = strings.TrimPrefix(rH,"0x") 187 | //dd,_:=hex.DecodeString(rH) 188 | //fmt.Println(dd) 189 | if !ok { 190 | t.Fatal(111) 191 | } 192 | fmt.Println("WeightMultiplier: ", weight) 193 | _, value3, err := md.MV.GetConstants("TransactionPayment", "WeightToFee") 194 | if err != nil { 195 | t.Fatal(err) 196 | } 197 | decoder3 := scale.NewDecoder(bytes.NewReader(value3)) 198 | vec := new(expand.Vec) 199 | var wtfc expand.WeightToFeeCoefficient 200 | err = vec.ProcessVec(*decoder3, wtfc) 201 | if err != nil { 202 | t.Fatal(err) 203 | } 204 | d, _ := json.Marshal(vec.Value) 205 | fmt.Println("WTFC:", string(d)) 206 | vv := vec.Value[0] 207 | hj := vv.(*expand.WeightToFeeCoefficient) 208 | cf := expand.NewCalcFee(hj, extrinsicBaseWeight, int64(transactionByteFee), int64(weight.Int64())) 209 | fee := cf.CalcPartialFee(190949000, 143) 210 | fmt.Println(fee) 211 | } 212 | 213 | func Test_GetChainName(t *testing.T) { 214 | c, err := client.New("wss://cc1.darwinia.network") 215 | if err != nil { 216 | t.Fatal(err) 217 | } 218 | d, err := c.C.RPC.State.GetRuntimeVersionLatest() 219 | if err != nil { 220 | t.Fatal(err) 221 | } 222 | fmt.Println(d.SpecName) 223 | } 224 | 225 | func Test_GetLatestBlock(t *testing.T) { 226 | 227 | } 228 | 229 | // 1700dcea9317bceb28b52bdae9229a3794de4ca85e36d990a78f779c6fd7f27eb54102890700003c001c0000000300000034f61bfda344b3fad3c3e38832a91448b3c613b199eb23e5110a635d71c13c6534f61bfda344b3fad3c3e38832a91448b3c613b199eb23e5110a635d71c13c65 230 | // 1700dcea9317bceb28b52bdae9229a3794de4ca85e36d990a78f779c6fd7f27eb54102890700003c001c0000000300000034f61bfda344b3fad3c3e38832a91448b3c613b199eb23e5110a635d71c13c6534f61bfda344b3fad3c3e38832a91448b3c613b199eb23e5110a635d71c13c65 231 | 232 | func Test_GetChainEvent(t *testing.T) { 233 | c, err := client.New("wss://rpc.polkadot.io") 234 | if err != nil { 235 | t.Fatal(err) 236 | } 237 | //fmt.Println(c.ChainName) 238 | //mod,_:=json.Marshal(c.Meta.AsMetadataV12.Modules) 239 | //fmt.Println(string(mod)) 240 | for _, mod := range c.Meta.AsMetadataV12.Modules { 241 | if mod.HasEvents { 242 | for _, event := range mod.Events { 243 | //typeName := fmt.Sprintf("%s_%s", mod.Name, event.Name) 244 | //if IsExist(typeName) { 245 | // continue 246 | //} 247 | 248 | //fmt.Println(event.Name) 249 | for _, arg := range event.Args { 250 | fmt.Println(arg) 251 | } 252 | } 253 | } 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /test/storage_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/JFJun/stafi-substrate-go/client" 7 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 8 | "testing" 9 | ) 10 | 11 | func Test_GetStorage(t *testing.T) { 12 | blockHash := "0x02c5ac1e520188c91fee16a27a1627dfbe8e5f6d349cae87e2889beece19ec9f" 13 | c, err := client.New("wss://mainnet-rpc.stafi.io") 14 | if err != nil { 15 | t.Fatal(err) 16 | } 17 | meta, err := c.C.RPC.State.GetMetadataLatest() 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | storage, err := types.CreateStorageKey(meta, "System", "Events", nil, nil) 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | 26 | key := storage.Hex() 27 | var r interface{} 28 | err = c.C.Client.Call(&r, "state_getStorage", key, blockHash) 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | 33 | e := types.EventRecordsRaw(types.MustHexDecodeString(r.(string))) 34 | events := types.EventRecords{} 35 | err = e.DecodeEventRecords(meta, &events) 36 | if err != nil { 37 | t.Fatal(err) 38 | } 39 | dd, _ := json.Marshal(events) 40 | fmt.Println(string(dd)) 41 | } 42 | -------------------------------------------------------------------------------- /test/tx2_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/JFJun/go-substrate-crypto/crypto" 6 | "github.com/JFJun/stafi-substrate-go/client" 7 | "github.com/JFJun/stafi-substrate-go/expand" 8 | "github.com/JFJun/stafi-substrate-go/tx" 9 | "testing" 10 | ) 11 | 12 | func Test_Tx2(t *testing.T) { 13 | // 1. 初始化rpc客户端 14 | c, err := client.New("") 15 | if err != nil { 16 | t.Fatal(err) 17 | } 18 | //2. 如果某些链(例如:chainX)的地址的字节前面需要0xff,则下面这个值设置为false 19 | //expand.SetSerDeOptions(false) 20 | from := "" 21 | to := "" 22 | amount := uint64(10000000000) 23 | //3. 获取from地址的nonce 24 | acc, err := c.GetAccountInfo(from) 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | nonce := uint64(acc.Nonce) 29 | //4. 创建一个substrate交易,这个方法满足所有遵循substrate 的交易结构的链 30 | transaction := tx.NewSubstrateTransaction(from, nonce) 31 | //5. 初始化metadata的扩张结构 32 | ed, err := expand.NewMetadataExpand(c.Meta) 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | //6. 初始化Balances.transfer的call方法 37 | call, err := ed.BalanceTransferCall(to, amount) 38 | if err != nil { 39 | t.Fatal(err) 40 | } 41 | /* 42 | //Balances.transfer_keep_alive call方法 43 | btkac,err:=ed.BalanceTransferKeepAliveCall(to,amount) 44 | */ 45 | 46 | /* 47 | toAmount:=make(map[string]uint64) 48 | toAmount[to] = amount 49 | //... 50 | //true: user Balances.transfer_keep_alive false: Balances.transfer 51 | ubtc,err:=ed.UtilityBatchTxCall(toAmount,false) 52 | */ 53 | 54 | //7. 设置交易的必要参数 55 | transaction.SetGenesisHashAndBlockHash(c.GetGenesisHash(), c.GetGenesisHash()). 56 | SetSpecAndTxVersion(uint32(c.SpecVersion), uint32(c.TransactionVersion)). 57 | SetCall(call) //设置call 58 | //8. 签名交易 59 | sig, err := transaction.SignTransaction("", crypto.Sr25519Type) 60 | if err != nil { 61 | t.Fatal(err) 62 | } 63 | //9. 提交交易 64 | var result interface{} 65 | err = c.C.Client.Call(&result, "author_submitExtrinsic", sig) 66 | if err != nil { 67 | t.Fatal(err) 68 | } 69 | //10. txid 70 | txid := result.(string) 71 | fmt.Println(txid) 72 | } 73 | -------------------------------------------------------------------------------- /test/tx_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/JFJun/go-substrate-crypto/crypto" 7 | "github.com/JFJun/go-substrate-crypto/ss58" 8 | "github.com/JFJun/stafi-substrate-go/client" 9 | "github.com/JFJun/stafi-substrate-go/expand" 10 | "github.com/JFJun/stafi-substrate-go/tx" 11 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 12 | "testing" 13 | ) 14 | 15 | func Test_tx(t *testing.T) { 16 | from := "eYGvmRP1fu418SSaypzN84S58YCm9c8SST9V2NriPbEMPP9" 17 | to := "gA7aiTz144UvgFtPEboN6JjoQgGGUiuM9Wx3NRzETw6gCW6" 18 | 19 | amount := uint64(50000000000000) 20 | c, err := client.New("wss://testnet.liebi.com") 21 | if err != nil { 22 | t.Fatal(err) 23 | } 24 | v, err := c.C.RPC.State.GetRuntimeVersionLatest() 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | fmt.Println(v.TransactionVersion) 29 | fmt.Println(v.SpecVersion) 30 | acc, err := c.GetAccountInfo(from) 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | c.SetPrefix(ss58.BifrostPrefix) 35 | nonce := uint64(acc.Nonce) 36 | fmt.Println(nonce) 37 | fmt.Println(c.GetGenesisHash()) 38 | types.SetSerDeOptions(types.SerDeOptions{NoPalletIndices: true}) 39 | transaction := tx.CreateTransaction(from, to, amount, nonce) 40 | transaction.SetGenesisHashAndBlockHash(c.GetGenesisHash(), 41 | c.GetGenesisHash()) 42 | ed, err := expand.NewMetadataExpand(c.Meta) 43 | if err != nil { 44 | t.Fatal(err) 45 | } 46 | btCall, err := ed.MV.GetCallIndex("Balances", "transfer") 47 | if err != nil { 48 | t.Fatal(err) 49 | } 50 | fmt.Println(btCall) 51 | transaction.SetSpecVersionAndCallId(uint32(v.SpecVersion), uint32(v.TransactionVersion), btCall) 52 | tt, err := transaction.SignTransaction("000", crypto.Sr25519Type) 53 | if err != nil { 54 | t.Fatal(err) 55 | } 56 | fmt.Println(tt) 57 | var result interface{} 58 | err = c.C.Client.Call(&result, "author_submitExtrinsic", tt) 59 | if err != nil { 60 | t.Fatal(err) 61 | } 62 | fmt.Println(result) 63 | } 64 | 65 | func Test_CreateUtilityBatch(t *testing.T) { 66 | from := "5DkswVFmWPUwPkmqMUEvavvso2HMdiyY71ixA2e52Ynwzvtg" 67 | to := "5H4N5JZHuqkprDKSR9SJeTMivbQQ94WrxeFELxh45ACoZFQC" 68 | nonce := uint64(16) 69 | //amount := uint64(123456) 70 | c, err := client.New("wss://api.crust.network/") 71 | if err != nil { 72 | t.Fatal(err) 73 | } 74 | v, err := c.C.RPC.State.GetRuntimeVersionLatest() 75 | if err != nil { 76 | t.Fatal(err) 77 | } 78 | acc, err := c.GetAccountInfo(from) 79 | if err != nil { 80 | t.Fatal(err) 81 | } 82 | nonce = uint64(acc.Nonce) 83 | pa := make(map[string]uint64) 84 | pa[to] = 100 85 | pa["5Hmy8BVAXAdaL6uxd41WJV4rhhWCNsXzekFRfuwLDkke9nG4"] = 1000000000 86 | types.SetSerDeOptions(types.SerDeOptions{NoPalletIndices: true}) 87 | transaction := tx.CreateUtilityBatchTransaction(from, nonce, pa, "1100") 88 | transaction.SetGenesisHashAndBlockHash("0x34f61bfda344b3fad3c3e38832a91448b3c613b199eb23e5110a635d71c13c65", 89 | "0x34f61bfda344b3fad3c3e38832a91448b3c613b199eb23e5110a635d71c13c65") 90 | transaction.SetSpecVersionAndCallId(uint32(v.SpecVersion), uint32(v.TransactionVersion), "1700") 91 | tt, err := transaction.SignTransaction("00000", crypto.Sr25519Type) 92 | if err != nil { 93 | t.Fatal(err) 94 | } 95 | fmt.Println(tt) 96 | var result interface{} 97 | err = c.C.Client.Call(&result, "author_submitExtrinsic", tt) 98 | if err != nil { 99 | t.Fatal(err) 100 | } 101 | fmt.Println(result) 102 | d, _ := json.Marshal(result) 103 | fmt.Println(string(d)) 104 | 105 | } 106 | 107 | func Test_TxWithMemo(t *testing.T) { 108 | from := "5Fq9MpKxdjzCWEHHtqZ6rdYkKUtW4qwmJV4VHwKBan2hxRyL" 109 | to := "5E2dFRZoSbXE4at8QjHPxfx8eWA9mvLFbH64ZE3wTAsEwVFu" 110 | nonce := uint64(1) 111 | amount := uint64(123456) 112 | c, err := client.New("wss://testnet-1.chainx.org/ws") 113 | if err != nil { 114 | t.Fatal(err) 115 | } 116 | v, err := c.C.RPC.State.GetRuntimeVersionLatest() 117 | if err != nil { 118 | t.Fatal(err) 119 | } 120 | ed, err := expand.NewMetadataExpand(c.Meta) 121 | if err != nil { 122 | t.Fatal(err) 123 | } 124 | ubCall, err := ed.MV.GetCallIndex("Utility", "batch") 125 | if err != nil { 126 | t.Fatal(err) 127 | } 128 | btCall, err := ed.MV.GetCallIndex("Balances", "transfer") 129 | if err != nil { 130 | t.Fatal(err) 131 | } 132 | srCall, err := ed.MV.GetCallIndex("System", "remark") 133 | transaction := tx.CreateTransactionWithMemo(from, to, "test", amount, nonce, ubCall, srCall) 134 | transaction.SetGenesisHashAndBlockHash(c.GetGenesisHash(), 135 | c.GetGenesisHash()) 136 | transaction.SetSpecVersionAndCallId(uint32(v.SpecVersion), uint32(v.TransactionVersion), btCall) 137 | tt, err := transaction.SignTransaction("", crypto.Sr25519Type) 138 | if err != nil { 139 | t.Fatal(err) 140 | } 141 | fmt.Println(tt) 142 | var result interface{} 143 | err = c.C.Client.Call(&result, "author_submitExtrinsic", tt) 144 | if err != nil { 145 | t.Fatal(err) 146 | } 147 | fmt.Println(result) 148 | d, _ := json.Marshal(result) 149 | fmt.Println(string(d)) 150 | } 151 | 152 | func Test_FakeDeposit(t *testing.T) { 153 | from := "5RHGBggpMDvQ9HtjjFpK7oETuc51DAj4QcATk6HH2dswNZ98" 154 | to := "5U2RLJHbQ1VQJ1bLACxYMtxSQVxRPqfp34WAUxfb5zpUEwaQ" 155 | nonce := uint64(15) 156 | amount := uint64(123456) 157 | c, err := client.New("") 158 | if err != nil { 159 | t.Fatal(err) 160 | } 161 | c.SetPrefix(ss58.ChainXPrefix) 162 | v, err := c.C.RPC.State.GetRuntimeVersionLatest() 163 | if err != nil { 164 | t.Fatal(err) 165 | } 166 | ed, err := expand.NewMetadataExpand(c.Meta) 167 | if err != nil { 168 | t.Fatal(err) 169 | } 170 | callIdx, err := ed.MV.GetCallIndex("Balances", "transfer") 171 | if err != nil { 172 | t.Fatal(err) 173 | } 174 | for i := 0; i < 2; i++ { 175 | if i == 0 { 176 | nonce = 5 177 | amount = 1000000 178 | } else { 179 | nonce = 6 180 | amount = 7000000 181 | } 182 | transaction := tx.CreateTransaction(from, to, amount, nonce) 183 | transaction.SetGenesisHashAndBlockHash(c.GetGenesisHash(), 184 | c.GetGenesisHash()) 185 | transaction.SetSpecVersionAndCallId(uint32(v.SpecVersion), uint32(v.TransactionVersion), callIdx) 186 | tt, err := transaction.SignTransaction("", crypto.Sr25519Type) 187 | if err != nil { 188 | t.Fatal(err) 189 | } 190 | var result interface{} 191 | err = c.C.Client.Call(&result, "author_submitExtrinsic", tt) 192 | if err != nil { 193 | t.Fatal(err) 194 | } 195 | fmt.Println(result) 196 | } 197 | } 198 | 199 | // 0x390284ff 8ce4f854296af0a2fa35faaf6f6577fb46d63d833d1a24a219d604506d151328 01 56e907f93a77685966e939d84c93366ff9895087c2d7f4cbe39ebaf750fe114004e7d01e836d4893d2c7cb8728915fb3d9908195ab70c8e1c53bcce25f52d48a 00 4102 00 0500ffc4b1c12fd91e7c199b4a3da3a3adee7bfd97f35dee81d58a670de7b294a7fa7402890700 200 | // 0x3102 84 8ce4f854296af0a2fa35faaf6f6577fb46d63d833d1a24a219d604506d151328 01 260d90add44e26c8c290314711f30022fc33c7e2c626e8fabbfac7ff16ea04585e7e69f420d2a3dd7adf0147cb9fbaf41838708f5e78bbdf3a3eb649f834de8a 00 4102 00 0500c4b1c12fd91e7c199b4a3da3a3adee7bfd97f35dee81d58a670de7b294a7fa7402890700 201 | 202 | // 0x350284ff4adffe0994aac9e292470b27eac94e505532ac1a22ae17012ddad445e6b78019018638639fb20aab85c72d5078439e6534a7b49039c4499ecb7d416d08792e7a4dbc4bc60db33a4681303a728ab3370a9e0d960ef4f60feef7c1eb391fc3538084003c001700ffdcea9317bceb28b52bdae9229a3794de4ca85e36d990a78f779c6fd7f27eb54102890700 203 | // 0x350284ff4adffe0994aac9e292470b27eac94e505532ac1a22ae17012ddad445e6b78019019c7d33500b6cf5bf2da5291c948d1a333766155b570e745a31647f589bafa50b8f62048ec7d4196f1242482944e5f64d0f829d0a2ffbfa7e9f308eacc44a538d003c001700ffdcea9317bceb28b52bdae9229a3794de4ca85e36d990a78f779c6fd7f27eb54102890700 204 | 205 | // 0x 2d02 84 8ce4f854296af0a2fa35faaf6f6577fb46d63d833d1a24a219d604506d151328 01 206 | // da7245068281d7bd5e1e3db63b3b1d8c43664f5393687f663d7d80c9515f590f 207 | // af5dd9be85eaebb83de8cbb53d368a77e7bd3b0a139602ddafaf0e1736e4038d 208 | // 000c000500c4b1c12fd91e7c199b4a3da3a3adee7bfd97f35dee81d58a670de7b294a7fa7402890700 209 | // 0x 2d02 84 8ce4f854296af0a2fa35faaf6f6577fb46d63d833d1a24a219d604506d151328 01 210 | // 288e08acd89507664ca4cd2684157921a7a3df810e9bbda30669edb63d7bcc79 211 | // 505d4e87b5d525cb0b8f1c497b0fb9aa36776ee9fc1315188119f309a821cb8f 212 | // 000c000500c4b1c12fd91e7c199b4a3da3a3adee7bfd97f35dee81d58a670de7b294a7fa7402890700 213 | -------------------------------------------------------------------------------- /tx/tx.go: -------------------------------------------------------------------------------- 1 | package tx 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | "fmt" 7 | "github.com/JFJun/go-substrate-crypto/crypto" 8 | "github.com/JFJun/stafi-substrate-go/expand" 9 | "github.com/JFJun/stafi-substrate-go/utils" 10 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 11 | "golang.org/x/crypto/blake2b" 12 | "strings" 13 | ) 14 | 15 | type Transaction struct { 16 | SenderPubkey string `json:"sender_pubkey"` // from address public key ,0x开头 17 | RecipientPubkey string `json:"recipient_pubkey"` // to address public key ,0x开头 18 | Amount uint64 `json:"amount"` // 转账金额 19 | Nonce uint64 `json:"nonce"` //nonce值 20 | Tip uint64 `json:"tip"` //小费 21 | BlockNumber uint64 `json:"block_Number"` //最新区块高度 22 | EraPeriod uint64 `json:"era_period"` // 存活最大区块 //default set 64 23 | BlockHash string `json:"block_hash"` //最新区块hash 24 | GenesisHash string `json:"genesis_hash"` // 25 | SpecVersion uint32 `json:"spec_version"` 26 | TransactionVersion uint32 `json:"transaction_version"` 27 | CallId string `json:"call_id"` // Balances.transfer的call index 28 | UtilityBatchCallId string `json:"utility_batch_call_id"` 29 | PubkeyAmount map[string]uint64 `json:"pubkey_amount"` //用于utilityBatch 30 | // 用于交易memo 31 | Memo string `json:"memo"` 32 | SystemRemarkCallId string `json:"system_remark_call_id"` //通过System.remark去携带memo信息 33 | } 34 | 35 | /* 36 | 创建一笔Balances.transfer或者Balances.transfer_keep_alive交易 37 | */ 38 | func CreateTransaction(from, to string, amount, nonce uint64) *Transaction { 39 | return &Transaction{ 40 | SenderPubkey: utils.AddressToPublicKey(from), 41 | RecipientPubkey: utils.AddressToPublicKey(to), 42 | Amount: amount, 43 | Nonce: nonce, 44 | } 45 | } 46 | 47 | /* 48 | 创建一笔Utility.batch交易 49 | */ 50 | func CreateUtilityBatchTransaction(from string, nonce uint64, address_amount map[string]uint64, utilityBatchCallId string) *Transaction { 51 | tx := new(Transaction) 52 | tx.SenderPubkey = utils.AddressToPublicKey(from) 53 | tx.Nonce = nonce 54 | pub_amount := make(map[string]uint64) 55 | for address, amount := range address_amount { 56 | pub_amount[utils.AddressToPublicKey(address)] = amount 57 | } 58 | tx.PubkeyAmount = pub_amount 59 | tx.UtilityBatchCallId = utilityBatchCallId 60 | return tx 61 | } 62 | 63 | /* 64 | 创建一笔带memo的交易 65 | 主要思想是在Utility.batch中,添加一个balance.transfer的call方法和一个System.remark的call方法 66 | */ 67 | func CreateTransactionWithMemo(from, to, memo string, amount, nonce uint64, utilityBatchCallId, systemRemarkCallId string) *Transaction { 68 | return &Transaction{ 69 | SenderPubkey: utils.AddressToPublicKey(from), 70 | RecipientPubkey: utils.AddressToPublicKey(to), 71 | Amount: amount, 72 | Nonce: nonce, 73 | Memo: memo, 74 | UtilityBatchCallId: utilityBatchCallId, 75 | SystemRemarkCallId: systemRemarkCallId, 76 | } 77 | } 78 | 79 | /* 80 | 设置交易的必要参数genesisHash和blockHash 81 | */ 82 | func (tx *Transaction) SetGenesisHashAndBlockHash(genesisHash, blockHash string) *Transaction { 83 | tx.GenesisHash = utils.Remove0X(genesisHash) 84 | tx.BlockHash = utils.Remove0X(blockHash) 85 | return tx 86 | } 87 | 88 | /* 89 | 设置链的版本以及交易版本还有Balance.transfer或者Balance.transfer_keep_alive的callIdx 90 | */ 91 | func (tx *Transaction) SetSpecVersionAndCallId(specVersion, transactionVersion uint32, callIdx string) *Transaction { 92 | tx.SpecVersion = specVersion 93 | tx.TransactionVersion = transactionVersion 94 | tx.CallId = callIdx 95 | return tx 96 | } 97 | 98 | /* 99 | 给矿工增加手续费,可以加快打包速度 100 | */ 101 | func (tx *Transaction) SetTip(tip uint64) *Transaction { 102 | tx.Tip = tip 103 | return tx 104 | } 105 | 106 | /* 107 | 设置如果交易一直处于pending中,最多存活多少个块 108 | */ 109 | func (tx *Transaction) SetEra(blockNumber, eraPeriod uint64) *Transaction { 110 | tx.BlockNumber = blockNumber 111 | tx.EraPeriod = eraPeriod 112 | return tx 113 | } 114 | 115 | /* 116 | 检查是否有必要的参数 117 | */ 118 | func (tx *Transaction) checkTxParams() error { 119 | if tx.SenderPubkey == "" { 120 | return errors.New("send public key is null") 121 | } 122 | if tx.BlockHash == "" { 123 | return errors.New("block hash is null") 124 | } 125 | if tx.GenesisHash == "" { 126 | return errors.New("genesis hash is null") 127 | } 128 | if tx.CallId == "" { 129 | return errors.New("callIdx is null") 130 | } 131 | if tx.UtilityBatchCallId != "" { 132 | if tx.Memo != "" { 133 | return nil 134 | } 135 | if len(tx.PubkeyAmount) == 0 { 136 | return errors.New("public key Amount map is null") 137 | } 138 | } else { 139 | if tx.RecipientPubkey == "" { 140 | return errors.New("recipient public key is null") 141 | } 142 | } 143 | return nil 144 | } 145 | 146 | /* 147 | signType: 0-->ecdsa 1--> sr25519 2--> ed25519 148 | 目前暂不支持ecdsa 149 | */ 150 | func (tx *Transaction) SignTransaction(privateKey string, signType int) (string, error) { 151 | var ( 152 | call types.Call 153 | err error 154 | ) 155 | //1. check params 156 | err = tx.checkTxParams() 157 | if err != nil { 158 | return "", fmt.Errorf("check params error: $v", err) 159 | } 160 | //2. create types.Call 161 | 162 | if tx.UtilityBatchCallId == "" { 163 | // Balances.transfer交易或者Balances.transfer_keep_alive交易 164 | call, err = expand.NewCall(tx.CallId, types.NewAddressFromAccountID(types.MustHexDecodeString( 165 | tx.RecipientPubkey)), 166 | types.NewUCompactFromUInt(tx.Amount)) 167 | } else { 168 | // Utility.batch 交易 169 | var args []interface{} 170 | if tx.SystemRemarkCallId != "" { 171 | balanceTransferCall, err := expand.NewCall(tx.CallId, types.NewAddressFromAccountID(types.MustHexDecodeString(tx.RecipientPubkey)), 172 | types.NewUCompactFromUInt(tx.Amount)) 173 | if err != nil { 174 | return "", fmt.Errorf("create utility.batch calls error: %v", err) 175 | } 176 | systemRemarkCall, err := expand.NewCall(tx.SystemRemarkCallId, tx.Memo) 177 | if err != nil { 178 | return "", fmt.Errorf("create System.remark error: %v", err) 179 | } 180 | //System.remark 181 | args = append(args, balanceTransferCall) 182 | args = append(args, systemRemarkCall) 183 | } else { 184 | for address, amount := range tx.PubkeyAmount { 185 | balanceTransferCall, err := expand.NewCall(tx.CallId, types.NewAddressFromAccountID(types.MustHexDecodeString(address)), 186 | types.NewUCompactFromUInt(amount)) 187 | if err != nil { 188 | return "", fmt.Errorf("create utility.batch calls error: %v", err) 189 | } 190 | args = append(args, balanceTransferCall) 191 | } 192 | } 193 | call, err = expand.NewCall(tx.UtilityBatchCallId, args) 194 | } 195 | if err != nil { 196 | return "", fmt.Errorf("create types.Call error: %v", err) 197 | } 198 | ext := types.NewExtrinsic(call) 199 | o := types.SignatureOptions{ 200 | BlockHash: types.NewHash(types.MustHexDecodeString(tx.BlockHash)), 201 | GenesisHash: types.NewHash(types.MustHexDecodeString(tx.GenesisHash)), 202 | Nonce: types.NewUCompactFromUInt(tx.Nonce), 203 | SpecVersion: types.NewU32(tx.SpecVersion), 204 | Tip: types.NewUCompactFromUInt(tx.Tip), 205 | TransactionVersion: types.NewU32(tx.TransactionVersion), 206 | } 207 | era := tx.getEra() 208 | if era != nil { 209 | o.Era = *era 210 | } 211 | e := &ext 212 | //签名 213 | err = tx.signTx(e, o, privateKey, signType) 214 | if err != nil { 215 | return "", fmt.Errorf("sign error: %v", err) 216 | } 217 | return types.EncodeToHexString(e) 218 | } 219 | 220 | func (tx *Transaction) signTx(e *types.Extrinsic, o types.SignatureOptions, privateKey string, signType int) error { 221 | if e.Type() != types.ExtrinsicVersion4 { 222 | return fmt.Errorf("unsupported extrinsic version: %v (isSigned: %v, type: %v)", e.Version, e.IsSigned(), e.Type()) 223 | } 224 | mb, err := types.EncodeToBytes(e.Method) 225 | if err != nil { 226 | return err 227 | } 228 | era := o.Era 229 | if !o.Era.IsMortalEra { 230 | era = types.ExtrinsicEra{IsImmortalEra: true} 231 | } 232 | payload := types.ExtrinsicPayloadV4{ 233 | ExtrinsicPayloadV3: types.ExtrinsicPayloadV3{ 234 | Method: mb, 235 | Era: era, 236 | Nonce: o.Nonce, 237 | Tip: o.Tip, 238 | SpecVersion: o.SpecVersion, 239 | GenesisHash: o.GenesisHash, 240 | BlockHash: o.BlockHash, 241 | }, 242 | TransactionVersion: o.TransactionVersion, 243 | } 244 | // sign 245 | data, err := types.EncodeToBytes(payload) 246 | if err != nil { 247 | return fmt.Errorf("encode payload error: %v", err) 248 | } 249 | // if data is longer than 256 bytes, hash it first 250 | if len(data) > 256 { 251 | h := blake2b.Sum256(data) 252 | data = h[:] 253 | } 254 | privateKey = strings.TrimPrefix(privateKey, "0x") 255 | priv, err := hex.DecodeString(privateKey) 256 | if err != nil { 257 | return fmt.Errorf("hex decode private key error: %v", err) 258 | } 259 | 260 | defer utils.ZeroBytes(priv) 261 | sig, err := crypto.Sign(priv, data, signType) 262 | if err != nil { 263 | return fmt.Errorf("sign error: %v", err) 264 | } 265 | signerPubKey := types.NewAddressFromAccountID(types.MustHexDecodeString( 266 | tx.SenderPubkey)) 267 | //fmt.Println(hex.EncodeToString(sig)) 268 | var ss types.MultiSignature 269 | if signType == crypto.Ed25519Type { 270 | ss = types.MultiSignature{IsEd25519: true, AsEd25519: types.NewSignature(sig)} 271 | } else if signType == crypto.Sr25519Type { 272 | ss = types.MultiSignature{IsSr25519: true, AsSr25519: types.NewSignature(sig)} 273 | } else if signType == crypto.EcdsaType { 274 | ss = types.MultiSignature{IsEcdsa: true, AsEcdsa: types.NewBytes(sig)} 275 | } else { 276 | return fmt.Errorf("unsupport sign type : %d", signType) 277 | } 278 | extSig := types.ExtrinsicSignatureV4{ 279 | Signer: signerPubKey, 280 | Signature: ss, 281 | Era: era, 282 | Nonce: o.Nonce, 283 | Tip: o.Tip, 284 | } 285 | e.Signature = extSig 286 | e.Version |= types.ExtrinsicBitSigned 287 | return nil 288 | } 289 | func (tx *Transaction) getEra() *types.ExtrinsicEra { 290 | if tx.BlockNumber == 0 || tx.EraPeriod == 0 { 291 | return nil 292 | } 293 | phase := tx.BlockNumber % tx.EraPeriod 294 | index := uint64(6) 295 | trailingZero := index - 1 296 | 297 | var encoded uint64 298 | if trailingZero > 1 { 299 | encoded = trailingZero 300 | } else { 301 | encoded = 1 302 | } 303 | 304 | if trailingZero < 15 { 305 | encoded = trailingZero 306 | } else { 307 | encoded = 15 308 | } 309 | encoded += phase / 1 << 4 310 | first := byte(encoded >> 8) 311 | second := byte(encoded & 0xff) 312 | era := new(types.ExtrinsicEra) 313 | era.IsMortalEra = true 314 | era.AsMortalEra.First = first 315 | era.AsMortalEra.Second = second 316 | return era 317 | } 318 | -------------------------------------------------------------------------------- /tx/tx2.go: -------------------------------------------------------------------------------- 1 | package tx 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "github.com/JFJun/go-substrate-crypto/crypto" 7 | "github.com/JFJun/stafi-substrate-go/utils" 8 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 9 | "golang.org/x/crypto/blake2b" 10 | "strings" 11 | ) 12 | 13 | /* 14 | ======================================= expand transaction=================================== 15 | 由于许多的链的交易格式稍有不同,所以决定交易结构进行更细 的拆分 16 | */ 17 | 18 | type SubstrateTransaction struct { 19 | SenderPubkey string `json:"sender_pubkey"` // from address public key ,0x开头 20 | Nonce uint64 `json:"nonce"` //nonce值 21 | BlockHash string `json:"block_hash"` //最新区块hash 22 | GenesisHash string `json:"genesis_hash"` // 23 | SpecVersion uint32 `json:"spec_version"` 24 | TransactionVersion uint32 `json:"transaction_version"` 25 | Tip uint64 `json:"tip"` //小费 26 | BlockNumber uint64 `json:"block_Number"` //最新区块高度 27 | EraPeriod uint64 `json:"era_period"` // 存活最大区块 28 | call types.Call 29 | } 30 | 31 | func NewSubstrateTransaction(from string, nonce uint64) *SubstrateTransaction { 32 | st := new(SubstrateTransaction) 33 | st.SenderPubkey = utils.AddressToPublicKey(from) 34 | st.Nonce = nonce 35 | return st 36 | } 37 | 38 | /* 39 | 设置交易的必要参数genesisHash和blockHash 40 | */ 41 | func (tx *SubstrateTransaction) SetGenesisHashAndBlockHash(genesisHash, blockHash string) *SubstrateTransaction { 42 | tx.GenesisHash = utils.Remove0X(genesisHash) 43 | tx.BlockHash = utils.Remove0X(blockHash) 44 | return tx 45 | } 46 | 47 | /* 48 | 设置链的版本以及交易版本 49 | */ 50 | func (tx *SubstrateTransaction) SetSpecAndTxVersion(specVersion, transactionVersion uint32) *SubstrateTransaction { 51 | tx.SpecVersion = specVersion 52 | tx.TransactionVersion = transactionVersion 53 | return tx 54 | } 55 | 56 | /* 57 | 给矿工增加手续费,可以加快打包速度 58 | */ 59 | func (tx *SubstrateTransaction) SetTip(tip uint64) *SubstrateTransaction { 60 | tx.Tip = tip 61 | return tx 62 | } 63 | 64 | /* 65 | 设置如果交易一直处于pending中,最多存活多少个块 66 | */ 67 | func (tx *SubstrateTransaction) SetEra(blockNumber, eraPeriod uint64) *SubstrateTransaction { 68 | tx.BlockNumber = blockNumber 69 | tx.EraPeriod = eraPeriod 70 | return tx 71 | } 72 | func (tx *SubstrateTransaction) SetCall(call types.Call) *SubstrateTransaction { 73 | tx.call = call 74 | return tx 75 | } 76 | 77 | func (tx *SubstrateTransaction) SignTransaction(privateKey string, signType int) (string, error) { 78 | 79 | ext := types.NewExtrinsic(tx.call) 80 | o := types.SignatureOptions{ 81 | BlockHash: types.NewHash(types.MustHexDecodeString(tx.BlockHash)), 82 | GenesisHash: types.NewHash(types.MustHexDecodeString(tx.GenesisHash)), 83 | Nonce: types.NewUCompactFromUInt(tx.Nonce), 84 | SpecVersion: types.NewU32(tx.SpecVersion), 85 | Tip: types.NewUCompactFromUInt(tx.Tip), 86 | TransactionVersion: types.NewU32(tx.TransactionVersion), 87 | } 88 | era := tx.getEra() 89 | if era != nil { 90 | o.Era = *era 91 | } 92 | e := &ext 93 | //签名 94 | err := tx.signTx(e, o, privateKey, signType) 95 | if err != nil { 96 | return "", fmt.Errorf("sign error: %v", err) 97 | } 98 | return types.EncodeToHexString(e) 99 | } 100 | 101 | func (tx *SubstrateTransaction) signTx(e *types.Extrinsic, o types.SignatureOptions, privateKey string, signType int) error { 102 | if e.Type() != types.ExtrinsicVersion4 { 103 | return fmt.Errorf("unsupported extrinsic version: %v (isSigned: %v, type: %v)", e.Version, e.IsSigned(), e.Type()) 104 | } 105 | mb, err := types.EncodeToBytes(e.Method) 106 | if err != nil { 107 | return err 108 | } 109 | era := o.Era 110 | if !o.Era.IsMortalEra { 111 | era = types.ExtrinsicEra{IsImmortalEra: true} 112 | } 113 | payload := types.ExtrinsicPayloadV4{ 114 | ExtrinsicPayloadV3: types.ExtrinsicPayloadV3{ 115 | Method: mb, 116 | Era: era, 117 | Nonce: o.Nonce, 118 | Tip: o.Tip, 119 | SpecVersion: o.SpecVersion, 120 | GenesisHash: o.GenesisHash, 121 | BlockHash: o.BlockHash, 122 | }, 123 | TransactionVersion: o.TransactionVersion, 124 | } 125 | // sign 126 | data, err := types.EncodeToBytes(payload) 127 | if err != nil { 128 | return fmt.Errorf("encode payload error: %v", err) 129 | } 130 | // if data is longer than 256 bytes, hash it first 131 | if len(data) > 256 { 132 | h := blake2b.Sum256(data) 133 | data = h[:] 134 | } 135 | privateKey = strings.TrimPrefix(privateKey, "0x") 136 | priv, err := hex.DecodeString(privateKey) 137 | if err != nil { 138 | return fmt.Errorf("hex decode private key error: %v", err) 139 | } 140 | 141 | defer utils.ZeroBytes(priv) 142 | sig, err := crypto.Sign(priv, data, signType) 143 | if err != nil { 144 | return fmt.Errorf("sign error: %v", err) 145 | } 146 | var signerPubKey types.Address 147 | signerPubKey = types.NewAddressFromAccountID(types.MustHexDecodeString( 148 | tx.SenderPubkey)) 149 | //fmt.Println(hex.EncodeToString(sig)) 150 | var ss types.MultiSignature 151 | if signType == crypto.Ed25519Type { 152 | ss = types.MultiSignature{IsEd25519: true, AsEd25519: types.NewSignature(sig)} 153 | } else if signType == crypto.Sr25519Type { 154 | ss = types.MultiSignature{IsSr25519: true, AsSr25519: types.NewSignature(sig)} 155 | } else if signType == crypto.EcdsaType { 156 | ss = types.MultiSignature{IsEcdsa: true, AsEcdsa: types.NewBytes(sig)} 157 | } else { 158 | return fmt.Errorf("unsupport sign type : %d", signType) 159 | } 160 | extSig := types.ExtrinsicSignatureV4{ 161 | Signer: signerPubKey, 162 | Signature: ss, 163 | Era: era, 164 | Nonce: o.Nonce, 165 | Tip: o.Tip, 166 | } 167 | e.Signature = extSig 168 | e.Version |= types.ExtrinsicBitSigned 169 | return nil 170 | } 171 | func (tx *SubstrateTransaction) getEra() *types.ExtrinsicEra { 172 | if tx.BlockNumber == 0 || tx.EraPeriod == 0 { 173 | return nil 174 | } 175 | phase := tx.BlockNumber % tx.EraPeriod 176 | index := uint64(6) 177 | trailingZero := index - 1 178 | 179 | var encoded uint64 180 | if trailingZero > 1 { 181 | encoded = trailingZero 182 | } else { 183 | encoded = 1 184 | } 185 | 186 | if trailingZero < 15 { 187 | encoded = trailingZero 188 | } else { 189 | encoded = 15 190 | } 191 | encoded += phase / 1 << 4 192 | first := byte(encoded >> 8) 193 | second := byte(encoded & 0xff) 194 | era := new(types.ExtrinsicEra) 195 | era.IsMortalEra = true 196 | era.AsMortalEra.First = first 197 | era.AsMortalEra.Second = second 198 | return era 199 | } 200 | -------------------------------------------------------------------------------- /uint128/uint128.go: -------------------------------------------------------------------------------- 1 | package uint128 2 | 3 | import ( 4 | "encoding/binary" 5 | "math/big" 6 | "math/bits" 7 | ) 8 | 9 | // Zero is a zero-valued uint128. 10 | var Zero Uint128 11 | 12 | // A Uint128 is an unsigned 128-bit number. 13 | type Uint128 struct { 14 | lo, hi uint64 15 | } 16 | 17 | // IsZero returns true if u == 0. 18 | func (u Uint128) IsZero() bool { 19 | return u == Zero 20 | } 21 | 22 | // Equals returns true if u == v. 23 | // 24 | // Uint128 values can be compared directly with ==, but use of the Equals method 25 | // is preferred for consistency. 26 | func (u Uint128) Equals(v Uint128) bool { 27 | return u == v 28 | } 29 | 30 | // Equals64 returns true if u == v. 31 | func (u Uint128) Equals64(v uint64) bool { 32 | return u.lo == v && u.hi == 0 33 | } 34 | 35 | // Cmp compares u and v and returns: 36 | // 37 | // -1 if u < v 38 | // 0 if u == v 39 | // +1 if u > v 40 | // 41 | func (u Uint128) Cmp(v Uint128) int { 42 | if u == v { 43 | return 0 44 | } else if u.hi < v.hi || (u.hi == v.hi && u.lo < v.lo) { 45 | return -1 46 | } else { 47 | return 1 48 | } 49 | } 50 | 51 | // Cmp64 compares u and v and returns: 52 | // 53 | // -1 if u < v 54 | // 0 if u == v 55 | // +1 if u > v 56 | // 57 | func (u Uint128) Cmp64(v uint64) int { 58 | if u.hi > 0 || u.lo > v { 59 | return 1 60 | } else if u.lo == v { 61 | return 0 62 | } else { 63 | return -1 64 | } 65 | } 66 | 67 | // And returns u&v. 68 | func (u Uint128) And(v Uint128) Uint128 { 69 | return Uint128{u.lo & v.lo, u.hi & v.hi} 70 | } 71 | 72 | // And64 returns u&v. 73 | func (u Uint128) And64(v uint64) Uint128 { 74 | return Uint128{u.lo & v, u.hi & 0} 75 | } 76 | 77 | // Or returns u|v. 78 | func (u Uint128) Or(v Uint128) Uint128 { 79 | return Uint128{u.lo | v.lo, u.hi | v.hi} 80 | } 81 | 82 | // Or64 returns u|v. 83 | func (u Uint128) Or64(v uint64) Uint128 { 84 | return Uint128{u.lo | v, u.hi | 0} 85 | } 86 | 87 | // Xor returns u^v. 88 | func (u Uint128) Xor(v Uint128) Uint128 { 89 | return Uint128{u.lo ^ v.lo, u.hi ^ v.hi} 90 | } 91 | 92 | // Xor64 returns u^v. 93 | func (u Uint128) Xor64(v uint64) Uint128 { 94 | return Uint128{u.lo ^ v, u.hi ^ 0} 95 | } 96 | 97 | // Add returns u+v. 98 | func (u Uint128) Add(v Uint128) Uint128 { 99 | lo, carry := bits.Add64(u.lo, v.lo, 0) 100 | hi, _ := bits.Add64(u.hi, v.hi, carry) 101 | return Uint128{lo, hi} 102 | } 103 | 104 | // Add64 returns u+v. 105 | func (u Uint128) Add64(v uint64) Uint128 { 106 | lo, carry := bits.Add64(u.lo, v, 0) 107 | hi := u.hi + carry 108 | return Uint128{lo, hi} 109 | } 110 | 111 | // Sub returns u-v. 112 | func (u Uint128) Sub(v Uint128) Uint128 { 113 | lo, borrow := bits.Sub64(u.lo, v.lo, 0) 114 | hi, _ := bits.Sub64(u.hi, v.hi, borrow) 115 | return Uint128{lo, hi} 116 | } 117 | 118 | // Sub64 returns u-v. 119 | func (u Uint128) Sub64(v uint64) Uint128 { 120 | lo, borrow := bits.Sub64(u.lo, v, 0) 121 | hi := u.hi - borrow 122 | return Uint128{lo, hi} 123 | } 124 | 125 | // Mul returns u*v. 126 | func (u Uint128) Mul(v Uint128) Uint128 { 127 | hi, lo := bits.Mul64(u.lo, v.lo) 128 | hi += u.hi*v.lo + u.lo*v.hi 129 | return Uint128{lo, hi} 130 | } 131 | 132 | // Mul64 returns u*v. 133 | func (u Uint128) Mul64(v uint64) Uint128 { 134 | hi, lo := bits.Mul64(u.lo, v) 135 | hi += u.hi * v 136 | return Uint128{lo, hi} 137 | } 138 | 139 | // Div returns u/v. 140 | func (u Uint128) Div(v Uint128) Uint128 { 141 | q, _ := u.QuoRem(v) 142 | return q 143 | } 144 | 145 | // Div64 returns u/v. 146 | func (u Uint128) Div64(v uint64) Uint128 { 147 | q, _ := u.QuoRem64(v) 148 | return q 149 | } 150 | 151 | // QuoRem returns q = u/v and r = u%v. 152 | func (u Uint128) QuoRem(v Uint128) (q, r Uint128) { 153 | if v.hi == 0 { 154 | var r64 uint64 155 | q, r64 = u.QuoRem64(v.lo) 156 | r = From64(r64) 157 | } else { 158 | // generate a "trial quotient," guaranteed to be within 1 of the actual 159 | // quotient, then adjust. 160 | n := uint(bits.LeadingZeros64(v.hi)) 161 | v1 := v.Lsh(n) 162 | u1 := u.Rsh(1) 163 | tq, _ := bits.Div64(u1.hi, u1.lo, v1.hi) 164 | tq >>= 63 - n 165 | if tq != 0 { 166 | tq-- 167 | } 168 | q = From64(tq) 169 | // calculate remainder using trial quotient, then adjust if remainder is 170 | // greater than divisor 171 | r = u.Sub(v.Mul64(tq)) 172 | if r.Cmp(v) >= 0 { 173 | q = q.Add64(1) 174 | r = r.Sub(v) 175 | } 176 | } 177 | return 178 | } 179 | 180 | // QuoRem64 returns q = u/v and r = u%v. 181 | func (u Uint128) QuoRem64(v uint64) (q Uint128, r uint64) { 182 | if u.hi < v { 183 | q.lo, r = bits.Div64(u.hi, u.lo, v) 184 | } else { 185 | q.hi, r = bits.Div64(0, u.hi, v) 186 | q.lo, r = bits.Div64(r, u.lo, v) 187 | } 188 | return 189 | } 190 | 191 | // Lsh returns u< 64 { 194 | s.lo = 0 195 | s.hi = u.lo << (n - 64) 196 | } else { 197 | s.lo = u.lo << n 198 | s.hi = u.hi<>(64-n) 199 | } 200 | return 201 | } 202 | 203 | // Rsh returns u>>n. 204 | func (u Uint128) Rsh(n uint) (s Uint128) { 205 | if n > 64 { 206 | s.lo = u.hi >> (n - 64) 207 | s.hi = 0 208 | } else { 209 | s.lo = u.lo>>n | u.hi<<(64-n) 210 | s.hi = u.hi >> n 211 | } 212 | return 213 | } 214 | 215 | // String returns the base-10 representation of u as a string. 216 | func (u Uint128) String() string { 217 | if u.IsZero() { 218 | return "0" 219 | } 220 | buf := []byte("0000000000000000000000000000000000000000") // log10(2^128) < 40 221 | for i := len(buf); ; i -= 19 { 222 | q, r := u.QuoRem64(1e19) // largest power of 10 that fits in a uint64 223 | var n int 224 | for ; r != 0; r /= 10 { 225 | n++ 226 | buf[i-n] = '0' + byte(r%10) 227 | } 228 | if q.IsZero() { 229 | return string(buf[i-n:]) 230 | } 231 | u = q 232 | } 233 | } 234 | 235 | // PutBytes stores u in b in little-endian order. It panics if len(b) < 16. 236 | func (u Uint128) PutBytes(b []byte) { 237 | binary.LittleEndian.PutUint64(b[:8], u.lo) 238 | binary.LittleEndian.PutUint64(b[8:], u.hi) 239 | } 240 | 241 | // Big returns u as a *big.Int. 242 | func (u Uint128) Big() *big.Int { 243 | i := new(big.Int).SetUint64(u.hi) 244 | i = i.Lsh(i, 64) 245 | i = i.Xor(i, new(big.Int).SetUint64(u.lo)) 246 | return i 247 | } 248 | 249 | // New returns the Uint128 value (lo,hi). 250 | func New(lo, hi uint64) Uint128 { 251 | return Uint128{lo, hi} 252 | } 253 | 254 | // From64 converts v to a Uint128 value. 255 | func From64(v uint64) Uint128 { 256 | return New(v, 0) 257 | } 258 | 259 | // FromBytes converts b to a Uint128 value. 260 | func FromBytes(b []byte) Uint128 { 261 | return New( 262 | binary.LittleEndian.Uint64(b[:8]), 263 | binary.LittleEndian.Uint64(b[8:]), 264 | ) 265 | } 266 | 267 | // FromBig converts i to a Uint128 value. It panics if i is negative or 268 | // overflows 128 bits. 269 | func FromBig(i *big.Int) (u Uint128) { 270 | if i.Sign() < 0 { 271 | panic("value cannot be negative") 272 | } else if i.BitLen() > 128 { 273 | panic("value overflows Uint128") 274 | } 275 | u.lo = i.Uint64() 276 | u.hi = new(big.Int).Rsh(i, 64).Uint64() 277 | return u 278 | } 279 | -------------------------------------------------------------------------------- /utils/util.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/hex" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/JFJun/go-substrate-crypto/ss58" 8 | "github.com/stafiprotocol/go-substrate-rpc-client/types" 9 | "math/big" 10 | "strings" 11 | ) 12 | 13 | func RemoveHex0x(hexStr string)string{ 14 | if strings.HasPrefix(hexStr,"0x"){ 15 | return hexStr[2:] 16 | } 17 | return hexStr 18 | } 19 | 20 | func BytesToHex(b []byte) string { 21 | c := make([]byte, hex.EncodedLen(len(b))) 22 | hex.Encode(c, b) 23 | return string(c) 24 | } 25 | 26 | func U256(v string) *big.Int { 27 | v = strings.TrimPrefix(v, "0x") 28 | bn := new(big.Int) 29 | n, _ := bn.SetString(v, 16) 30 | return n 31 | } 32 | 33 | func UCompactToBigInt(u types.UCompact)*big.Int{ 34 | b:=big.Int(u) 35 | return &b 36 | } 37 | 38 | func IntInSlice(a int, list []int) bool { 39 | for _, b := range list { 40 | if b == a { 41 | return true 42 | } 43 | } 44 | return false 45 | } 46 | 47 | func IsNumberString(str string)bool{ 48 | for _,a:=range str{ 49 | if a >57 || a<48 { 50 | return false 51 | } 52 | } 53 | return true 54 | } 55 | func IntToHex(i interface{}) string { 56 | return fmt.Sprintf("%x", i) 57 | } 58 | 59 | func CheckStructData(object interface{}){ 60 | d,_:=json.Marshal(object) 61 | fmt.Println(string(d)) 62 | } 63 | 64 | func AddressToPublicKey(address string) string { 65 | if address == "" { 66 | return "" 67 | } 68 | pub, err := ss58.DecodeToPub(address) 69 | 70 | if err != nil { 71 | return "" 72 | } 73 | if len(pub) != 32 { 74 | return "" 75 | } 76 | pubHex := hex.EncodeToString(pub) 77 | return pubHex 78 | } 79 | 80 | func Remove0X(hexData string) string { 81 | if strings.HasPrefix(hexData, "0x") { 82 | return hexData[2:] 83 | } 84 | return hexData 85 | } 86 | 87 | func ZeroBytes(data []byte){ 88 | for i,_:=range data{ 89 | data[i] = 0 90 | } 91 | } 92 | 93 | --------------------------------------------------------------------------------