github.com/stafiprotocol/go-substrate-rpc-client@v1.4.7/rpc/rpcs_test.go (about) 1 package rpc_test 2 3 import ( 4 "fmt" 5 "math/big" 6 "testing" 7 "time" 8 9 "github.com/stafiprotocol/go-substrate-rpc-client/config" 10 "github.com/stafiprotocol/go-substrate-rpc-client/rpc" 11 "github.com/stafiprotocol/go-substrate-rpc-client/signature" 12 "github.com/stafiprotocol/go-substrate-rpc-client/types" 13 ) 14 15 func Example_simpleConnect() { 16 // The following example shows how to instantiate a Substrate API and use it to connect to a node 17 18 rpcs, err := rpc.NewRPCS(config.Default().RPCURL) 19 if err != nil { 20 panic(err) 21 } 22 23 chain, err := rpcs.System.Chain() 24 if err != nil { 25 panic(err) 26 } 27 nodeName, err := rpcs.System.Name() 28 if err != nil { 29 panic(err) 30 } 31 nodeVersion, err := rpcs.System.Version() 32 if err != nil { 33 panic(err) 34 } 35 36 fmt.Printf("You are connected to chain %v using %v v%v\n", chain, nodeName, nodeVersion) 37 38 // Output: You are connected to chain Development using Substrate Node v2.0.0-a200cdb9-x86_64-linux-gnu 39 } 40 41 func Example_listenToNewBlocks() { 42 // This example shows how to subscribe to new blocks. 43 // 44 // It displays the block number every time a new block is seen by the node you are connected to. 45 // 46 // NOTE: The example runs until 10 blocks are received or until you stop it with CTRL+C 47 48 rpcs, err := rpc.NewRPCS(config.Default().RPCURL) 49 if err != nil { 50 panic(err) 51 } 52 53 sub, err := rpcs.Chain.SubscribeNewHeads() 54 if err != nil { 55 panic(err) 56 } 57 defer sub.Unsubscribe() 58 59 count := 0 60 61 for { 62 head := <-sub.Chan() 63 fmt.Printf("Chain is at block: #%v\n", head.Number) 64 count++ 65 66 if count == 10 { 67 sub.Unsubscribe() 68 break 69 } 70 } 71 } 72 73 func Example_listenToBalanceChange() { 74 // This example shows how to instantiate a Substrate API and use it to connect to a node and retrieve balance 75 // updates 76 // 77 // NOTE: The example runs until you stop it with CTRL+C 78 79 rpcs, err := rpc.NewRPCS(config.Default().RPCURL) 80 if err != nil { 81 panic(err) 82 } 83 84 meta, err := rpcs.State.GetMetadataLatest() 85 if err != nil { 86 panic(err) 87 } 88 89 // Known account we want to use (available on dev chain, with funds) 90 alice, err := types.HexDecodeString("0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d") 91 if err != nil { 92 panic(err) 93 } 94 95 key, err := types.CreateStorageKey(meta, "Balances", "FreeBalance", alice, nil) 96 if err != nil { 97 panic(err) 98 } 99 100 // Retrieve the initial balance 101 var previous types.U128 102 ok, err := rpcs.State.GetStorageLatest(key, &previous) 103 if err != nil || !ok { 104 panic(err) 105 } 106 107 fmt.Printf("%#x has a balance of %v\n", alice, previous) 108 fmt.Printf("You may leave this example running and transfer any value to %#x\n", alice) 109 110 // Here we subscribe to any balance changes 111 sub, err := rpcs.State.SubscribeStorageRaw([]types.StorageKey{key}) 112 if err != nil { 113 panic(err) 114 } 115 defer sub.Unsubscribe() 116 117 // outer for loop for subscription notifications 118 for { 119 // inner loop for the changes within one of those notifications 120 for _, chng := range (<-sub.Chan()).Changes { 121 var current types.U128 122 if err = types.DecodeFromBytes(chng.StorageData, ¤t); err != nil { 123 panic(err) 124 } 125 126 // Calculate the delta 127 var change = types.U128{Int: big.NewInt(0).Sub(current.Int, previous.Int)} 128 129 // Only display positive value changes (Since we are pulling `previous` above already, 130 // the initial balance change will also be zero) 131 if change.Cmp(big.NewInt(0)) != 0 { 132 previous = current 133 fmt.Printf("New balance change of: %v\n", change) 134 return 135 } 136 } 137 } 138 } 139 140 func Example_unsubscribeFromListeningToUpdates() { 141 // This example shows how to subscribe to and later unsubscribe from listening to block updates. 142 // 143 // In this example we're calling the built-in unsubscribe() function after a timeOut of 20s to cleanup and 144 // unsubscribe from listening to updates. 145 146 rpcs, err := rpc.NewRPCS(config.Default().RPCURL) 147 if err != nil { 148 panic(err) 149 } 150 151 sub, err := rpcs.Chain.SubscribeNewHeads() 152 if err != nil { 153 panic(err) 154 } 155 defer sub.Unsubscribe() 156 157 timeout := time.After(20 * time.Second) 158 159 for { 160 select { 161 case head := <-sub.Chan(): 162 fmt.Printf("Chain is at block: #%v\n", head.Number) 163 case <-timeout: 164 sub.Unsubscribe() 165 fmt.Println("Unsubscribed") 166 return 167 } 168 } 169 } 170 171 func Example_makeASimpleTransfer() { 172 // This sample shows how to create a transaction to make a transfer from one an account to another. 173 174 // Instantiate the API 175 rpcs, err := rpc.NewRPCS(config.Default().RPCURL) 176 if err != nil { 177 panic(err) 178 } 179 180 meta, err := rpcs.State.GetMetadataLatest() 181 if err != nil { 182 panic(err) 183 } 184 185 // Create a call, transferring 12345 units to Bob 186 bob, err := types.NewAddressFromHexAccountID("0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48") 187 if err != nil { 188 panic(err) 189 } 190 191 c, err := types.NewCall(meta, "Balances.transfer", bob, types.NewUCompactFromUInt(12345)) 192 if err != nil { 193 panic(err) 194 } 195 196 // Create the extrinsic 197 ext := types.NewExtrinsic(c) 198 199 genesisHash, err := rpcs.Chain.GetBlockHash(0) 200 if err != nil { 201 panic(err) 202 } 203 204 rv, err := rpcs.State.GetRuntimeVersionLatest() 205 if err != nil { 206 panic(err) 207 } 208 209 key, err := types.CreateStorageKey(meta, "System", "Account", signature.TestKeyringPairAlice.PublicKey, nil) 210 if err != nil { 211 panic(err) 212 } 213 214 var accountInfo types.AccountInfo 215 ok, err := rpcs.State.GetStorageLatest(key, &accountInfo) 216 if err != nil || !ok { 217 panic(err) 218 } 219 220 nonce := uint32(accountInfo.Nonce) 221 222 o := types.SignatureOptions{ 223 BlockHash: genesisHash, 224 Era: types.ExtrinsicEra{IsMortalEra: false}, 225 GenesisHash: genesisHash, 226 Nonce: types.NewUCompactFromUInt(uint64(nonce)), 227 SpecVersion: rv.SpecVersion, 228 Tip: types.NewUCompactFromUInt(0), 229 } 230 231 // Sign the transaction using Alice's default account 232 err = ext.Sign(signature.TestKeyringPairAlice, o) 233 if err != nil { 234 panic(err) 235 } 236 237 // Send the extrinsic 238 hash, err := rpcs.Author.SubmitExtrinsic(ext) 239 if err != nil { 240 panic(err) 241 } 242 243 fmt.Printf("Transfer sent with hash %#x\n", hash) 244 } 245 246 func Example_displaySystemEvents() { 247 // Query the system events and extract information from them. This example runs until exited via Ctrl-C 248 249 // Create our API with a default connection to the local node 250 rpcs, err := rpc.NewRPCS(config.Default().RPCURL) 251 if err != nil { 252 panic(err) 253 } 254 meta, err := rpcs.State.GetMetadataLatest() 255 if err != nil { 256 panic(err) 257 } 258 259 // Subscribe to system events via storage 260 key, err := types.CreateStorageKey(meta, "System", "Events", nil, nil) 261 if err != nil { 262 panic(err) 263 } 264 265 sub, err := rpcs.State.SubscribeStorageRaw([]types.StorageKey{key}) 266 if err != nil { 267 panic(err) 268 } 269 defer sub.Unsubscribe() 270 271 // outer for loop for subscription notifications 272 for { 273 set := <-sub.Chan() 274 // inner loop for the changes within one of those notifications 275 for _, chng := range set.Changes { 276 if !types.Eq(chng.StorageKey, key) || !chng.HasStorageData { 277 // skip, we are only interested in events with content 278 continue 279 } 280 281 // Decode the event records 282 events := types.EventRecords{} 283 err = types.EventRecordsRaw(chng.StorageData).DecodeEventRecords(meta, &events) 284 if err != nil { 285 panic(err) 286 } 287 288 // Show what we are busy with 289 for _, e := range events.Balances_Endowed { 290 fmt.Printf("\tBalances:Endowed:: (phase=%#v)\n", e.Phase) 291 fmt.Printf("\t\t%#x, %v\n", e.Who, e.Balance) 292 } 293 for _, e := range events.Balances_DustLost { 294 fmt.Printf("\tBalances:DustLost:: (phase=%#v)\n", e.Phase) 295 fmt.Printf("\t\t%#x, %v\n", e.Who, e.Balance) 296 } 297 for _, e := range events.Balances_Transfer { 298 fmt.Printf("\tBalances:Transfer:: (phase=%#v)\n", e.Phase) 299 fmt.Printf("\t\t%v, %v, %v\n", e.From, e.To, e.Value) 300 } 301 for _, e := range events.Balances_BalanceSet { 302 fmt.Printf("\tBalances:BalanceSet:: (phase=%#v)\n", e.Phase) 303 fmt.Printf("\t\t%v, %v, %v\n", e.Who, e.Free, e.Reserved) 304 } 305 for _, e := range events.Balances_Deposit { 306 fmt.Printf("\tBalances:Deposit:: (phase=%#v)\n", e.Phase) 307 fmt.Printf("\t\t%v, %v\n", e.Who, e.Balance) 308 } 309 for _, e := range events.Grandpa_NewAuthorities { 310 fmt.Printf("\tGrandpa:NewAuthorities:: (phase=%#v)\n", e.Phase) 311 fmt.Printf("\t\t%v\n", e.NewAuthorities) 312 } 313 for _, e := range events.Grandpa_Paused { 314 fmt.Printf("\tGrandpa:Paused:: (phase=%#v)\n", e.Phase) 315 } 316 for _, e := range events.Grandpa_Resumed { 317 fmt.Printf("\tGrandpa:Resumed:: (phase=%#v)\n", e.Phase) 318 } 319 for _, e := range events.ImOnline_HeartbeatReceived { 320 fmt.Printf("\tImOnline:HeartbeatReceived:: (phase=%#v)\n", e.Phase) 321 fmt.Printf("\t\t%#x\n", e.AuthorityID) 322 } 323 for _, e := range events.ImOnline_AllGood { 324 fmt.Printf("\tImOnline:AllGood:: (phase=%#v)\n", e.Phase) 325 } 326 for _, e := range events.ImOnline_SomeOffline { 327 fmt.Printf("\tImOnline:SomeOffline:: (phase=%#v)\n", e.Phase) 328 fmt.Printf("\t\t%v\n", e.IdentificationTuples) 329 } 330 for _, e := range events.Indices_IndexAssigned { 331 fmt.Printf("\tIndices:IndexAssigned:: (phase=%#v)\n", e.Phase) 332 fmt.Printf("\t\t%#x%v\n", e.AccountID, e.AccountIndex) 333 } 334 for _, e := range events.Indices_IndexFreed { 335 fmt.Printf("\tIndices:IndexFreed:: (phase=%#v)\n", e.Phase) 336 fmt.Printf("\t\t%v\n", e.AccountIndex) 337 } 338 for _, e := range events.Offences_Offence { 339 fmt.Printf("\tOffences:Offence:: (phase=%#v)\n", e.Phase) 340 fmt.Printf("\t\t%v%v\n", e.Kind, e.OpaqueTimeSlot) 341 } 342 for _, e := range events.Session_NewSession { 343 fmt.Printf("\tSession:NewSession:: (phase=%#v)\n", e.Phase) 344 fmt.Printf("\t\t%v\n", e.SessionIndex) 345 } 346 for _, e := range events.Staking_Reward { 347 fmt.Printf("\tStaking:Reward:: (phase=%#v)\n", e.Phase) 348 fmt.Printf("\t\t%v\n", e.Amount) 349 } 350 for _, e := range events.Staking_Slash { 351 fmt.Printf("\tStaking:Slash:: (phase=%#v)\n", e.Phase) 352 fmt.Printf("\t\t%#x%v\n", e.AccountID, e.Balance) 353 } 354 for _, e := range events.Staking_OldSlashingReportDiscarded { 355 fmt.Printf("\tStaking:OldSlashingReportDiscarded:: (phase=%#v)\n", e.Phase) 356 fmt.Printf("\t\t%v\n", e.SessionIndex) 357 } 358 for _, e := range events.System_ExtrinsicSuccess { 359 fmt.Printf("\tSystem:ExtrinsicSuccess:: (phase=%#v)\n", e.Phase) 360 } 361 for _, e := range events.System_ExtrinsicFailed { 362 fmt.Printf("\tSystem:ErtrinsicFailed:: (phase=%#v)\n", e.Phase) 363 fmt.Printf("\t\t%v\n", e.DispatchError) 364 } 365 for _, e := range events.System_CodeUpdated { 366 fmt.Printf("\tSystem:CodeUpdated:: (phase=%#v)\n", e.Phase) 367 } 368 for _, e := range events.System_NewAccount { 369 fmt.Printf("\tSystem:NewAccount:: (phase=%#v)\n", e.Phase) 370 fmt.Printf("\t\t%#x\n", e.Who) 371 } 372 for _, e := range events.System_KilledAccount { 373 fmt.Printf("\tSystem:KilledAccount:: (phase=%#v)\n", e.Phase) 374 fmt.Printf("\t\t%#X\n", e.Who) 375 } 376 } 377 } 378 } 379 380 func Example_transactionWithEvents() { 381 // Display the events that occur during a transfer by sending a value to bob 382 383 // Instantiate the API 384 rpcs, err := rpc.NewRPCS(config.Default().RPCURL) 385 if err != nil { 386 panic(err) 387 } 388 389 meta, err := rpcs.State.GetMetadataLatest() 390 if err != nil { 391 panic(err) 392 } 393 394 // Create a call, transferring 12345 units to Bob 395 bob, err := types.NewAddressFromHexAccountID("0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48") 396 if err != nil { 397 panic(err) 398 } 399 400 amount := types.NewUCompactFromUInt(12345) 401 402 c, err := types.NewCall(meta, "Balances.transfer", bob, amount) 403 if err != nil { 404 panic(err) 405 } 406 407 // Create the extrinsic 408 ext := types.NewExtrinsic(c) 409 if err != nil { 410 panic(err) 411 } 412 413 genesisHash, err := rpcs.Chain.GetBlockHash(0) 414 if err != nil { 415 panic(err) 416 } 417 418 rv, err := rpcs.State.GetRuntimeVersionLatest() 419 if err != nil { 420 panic(err) 421 } 422 423 // Get the nonce for Alice 424 key, err := types.CreateStorageKey(meta, "System", "Account", signature.TestKeyringPairAlice.PublicKey, nil) 425 if err != nil { 426 panic(err) 427 } 428 429 var accountInfo types.AccountInfo 430 ok, err := rpcs.State.GetStorageLatest(key, &accountInfo) 431 if err != nil || !ok { 432 panic(err) 433 } 434 435 nonce := uint32(accountInfo.Nonce) 436 437 o := types.SignatureOptions{ 438 BlockHash: genesisHash, 439 Era: types.ExtrinsicEra{IsMortalEra: false}, 440 GenesisHash: genesisHash, 441 Nonce: types.NewUCompactFromUInt(uint64(nonce)), 442 SpecVersion: rv.SpecVersion, 443 Tip: types.NewUCompactFromUInt(0), 444 } 445 446 fmt.Printf("Sending %v from %#x to %#x with nonce %v", amount, signature.TestKeyringPairAlice.PublicKey, bob.AsAccountID, nonce) 447 448 // Sign the transaction using Alice's default account 449 err = ext.Sign(signature.TestKeyringPairAlice, o) 450 if err != nil { 451 panic(err) 452 } 453 454 // Do the transfer and track the actual status 455 sub, err := rpcs.Author.SubmitAndWatchExtrinsic(ext) 456 if err != nil { 457 panic(err) 458 } 459 defer sub.Unsubscribe() 460 461 for { 462 status := <-sub.Chan() 463 fmt.Printf("Transaction status: %#v\n", status) 464 465 if status.IsInBlock { 466 fmt.Printf("Completed at block hash: %#x\n", status.AsInBlock) 467 return 468 } 469 } 470 } 471 472 func TestV13(t *testing.T) { 473 url := "wss://kusama-rpc.polkadot.io" 474 rpcs, err := rpc.NewRPCS(url) 475 if err != nil { 476 panic(err) 477 } 478 479 genesisHash, err := rpcs.Chain.GetBlockHash(0) 480 if err != nil { 481 t.Fatal(err) 482 } 483 484 t.Log(genesisHash.Hex()) 485 } 486 487 type StakingLedger struct { 488 Stash types.AccountID 489 Total types.UCompact 490 Active types.UCompact 491 Unlocking []UnlockChunk 492 ClaimedRewards []uint32 493 } 494 495 type UnlockChunk struct { 496 Value types.UCompact 497 Era types.UCompact 498 } 499 500 func QueryStakingLeder(endpoint string, ac types.AccountID) (*StakingLedger, bool, error) { 501 rpcs, err := rpc.NewRPCS(config.Default().RPCURL) 502 if err != nil { 503 panic(err) 504 } 505 506 meta, err := rpcs.State.GetMetadataLatest() 507 if err != nil { 508 return nil, false, err 509 } 510 511 key, err := types.CreateStorageKey(meta, "Staking", "Ledger", ac[:], nil) 512 if err != nil { 513 return nil, false, err 514 } 515 516 ledger := new(StakingLedger) 517 ok, err := rpcs.State.GetStorageLatest(key, ledger) 518 if err != nil { 519 return nil, false, err 520 } 521 522 return ledger, ok, nil 523 }