├── .private.seed.example ├── .gitignore ├── assets ├── matrix-example-512.png ├── crunchbot-avatar-128.png ├── crunchbot-kusama-room.png ├── crunchbot-github-header.png ├── crunchbot-polkadot-room.png ├── crunchbot-westend-room.png ├── crunchbot-kusama-room-128.png ├── crunchbot-westend-room-128.png └── crunchbot-polkadot-room-128.png ├── .remote.stashes.example ├── packages ├── chains │ ├── relay-chain-paseo │ │ ├── metadata │ │ │ └── paseo_metadata_small.scale │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── people-paseo │ │ ├── metadata │ │ │ └── people_paseo_metadata_small.scale │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── relay-chain-kusama │ │ ├── metadata │ │ │ └── kusama_metadata_small.scale │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── people-kusama │ │ ├── metadata │ │ │ └── people_kusama_metadata_small.scale │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── relay-chain-westend │ │ ├── metadata │ │ │ └── westend_metadata_small.scale │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── asset-hub-paseo │ │ ├── metadata │ │ │ └── asset_hub_paseo_metadata_small.scale │ │ └── Cargo.toml │ ├── people-polkadot │ │ ├── metadata │ │ │ └── people_polkadot_metadata_small.scale │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── people-westend │ │ ├── metadata │ │ │ └── people_westend_metadata_small.scale │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── relay-chain-polkadot │ │ ├── metadata │ │ │ └── polkadot_metadata_small.scale │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── asset-hub-kusama │ │ ├── metadata │ │ │ └── asset_hub_kusama_metadata_small.scale │ │ └── Cargo.toml │ ├── asset-hub-westend │ │ ├── metadata │ │ │ └── asset_hub_westend_metadata_small.scale │ │ └── Cargo.toml │ ├── asset-hub-polkadot │ │ ├── metadata │ │ │ └── asset_hub_polkadot_metadata_small.scale │ │ └── Cargo.toml │ ├── paseo │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── kusama │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── westend │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── polkadot │ │ └── Cargo.toml ├── stats │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── pools │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── support │ ├── Cargo.toml │ ├── chain-specs │ │ ├── people-polkadot.json │ │ ├── people-kusama.json │ │ ├── people-paseo.json │ │ ├── asset-hub-paseo.json │ │ ├── people-westend.json │ │ ├── asset-hub-polkadot.json │ │ ├── asset-hub-kusama.json │ │ └── asset-hub-westend.json │ └── src │ │ └── lib.rs ├── config │ └── Cargo.toml ├── report │ └── Cargo.toml ├── error │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── matrix │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ └── error.rs ├── core │ └── Cargo.toml └── crunch │ ├── Cargo.toml │ └── src │ └── main.rs ├── docker-compose.yaml ├── .dockerignore ├── Dockerfile ├── Dockerfile.alpine ├── .github └── workflows │ ├── release_bot.yml │ └── create_release.yml ├── .rustfmt.toml ├── Cargo.toml ├── crunch-update.sh ├── update-metadata.sh ├── .env.example └── LICENSE /.private.seed.example: -------------------------------------------------------------------------------- 1 | write your private seed here to sign the transactions -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.vscode 3 | .env 4 | .private.seed 5 | bump-version.sh 6 | -------------------------------------------------------------------------------- /assets/matrix-example-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/assets/matrix-example-512.png -------------------------------------------------------------------------------- /assets/crunchbot-avatar-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/assets/crunchbot-avatar-128.png -------------------------------------------------------------------------------- /assets/crunchbot-kusama-room.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/assets/crunchbot-kusama-room.png -------------------------------------------------------------------------------- /assets/crunchbot-github-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/assets/crunchbot-github-header.png -------------------------------------------------------------------------------- /assets/crunchbot-polkadot-room.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/assets/crunchbot-polkadot-room.png -------------------------------------------------------------------------------- /assets/crunchbot-westend-room.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/assets/crunchbot-westend-room.png -------------------------------------------------------------------------------- /assets/crunchbot-kusama-room-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/assets/crunchbot-kusama-room-128.png -------------------------------------------------------------------------------- /assets/crunchbot-westend-room-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/assets/crunchbot-westend-room-128.png -------------------------------------------------------------------------------- /assets/crunchbot-polkadot-room-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/assets/crunchbot-polkadot-room-128.png -------------------------------------------------------------------------------- /.remote.stashes.example: -------------------------------------------------------------------------------- 1 | 5C556QTtg1bJ43GDSgeowa3Ark6aeSHGTac1b2rKSXtgmSmW 2 | 5GTD7ZeD823BjpmZBCSzBQp7cvHR1Gunq7oDkurZr9zUev2n 3 | 5FUJHYEzKpVJfNbtXmR9HFqmcSEz6ak7ZUhBECz7GpsFkSYR -------------------------------------------------------------------------------- /packages/chains/relay-chain-paseo/metadata/paseo_metadata_small.scale: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/packages/chains/relay-chain-paseo/metadata/paseo_metadata_small.scale -------------------------------------------------------------------------------- /packages/chains/people-paseo/metadata/people_paseo_metadata_small.scale: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/packages/chains/people-paseo/metadata/people_paseo_metadata_small.scale -------------------------------------------------------------------------------- /packages/chains/relay-chain-kusama/metadata/kusama_metadata_small.scale: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/packages/chains/relay-chain-kusama/metadata/kusama_metadata_small.scale -------------------------------------------------------------------------------- /packages/chains/people-kusama/metadata/people_kusama_metadata_small.scale: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/packages/chains/people-kusama/metadata/people_kusama_metadata_small.scale -------------------------------------------------------------------------------- /packages/chains/relay-chain-westend/metadata/westend_metadata_small.scale: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/packages/chains/relay-chain-westend/metadata/westend_metadata_small.scale -------------------------------------------------------------------------------- /packages/chains/asset-hub-paseo/metadata/asset_hub_paseo_metadata_small.scale: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/packages/chains/asset-hub-paseo/metadata/asset_hub_paseo_metadata_small.scale -------------------------------------------------------------------------------- /packages/chains/people-polkadot/metadata/people_polkadot_metadata_small.scale: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/packages/chains/people-polkadot/metadata/people_polkadot_metadata_small.scale -------------------------------------------------------------------------------- /packages/chains/people-westend/metadata/people_westend_metadata_small.scale: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/packages/chains/people-westend/metadata/people_westend_metadata_small.scale -------------------------------------------------------------------------------- /packages/chains/relay-chain-polkadot/metadata/polkadot_metadata_small.scale: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/packages/chains/relay-chain-polkadot/metadata/polkadot_metadata_small.scale -------------------------------------------------------------------------------- /packages/chains/asset-hub-kusama/metadata/asset_hub_kusama_metadata_small.scale: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/packages/chains/asset-hub-kusama/metadata/asset_hub_kusama_metadata_small.scale -------------------------------------------------------------------------------- /packages/chains/asset-hub-westend/metadata/asset_hub_westend_metadata_small.scale: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/packages/chains/asset-hub-westend/metadata/asset_hub_westend_metadata_small.scale -------------------------------------------------------------------------------- /packages/chains/asset-hub-polkadot/metadata/asset_hub_polkadot_metadata_small.scale: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboflakes/crunch/HEAD/packages/chains/asset-hub-polkadot/metadata/asset_hub_polkadot_metadata_small.scale -------------------------------------------------------------------------------- /packages/stats/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-stats" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | crunch: 4 | image: localhost/crunch:latest 5 | volumes: 6 | - ./.env:/.env 7 | - ./.private.seed:/.private.seed 8 | command: --config-path /.env polkadot rewards daily -f /.private.seed 9 | -------------------------------------------------------------------------------- /packages/pools/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-pools" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | hex = { workspace = true } 12 | subxt = { workspace = true } 13 | sp-core = { workspace = true } 14 | -------------------------------------------------------------------------------- /packages/support/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-support" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-config = { path = "../config" } 12 | 13 | serde_json = { workspace = true } 14 | subxt = { workspace = true } 15 | -------------------------------------------------------------------------------- /packages/chains/relay-chain-kusama/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-relay-chain-kusama" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-core = { path = "../../core" } 12 | crunch-error = { path = "../../error" } 13 | 14 | subxt = { workspace = true } 15 | -------------------------------------------------------------------------------- /packages/chains/relay-chain-paseo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-relay-chain-paseo" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-core = { path = "../../core" } 12 | crunch-error = { path = "../../error" } 13 | 14 | subxt = { workspace = true } 15 | -------------------------------------------------------------------------------- /packages/chains/relay-chain-polkadot/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-relay-chain-polkadot" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-core = { path = "../../core" } 12 | crunch-error = { path = "../../error" } 13 | 14 | subxt = { workspace = true } 15 | -------------------------------------------------------------------------------- /packages/chains/relay-chain-westend/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-relay-chain-westend" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-core = { path = "../../core" } 12 | crunch-error = { path = "../../error" } 13 | 14 | subxt = { workspace = true } 15 | -------------------------------------------------------------------------------- /packages/config/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-config" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | clap = { workspace = true } 12 | dotenv = { workspace = true } 13 | envy = { workspace = true } 14 | lazy_static = { workspace = true } 15 | log = { workspace = true } 16 | serde = { workspace = true } 17 | -------------------------------------------------------------------------------- /packages/report/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-report" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-core = { path = "../core" } 12 | crunch-config = { path = "../config" } 13 | crunch-stats = { path = "../stats" } 14 | 15 | log = { workspace = true } 16 | rand = { workspace = true } 17 | regex = { workspace = true } 18 | subxt = { workspace = true } 19 | -------------------------------------------------------------------------------- /packages/chains/people-paseo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-people-paseo" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-core = { path = "../../core" } 12 | crunch-error = { path = "../../error" } 13 | crunch-report = { path = "../../report" } 14 | 15 | async-recursion = { workspace = true } 16 | log = { workspace = true } 17 | subxt = { workspace = true } 18 | -------------------------------------------------------------------------------- /packages/chains/people-kusama/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-people-kusama" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-core = { path = "../../core" } 12 | crunch-error = { path = "../../error" } 13 | crunch-report = { path = "../../report" } 14 | 15 | async-recursion = { workspace = true } 16 | log = { workspace = true } 17 | subxt = { workspace = true } 18 | -------------------------------------------------------------------------------- /packages/chains/people-westend/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-people-westend" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-core = { path = "../../core" } 12 | crunch-error = { path = "../../error" } 13 | crunch-report = { path = "../../report" } 14 | 15 | async-recursion = { workspace = true } 16 | log = { workspace = true } 17 | subxt = { workspace = true } 18 | -------------------------------------------------------------------------------- /packages/chains/people-polkadot/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-people-polkadot" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-core = { path = "../../core" } 12 | crunch-error = { path = "../../error" } 13 | crunch-report = { path = "../../report" } 14 | 15 | async-recursion = { workspace = true } 16 | log = { workspace = true } 17 | subxt = { workspace = true } 18 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Rust build artifacts 2 | target/ 3 | **/*.rs.bk 4 | *.pdb 5 | 6 | # IDE directories 7 | .vscode/ 8 | .idea/ 9 | 10 | # Git 11 | .git/ 12 | .gitignore 13 | 14 | # Docker files (don't need to copy these into the context) 15 | Dockerfile 16 | Dockerfile.alpine 17 | docker-compose.yaml 18 | .dockerignore 19 | 20 | # Documentation and scripts (not needed for building) 21 | README.md 22 | CHANGELOG.md 23 | bump-version.sh 24 | crunch-update.sh 25 | update-metadata.sh 26 | 27 | # Deprecated directories 28 | DEPRECATED_* 29 | 30 | # GitHub 31 | .github/ 32 | -------------------------------------------------------------------------------- /packages/error/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-error" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-matrix = { path = "../matrix" } 12 | 13 | codec = { workspace = true } 14 | url = { workspace = true } 15 | serde = { workspace = true } 16 | serde_json = { workspace = true } 17 | subxt = { workspace = true } 18 | subxt-signer = { workspace = true } 19 | reqwest = { workspace = true } 20 | thiserror = { workspace = true } 21 | 22 | [target.x86_64-unknown-linux-musl.dependencies] 23 | openssl = { version = "0.10", features = ["vendored"] } 24 | -------------------------------------------------------------------------------- /packages/chains/asset-hub-kusama/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-asset-hub-kusama" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-core = { path = "../../core" } 12 | crunch-config = { path = "../../config" } 13 | crunch-error = { path = "../../error" } 14 | crunch-pools = { path = "../../pools" } 15 | crunch-report = { path = "../../report" } 16 | crunch-stats = { path = "../../stats" } 17 | crunch-people-kusama = { path = "../people-kusama" } 18 | 19 | log = { workspace = true } 20 | subxt = { workspace = true } 21 | subxt-signer = { workspace = true } 22 | -------------------------------------------------------------------------------- /packages/chains/asset-hub-paseo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-asset-hub-paseo" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-core = { path = "../../core" } 12 | crunch-config = { path = "../../config" } 13 | crunch-error = { path = "../../error" } 14 | crunch-pools = { path = "../../pools" } 15 | crunch-report = { path = "../../report" } 16 | crunch-stats = { path = "../../stats" } 17 | crunch-people-paseo = { path = "../people-paseo" } 18 | 19 | log = { workspace = true } 20 | subxt = { workspace = true } 21 | subxt-signer = { workspace = true } 22 | -------------------------------------------------------------------------------- /packages/chains/asset-hub-westend/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-asset-hub-westend" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-core = { path = "../../core" } 12 | crunch-config = { path = "../../config" } 13 | crunch-error = { path = "../../error" } 14 | crunch-pools = { path = "../../pools" } 15 | crunch-report = { path = "../../report" } 16 | crunch-stats = { path = "../../stats" } 17 | crunch-people-westend = { path = "../people-westend" } 18 | 19 | log = { workspace = true } 20 | subxt = { workspace = true } 21 | subxt-signer = { workspace = true } 22 | -------------------------------------------------------------------------------- /packages/chains/asset-hub-polkadot/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-asset-hub-polkadot" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-core = { path = "../../core" } 12 | crunch-config = { path = "../../config" } 13 | crunch-error = { path = "../../error" } 14 | crunch-pools = { path = "../../pools" } 15 | crunch-report = { path = "../../report" } 16 | crunch-stats = { path = "../../stats" } 17 | crunch-people-polkadot = { path = "../people-polkadot" } 18 | 19 | log = { workspace = true } 20 | subxt = { workspace = true } 21 | subxt-signer = { workspace = true } 22 | -------------------------------------------------------------------------------- /packages/matrix/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-matrix" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-config = { path = "../config" } 12 | crunch-support = { path = "../support" } 13 | 14 | async-recursion = { workspace = true } 15 | base64 = { workspace = true } 16 | log = { workspace = true } 17 | reqwest = { workspace = true } 18 | serde = { workspace = true } 19 | serde_json = { workspace = true } 20 | subxt = { workspace = true } 21 | thiserror = { workspace = true } 22 | url = { workspace = true } 23 | 24 | [target.x86_64-unknown-linux-musl.dependencies] 25 | openssl = { version = "0.10", features = ["vendored"] } 26 | -------------------------------------------------------------------------------- /packages/chains/paseo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-paseo" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-core = { path = "../../core" } 12 | crunch-config = { path = "../../config" } 13 | crunch-error = { path = "../../error" } 14 | crunch-report = { path = "../../report" } 15 | crunch-stats = { path = "../../stats" } 16 | crunch-asset-hub-paseo = { path = "../asset-hub-paseo" } 17 | crunch-people-paseo = { path = "../people-paseo" } 18 | crunch-relay-chain-paseo = { path = "../relay-chain-paseo" } 19 | 20 | async-recursion = { workspace = true } 21 | log = { workspace = true } 22 | subxt = { workspace = true } 23 | subxt-signer = { workspace = true } 24 | -------------------------------------------------------------------------------- /packages/chains/kusama/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-kusama" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-core = { path = "../../core" } 12 | crunch-config = { path = "../../config" } 13 | crunch-error = { path = "../../error" } 14 | crunch-report = { path = "../../report" } 15 | crunch-stats = { path = "../../stats" } 16 | crunch-asset-hub-kusama = { path = "../asset-hub-kusama" } 17 | crunch-people-kusama = { path = "../people-kusama" } 18 | crunch-relay-chain-kusama = { path = "../relay-chain-kusama" } 19 | 20 | async-recursion = { workspace = true } 21 | log = { workspace = true } 22 | subxt = { workspace = true } 23 | subxt-signer = { workspace = true } 24 | -------------------------------------------------------------------------------- /packages/chains/westend/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-westend" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-core = { path = "../../core" } 12 | crunch-config = { path = "../../config" } 13 | crunch-error = { path = "../../error" } 14 | crunch-report = { path = "../../report" } 15 | crunch-stats = { path = "../../stats" } 16 | crunch-asset-hub-westend = { path = "../asset-hub-westend" } 17 | crunch-people-westend = { path = "../people-westend" } 18 | crunch-relay-chain-westend = { path = "../relay-chain-westend" } 19 | 20 | async-recursion = { workspace = true } 21 | log = { workspace = true } 22 | subxt = { workspace = true } 23 | subxt-signer = { workspace = true } 24 | -------------------------------------------------------------------------------- /packages/chains/polkadot/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-polkadot" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-core = { path = "../../core" } 12 | crunch-config = { path = "../../config" } 13 | crunch-error = { path = "../../error" } 14 | crunch-report = { path = "../../report" } 15 | crunch-stats = { path = "../../stats" } 16 | crunch-asset-hub-polkadot = { path = "../asset-hub-polkadot" } 17 | crunch-people-polkadot = { path = "../people-polkadot" } 18 | crunch-relay-chain-polkadot = { path = "../relay-chain-polkadot" } 19 | 20 | async-recursion = { workspace = true } 21 | log = { workspace = true } 22 | subxt = { workspace = true } 23 | subxt-signer = { workspace = true } 24 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:jammy AS builder 2 | 3 | ARG PROFILE=release 4 | 5 | RUN apt-get update \ 6 | && apt-get -y install build-essential curl libssl-dev pkg-config \ 7 | && rm -rf /var/lib/apt/lists/* 8 | RUN curl https://sh.rustup.rs -sSf | sh -s -- -y 9 | RUN /root/.cargo/bin/rustup update 10 | 11 | COPY . /app 12 | WORKDIR /app 13 | RUN /root/.cargo/bin/cargo build --$PROFILE --package crunch 14 | 15 | # ===== SECOND STAGE ====== 16 | FROM ubuntu:jammy 17 | 18 | RUN apt-get update \ 19 | && apt-get -y install ca-certificates \ 20 | && rm -rf /var/lib/apt/lists/* 21 | 22 | ARG PROFILE=release 23 | COPY --from=builder /app/target/$PROFILE/crunch /usr/local/bin 24 | 25 | RUN useradd -u 1000 -U -s /bin/sh crunch 26 | USER crunch 27 | 28 | ENV RUST_BACKTRACE=1 29 | ENV RUST_LOG="info" 30 | 31 | RUN /usr/local/bin/crunch --version 32 | 33 | ENTRYPOINT [ "/usr/local/bin/crunch" ] 34 | -------------------------------------------------------------------------------- /Dockerfile.alpine: -------------------------------------------------------------------------------- 1 | FROM alpine:3.19 2 | 3 | # Install required dependencies 4 | RUN apk add --no-cache wget ca-certificates jq bash libc6-compat 5 | 6 | WORKDIR /app 7 | 8 | # Download the latest alpine release 9 | RUN set -x && \ 10 | LATEST_RELEASE=$(wget -qO- https://api.github.com/repos/turboflakes/crunch/releases/latest | jq -r '.tag_name') && \ 11 | echo "Latest release: ${LATEST_RELEASE}" && \ 12 | wget -q https://github.com/turboflakes/crunch/releases/download/${LATEST_RELEASE}/crunch.linux-musl -O /usr/local/bin/crunch && \ 13 | chmod +x /usr/local/bin/crunch 14 | 15 | # Create a non-root user to run the application 16 | RUN addgroup -S crunch && adduser -S -G crunch -s /bin/sh crunch 17 | USER crunch 18 | 19 | # Set environment variables 20 | ENV RUST_BACKTRACE=1 21 | ENV RUST_LOG="info" 22 | 23 | # Verify installation 24 | RUN /usr/local/bin/crunch --version 25 | 26 | # Set the entrypoint 27 | ENTRYPOINT ["/usr/local/bin/crunch"] 28 | -------------------------------------------------------------------------------- /packages/core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch-core" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-config = { path = "../config" } 12 | crunch-matrix = { path = "../matrix" } 13 | crunch-support = { path = "../support" } 14 | crunch-error = { path = "../error" } 15 | 16 | async-std = { workspace = true } 17 | env_logger = { workspace = true } 18 | hex = { workspace = true } 19 | log = { workspace = true } 20 | serde = { workspace = true } 21 | serde_json = { workspace = true } 22 | sp-core = { workspace = true } 23 | subxt = { workspace = true } 24 | subxt-signer = { workspace = true } 25 | rand = { workspace = true } 26 | regex = { workspace = true } 27 | reqwest = { workspace = true } 28 | 29 | [target.x86_64-unknown-linux-musl.dependencies] 30 | openssl = { version = "0.10", features = ["vendored"] } 31 | -------------------------------------------------------------------------------- /packages/support/chain-specs/people-polkadot.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Polkadot People", 3 | "id": "people-polkadot", 4 | "chainType": "Live", 5 | "bootNodes": [ 6 | "/dns/polkadot-people-connect-0.polkadot.io/tcp/443/wss/p2p/12D3KooWP7BoJ7nAF9QnsreN8Eft1yHNUhvhxFiQyKFEUePi9mu3", 7 | "/dns/polkadot-people-connect-1.polkadot.io/tcp/443/wss/p2p/12D3KooWSSfWY3fTGJvGkuNUNBSNVCdLLNJnwkZSNQt7GCRYXu4o", 8 | "/dns/people-polkadot-bootnode.turboflakes.io/tcp/30740/wss/p2p/12D3KooWBpZs5BE7yozig2HhfNqhy6uuq2cirFtEY2T13gF7m5hf" 9 | ], 10 | "telemetryEndpoints": null, 11 | "protocolId": null, 12 | "properties": { 13 | "ss58Format": 0, 14 | "tokenDecimals": 10, 15 | "tokenSymbol": "DOT" 16 | }, 17 | "relay_chain": "polkadot", 18 | "para_id": 1004, 19 | "codeSubstitutes": {}, 20 | "genesis": { 21 | "stateRootHash": "0x7a62a14ebf9b2e86292593414d58818324acc5701ea369734c0074f7c962bc0f" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.github/workflows/release_bot.yml: -------------------------------------------------------------------------------- 1 | name: Matrix - Push Notification 2 | on: 3 | release: 4 | types: 5 | - published 6 | jobs: 7 | ping_matrix: 8 | strategy: 9 | matrix: 10 | channel: 11 | - '!GXvEBJwHIZLDETyjJY:matrix.org' # #westend-crunch-bot:matrix.org -> Westend Crunch Bot (Public) 12 | - '!gCVSnDNPSDmUBxnDte:matrix.org' # #kusama-crunch-bot:matrix.org -> Kusama Crunch Bot (Public) 13 | - '!IdOnZRytzQTJTyZtQf:matrix.org' # #polkadot-crunch-bot:matrix.org -> Polkadot Crunch Bot (Public) 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: s3krit/matrix-message-action@v0.0.3 17 | with: 18 | room_id: ${{ matrix.channel }} 19 | access_token: ${{ secrets.MATRIX_ACCESS_TOKEN }} 20 | message: "

Hey, crunch ${{github.event.release.tag_name}} has been released ✌️

🔖 ${{github.event.release.html_url}}

@room
${{github.event.release.body}}

" 21 | server: "matrix.org" -------------------------------------------------------------------------------- /packages/support/chain-specs/people-kusama.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Kusama People", 3 | "id": "people-kusama", 4 | "chainType": "Live", 5 | "bootNodes": [ 6 | "/dns/kusama-people-connect-0.polkadot.io/tcp/443/wss/p2p/12D3KooWQaqG5TNmDfRWrtH7tMsN7YeqwVkSfoZT4GkemSzezNi1", 7 | "/dns/kusama-people-connect-1.polkadot.io/tcp/443/wss/p2p/12D3KooWKhYoQH9LdSyvY3SVZY9gFf6ZV1bFh6317TRehUP3r5fm", 8 | "/dns/people-kusama.bootnode.amforc.com/tcp/29999/wss/p2p/12D3KooWPjzgKZe5jdG6TY4gwcFq8QxyyhqsYbQo6N29pwGePWLA", 9 | "/dns/people-kusama-bootnode.turboflakes.io/tcp/30745/wss/p2p/12D3KooWCR2Q8J2NFFfuofDak4zSgWkuBq7orP96HFaxLgAoDUBV" 10 | ], 11 | "telemetryEndpoints": null, 12 | "protocolId": null, 13 | "properties": { 14 | "ss58Format": 2, 15 | "tokenDecimals": 12, 16 | "tokenSymbol": "KSM" 17 | }, 18 | "relay_chain": "ksmcc3", 19 | "para_id": 1004, 20 | "codeSubstitutes": {}, 21 | "genesis": { 22 | "stateRootHash": "0xd83cfbf48e85ab66eb53b647aff8cd75954af93b127467b31d07a21dd06cea38" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/crunch/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "crunch" 3 | version.workspace = true 4 | license.workspace = true 5 | repository.workspace = true 6 | authors.workspace = true 7 | description.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | crunch-core = { path = "../core" } 12 | crunch-config = { path = "../config" } 13 | crunch-error = { path = "../error" } 14 | crunch-matrix = { path = "../matrix" } 15 | crunch-support = { path = "../support" } 16 | crunch-kusama = { path = "../chains/kusama" } 17 | crunch-paseo = { path = "../chains/paseo" } 18 | crunch-polkadot = { path = "../chains/polkadot" } 19 | crunch-westend = { path = "../chains/westend" } 20 | 21 | async-std = { workspace = true } 22 | env_logger = { workspace = true } 23 | hex = { workspace = true } 24 | log = { workspace = true } 25 | serde = { workspace = true } 26 | serde_json = { workspace = true } 27 | sp-core = { workspace = true } 28 | subxt = { workspace = true } 29 | subxt-signer = { workspace = true } 30 | rand = { workspace = true } 31 | regex = { workspace = true } 32 | reqwest = { workspace = true } 33 | 34 | [target.x86_64-unknown-linux-musl.dependencies] 35 | openssl = { version = "0.10", features = ["vendored"] } 36 | -------------------------------------------------------------------------------- /packages/matrix/src/lib.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Aukbit Ltd. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | pub mod error; 23 | pub mod matrix; 24 | 25 | pub use matrix::Matrix; 26 | -------------------------------------------------------------------------------- /packages/support/chain-specs/people-paseo.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Paseo People", 3 | "id": "people-paseo", 4 | "chainType": "Live", 5 | "bootNodes": [ 6 | "/dns/boot.metaspan.io/tcp/10853/p2p/12D3KooWSM9tHdhSCS5YrtYZrMb9Kst1fUsBAJZkFjDHdt9bBk6A", 7 | "/dns/boot.metaspan.io/tcp/10856/wss/p2p/12D3KooWSM9tHdhSCS5YrtYZrMb9Kst1fUsBAJZkFjDHdt9bBk6A", 8 | "/dns/boot-node.helikon.io/tcp/10410/p2p/12D3KooWA3z8dVdADkDTN4xgpTkDo3AtsTq6o8nn7Xx54GjXXBgK", 9 | "/dns/boot-node.helikon.io/tcp/10412/wss/p2p/12D3KooWA3z8dVdADkDTN4xgpTkDo3AtsTq6o8nn7Xx54GjXXBgK", 10 | "/dns/people-paseo-bootnode.turboflakes.io/tcp/30840/p2p/12D3KooWPHNzviPuVsEsWNQQYL9gvfQ4BATKzYik3bh2p95pqvAJ", 11 | "/dns/people-paseo-bootnode.turboflakes.io/tcp/30440/wss/p2p/12D3KooWPHNzviPuVsEsWNQQYL9gvfQ4BATKzYik3bh2p95pqvAJ", 12 | "/dns/ibp-boot-people-paseo.luckyfriday.io/tcp/30339/p2p/12D3KooWL2M7HjQrvwEVw4EVr6y8LcggToyc9bgKN3LCsaq4UBq2", 13 | "/dns/ibp-boot-people-paseo.luckyfriday.io/tcp/443/wss/p2p/12D3KooWL2M7HjQrvwEVw4EVr6y8LcggToyc9bgKN3LCsaq4UBq2", 14 | "/dns/people-paseo.boot.stake.plus/tcp/30332/wss/p2p/12D3KooWPsVTRqGzYaJcR2JhZi3iBktegVBWvt9XaQbrVM3k6D9L", 15 | "/dns/people-paseo.boot.stake.plus/tcp/31332/wss/p2p/12D3KooWDVh1Srn7RHsu2wMN6tRjZ1DJACPMbkCcKo2ajvmENi3w" 16 | ], 17 | "telemetryEndpoints": null, 18 | "protocolId": null, 19 | "properties": { 20 | "ss58Format": 0, 21 | "tokenDecimals": 10, 22 | "tokenSymbol": "PAS" 23 | }, 24 | "relay_chain": "paseo", 25 | "para_id": 1004, 26 | "codeSubstitutes": {}, 27 | "genesis": { 28 | "stateRootHash": "0x2f6b718c1d9f0a87856807ce2540d4b90e55c5465b55cfdcdf721918a80a5779" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/matrix/src/error.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Aukbit Ltd. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | use std::string::String; 23 | use thiserror::Error; 24 | 25 | /// Matrix specific error messages 26 | #[derive(Error, Debug)] 27 | pub enum MatrixError { 28 | #[error("Reqwest error: {0}")] 29 | ReqwestError(#[from] reqwest::Error), 30 | #[error("ParseError error: {0}")] 31 | ParseError(#[from] url::ParseError), 32 | #[error("SerdeError error: {0}")] 33 | SerdeError(#[from] serde_json::Error), 34 | #[error("IOError error: {0}")] 35 | IOError(#[from] std::io::Error), 36 | #[error("{0}")] 37 | Other(String), 38 | } 39 | 40 | /// Convert MatrixError to String 41 | impl From for String { 42 | fn from(error: MatrixError) -> Self { 43 | format!("{}", error).to_string() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 90 # changed 2 | hard_tabs = false 3 | tab_spaces = 4 4 | newline_style = "Auto" 5 | use_small_heuristics = "Default" 6 | indent_style = "Block" 7 | wrap_comments = false 8 | format_code_in_doc_comments = false 9 | comment_width = 80 10 | normalize_comments = true # changed 11 | normalize_doc_attributes = false 12 | license_template_path = "FILE_TEMPLATE" # changed 13 | format_strings = false 14 | format_macro_matchers = false 15 | format_macro_bodies = true 16 | empty_item_single_line = true 17 | struct_lit_single_line = true 18 | fn_single_line = false 19 | where_single_line = false 20 | imports_indent = "Block" 21 | imports_layout = "Vertical" # changed 22 | imports_granularity = "Crate" # changed 23 | reorder_imports = true 24 | reorder_modules = true 25 | reorder_impl_items = false 26 | type_punctuation_density = "Wide" 27 | space_before_colon = false 28 | space_after_colon = true 29 | spaces_around_ranges = false 30 | binop_separator = "Front" 31 | remove_nested_parens = true 32 | combine_control_expr = false # changed 33 | overflow_delimited_expr = false 34 | struct_field_align_threshold = 0 35 | enum_discrim_align_threshold = 0 36 | match_arm_blocks = true 37 | force_multiline_blocks = true # changed 38 | fn_args_layout = "Tall" 39 | brace_style = "SameLineWhere" 40 | control_brace_style = "AlwaysSameLine" 41 | trailing_semicolon = false # changed 42 | trailing_comma = "Vertical" 43 | match_block_trailing_comma = false 44 | blank_lines_upper_bound = 1 45 | blank_lines_lower_bound = 0 46 | edition = "2021" # changed 47 | version = "One" 48 | merge_derives = true 49 | use_try_shorthand = true # changed 50 | use_field_init_shorthand = true # changed 51 | force_explicit_abi = true 52 | condense_wildcard_suffixes = false 53 | color = "Auto" 54 | unstable_features = true # changed 55 | disable_all_formatting = false 56 | skip_children = false 57 | hide_parse_errors = false 58 | error_on_line_overflow = false 59 | error_on_unformatted = false 60 | report_todo = "Always" 61 | report_fixme = "Always" 62 | ignore = [] 63 | 64 | # Below are `rustfmt` internal settings 65 | # 66 | # emit_mode = "Files" 67 | # make_backup = false 68 | -------------------------------------------------------------------------------- /packages/chains/relay-chain-paseo/src/lib.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Aukbit Ltd. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | use crunch_core::Crunch; 23 | use crunch_error::CrunchError; 24 | use std::result::Result; 25 | 26 | #[subxt::subxt( 27 | runtime_metadata_path = "metadata/paseo_metadata_small.scale", 28 | derive_for_all_types = "Clone, PartialEq" 29 | )] 30 | mod rc_metadata {} 31 | 32 | use rc_metadata::session::storage::types::validators::Validators; 33 | 34 | /// Fetch the set of authorities (validators) at the latest block hash 35 | pub async fn fetch_authorities(crunch: &Crunch) -> Result { 36 | let api = crunch.client().clone(); 37 | let addr = rc_metadata::storage().session().validators(); 38 | 39 | api.storage() 40 | .at_latest() 41 | .await? 42 | .fetch(&addr) 43 | .await? 44 | .ok_or_else(|| { 45 | CrunchError::from(format!( 46 | "Current validators not defined at latest block hash" 47 | )) 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /packages/chains/relay-chain-kusama/src/lib.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Aukbit Ltd. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | use crunch_core::Crunch; 23 | use crunch_error::CrunchError; 24 | use std::result::Result; 25 | 26 | #[subxt::subxt( 27 | runtime_metadata_path = "metadata/kusama_metadata_small.scale", 28 | derive_for_all_types = "Clone, PartialEq" 29 | )] 30 | mod rc_metadata {} 31 | 32 | use rc_metadata::session::storage::types::validators::Validators; 33 | 34 | /// Fetch the set of authorities (validators) at the latest block hash 35 | pub async fn fetch_authorities(crunch: &Crunch) -> Result { 36 | let api = crunch.client().clone(); 37 | let addr = rc_metadata::storage().session().validators(); 38 | 39 | api.storage() 40 | .at_latest() 41 | .await? 42 | .fetch(&addr) 43 | .await? 44 | .ok_or_else(|| { 45 | CrunchError::from(format!( 46 | "Current validators not defined at latest block hash" 47 | )) 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /packages/chains/relay-chain-polkadot/src/lib.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Aukbit Ltd. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | use crunch_core::Crunch; 23 | use crunch_error::CrunchError; 24 | use std::result::Result; 25 | 26 | #[subxt::subxt( 27 | runtime_metadata_path = "metadata/polkadot_metadata_small.scale", 28 | derive_for_all_types = "Clone, PartialEq" 29 | )] 30 | mod rc_metadata {} 31 | 32 | use rc_metadata::session::storage::types::validators::Validators; 33 | 34 | /// Fetch the set of authorities (validators) at the latest block hash 35 | pub async fn fetch_authorities(crunch: &Crunch) -> Result { 36 | let api = crunch.client().clone(); 37 | let addr = rc_metadata::storage().session().validators(); 38 | 39 | api.storage() 40 | .at_latest() 41 | .await? 42 | .fetch(&addr) 43 | .await? 44 | .ok_or_else(|| { 45 | CrunchError::from(format!( 46 | "Current validators not defined at latest block hash" 47 | )) 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /packages/chains/relay-chain-westend/src/lib.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Aukbit Ltd. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | use crunch_core::Crunch; 23 | use crunch_error::CrunchError; 24 | use std::result::Result; 25 | 26 | #[subxt::subxt( 27 | runtime_metadata_path = "metadata/westend_metadata_small.scale", 28 | derive_for_all_types = "Clone, PartialEq" 29 | )] 30 | mod rc_metadata {} 31 | 32 | use rc_metadata::session::storage::types::validators::Validators; 33 | 34 | /// Fetch the set of authorities (validators) at the latest block hash 35 | pub async fn fetch_authorities(crunch: &Crunch) -> Result { 36 | let api = crunch.client().clone(); 37 | let addr = rc_metadata::storage().session().validators(); 38 | 39 | api.storage() 40 | .at_latest() 41 | .await? 42 | .fetch(&addr) 43 | .await? 44 | .ok_or_else(|| { 45 | CrunchError::from(format!( 46 | "Current validators not defined at latest block hash" 47 | )) 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "packages/chains/asset-hub-polkadot", 4 | "packages/chains/people-polkadot", 5 | "packages/chains/relay-chain-polkadot", 6 | "packages/chains/polkadot", 7 | "packages/chains/asset-hub-kusama", 8 | "packages/chains/people-kusama", 9 | "packages/chains/relay-chain-kusama", 10 | "packages/chains/kusama", 11 | "packages/chains/asset-hub-paseo", 12 | "packages/chains/people-paseo", 13 | "packages/chains/relay-chain-paseo", 14 | "packages/chains/paseo", 15 | "packages/chains/asset-hub-westend", 16 | "packages/chains/people-westend", 17 | "packages/chains/relay-chain-westend", 18 | "packages/chains/westend", 19 | "packages/config", 20 | "packages/core", 21 | "packages/crunch", 22 | "packages/error", 23 | "packages/matrix", 24 | "packages/pools", 25 | "packages/report", 26 | "packages/stats", 27 | "packages/support", 28 | ] 29 | 30 | resolver = "2" 31 | 32 | [workspace.package] 33 | version = "0.28.0" 34 | license = "Apache-2.0" 35 | repository = "https://github.com/turboflakes/crunch" 36 | authors = ["Paulo "] 37 | description = "Crunch is a command-line interface (CLI) to claim staking rewards (flakes) every X hours for Substrate-based chains" 38 | edition = "2021" 39 | 40 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 41 | 42 | [workspace.dependencies] 43 | # crunch dependencies 44 | async-std = { version = "1.13.0", features = ["attributes", "tokio1"] } 45 | codec = { package = "parity-scale-codec", version = "3.7.4", default-features = false, features = [ 46 | "derive", 47 | "full", 48 | "bit-vec", 49 | ] } 50 | dotenv = "0.15" 51 | envy = "0.4" 52 | env_logger = "0.11.6" 53 | futures = "0.3.31" 54 | hex = "0.4.3" 55 | log = "0.4" 56 | clap = "2.33" 57 | lazy_static = "1.5" 58 | async-recursion = "1.1.1" 59 | serde = { version = "1.0.132", features = ["derive"] } 60 | serde_json = "1.0.68" 61 | thiserror = "2.0.11" 62 | chrono = "0.4.40" 63 | regex = "1.11.1" 64 | reqwest = { version = "0.12.12", features = ["json"] } 65 | url = "2.5.4" 66 | base64 = "0.22.1" 67 | rand = "0.9.0" 68 | # subxt dependencies 69 | subxt = { version = "0.44.0", features = [ 70 | "native", 71 | "reconnecting-rpc-client", 72 | "unstable-light-client", 73 | ] } 74 | subxt-signer = { version = "0.44.0", features = ["subxt"] } 75 | # substrate dependencies 76 | sp-core = "35.0.0" 77 | -------------------------------------------------------------------------------- /crunch-update.sh: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # Copyright © 2021 Aukbit Ltd. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in all 12 | # copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | # SOFTWARE. 21 | 22 | #!/bin/bash 23 | # 24 | # > make a file executable 25 | # chmod +x ./crunch-update.sh 26 | 27 | DIRNAME="~/crunch-bot" 28 | FILENAME="$DIRNAME/crunch" 29 | 30 | read -p "Enter the Crunch version that you would like to download (e.g.: 0.21.0): " INPUT_VERSION 31 | 32 | if [ "$INPUT_VERSION" = "" ]; then 33 | INPUT_VERSION="0.21.0" 34 | fi 35 | 36 | read -p "Enter a specific Ubuntu version the release was built on, or leave empty for latest. Available options: [ubuntu-22.04, ubuntu-20.04, linux-musl]: " TARGET_VERSION 37 | 38 | if [ "$TARGET_VERSION" != "" ]; then 39 | TARGET_VERSION=".${TARGET_VERSION//.}" 40 | fi 41 | 42 | URI="https://github.com/turboflakes/crunch/releases/download/v$INPUT_VERSION/crunch$TARGET_VERSION" 43 | URI_SHA256="https://github.com/turboflakes/crunch/releases/download/v$INPUT_VERSION/crunch.sha256$TARGET_VERSION" 44 | wget $URI && wget $URI_SHA256 45 | 46 | if [ "$TARGET_VERSION" != "" ]; then 47 | mv "crunch$TARGET_VERSION" crunch 48 | fi 49 | 50 | if sha256sum -c "crunch.sha256$TARGET_VERSION" 2>&1 | grep -q 'OK' 51 | then 52 | if [ ! -d "$DIRNAME" ] 53 | then 54 | mkdir $DIRNAME 55 | fi 56 | if [[ -f "$FILENAME" ]] 57 | then 58 | mv "$FILENAME" "$FILENAME.backup" 59 | fi 60 | rm "crunch.sha256$TARGET_VERSION" 61 | chmod +x crunch 62 | mv crunch "$FILENAME" 63 | echo "** crunch v$INPUT_VERSION successfully downloaded and verified $FILENAME **" 64 | else 65 | echo "Error: SHA256 doesn't match!" 66 | rm "$FILENAME*" 67 | fi 68 | -------------------------------------------------------------------------------- /packages/support/chain-specs/asset-hub-paseo.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Paseo Asset Hub", 3 | "id": "asset-hub-paseo", 4 | "chainType": "Live", 5 | "bootNodes": [ 6 | "/dns/asset-hub-paseo.bootnode.amforc.com/tcp/30333/wss/p2p/12D3KooWERfFUg8UFPCakzTFkktdRYeG2cD3A9ga1DfynbPdYqGL", 7 | "/dns/asset-hub-paseo.bootnode.amforc.com/tcp/30345/p2p/12D3KooWERfFUg8UFPCakzTFkktdRYeG2cD3A9ga1DfynbPdYqGL", 8 | "/dns/asset-hub-paseo-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWGoC9CdpY8T5bgf6PqKgry2DjCxaqQS7R9WdQ8rVMeEMg", 9 | "/dns/asset-hub-paseo-boot-ng.dwellir.com/tcp/30357/p2p/12D3KooWGoC9CdpY8T5bgf6PqKgry2DjCxaqQS7R9WdQ8rVMeEMg", 10 | "/dns/boot.gatotech.network/tcp/33410/p2p/12D3KooWS94imuEGq76dNBJb11hDKhx4UrJ8gG7hrgaeDRzDEcGG", 11 | "/dns/boot.gatotech.network/tcp/35410/wss/p2p/12D3KooWS94imuEGq76dNBJb11hDKhx4UrJ8gG7hrgaeDRzDEcGG", 12 | "/dns/boot-node.helikon.io/tcp/10120/p2p/12D3KooWKrg6Qb2HhTc3onfkgsNbRU72RjNDm96Xh2E7sWso5LZT", 13 | "/dns/boot-node.helikon.io/tcp/10122/wss/p2p/12D3KooWKrg6Qb2HhTc3onfkgsNbRU72RjNDm96Xh2E7sWso5LZT", 14 | "/dns/boot-kusama-assethub.luckyfriday.io/tcp/443/wss/p2p/12D3KooWSwaeFs6FNgpgh54fdoxSDAA4nJNaPE3PAcse2GRrG7b3", 15 | "/dns/boot.metaspan.io/tcp/36092/p2p/12D3KooWHdtFsjicVWecmiRrc8XLc3A6iLLKKfUwjZXu5mtg1k3w", 16 | "/dns/boot.metaspan.io/tcp/36096/wss/p2p/12D3KooWHdtFsjicVWecmiRrc8XLc3A6iLLKKfUwjZXu5mtg1k3w", 17 | "/dns/asset-hub-kusama.bootnodes.polkadotters.com/tcp/30511/p2p/12D3KooWDpk7wVH7RgjErEvbvAZ2kY5VeaAwRJP5ojmn1e8b8UbU", 18 | "/dns/asset-hub-kusama.bootnodes.polkadotters.com/tcp/30513/wss/p2p/12D3KooWDpk7wVH7RgjErEvbvAZ2kY5VeaAwRJP5ojmn1e8b8UbU", 19 | "/dns/assethub-paseo-bootnode.radiumblock.com/tcp/30333/p2p/12D3KooWP8aNgAjkYzH1QuwLjYyNqfpWkJkFRgdtUuey9KzEJciq", 20 | "/dns/assethub-paseo-bootnode.radiumblock.com/tcp/30336/wss/p2p/12D3KooWP8aNgAjkYzH1QuwLjYyNqfpWkJkFRgdtUuey9KzEJciq", 21 | "/dns/asset-hub-paseo.boot.rotko.net/tcp/34011/p2p/12D3KooWLzC336hvwY7Vyjdwc8VMMMyqnwph1UXMoi1LEbw8RiHj", 22 | "/dns/asset-hub-paseo.boot.rotko.net/tcp/30435/wss/p2p/12D3KooWLzC336hvwY7Vyjdwc8VMMMyqnwph1UXMoi1LEbw8RiHj", 23 | "/dns/boot.stake.plus/tcp/44333/p2p/12D3KooWSaDfEuvzA8xFyPvDaptCJn2WYUz1f1QFtTiwk4MpnHVo", 24 | "/dns/boot.stake.plus/tcp/44333/wss/p2p/12D3KooWSaDfEuvzA8xFyPvDaptCJn2WYUz1f1QFtTiwk4MpnHVo", 25 | "/dns/asset-hub-paseo-bootnode.turboflakes.io/tcp/30330/p2p/12D3KooWJzfVkdDnKfn2hQ1c3ysrbmReTjVKrEBHkdwgZThbB1BM", 26 | "/dns/asset-hub-paseo-bootnode.turboflakes.io/tcp/30430/wss/p2p/12D3KooWJzfVkdDnKfn2hQ1c3ysrbmReTjVKrEBHkdwgZThbB1BM" 27 | ], 28 | "telemetryEndpoints": null, 29 | "protocolId": "ah-pas", 30 | "properties": { 31 | "ss58Format": 0, 32 | "tokenDecimals": 10, 33 | "tokenSymbol": "PAS" 34 | }, 35 | "relay_chain": "paseo", 36 | "para_id": 1000, 37 | "codeSubstitutes": {}, 38 | "genesis": { 39 | "stateRootHash": "0xe24abb446f9aa757610fedc53cc631536f3b456a927a1ab1b9018ea2df3c925d" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/pools/src/lib.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Aukbit Ltd. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | use hex::ToHex; 22 | use std::str::FromStr; 23 | use subxt::utils::AccountId32; 24 | 25 | /// The type of account being created. 26 | #[allow(dead_code)] 27 | pub enum AccountType { 28 | Bonded, 29 | Reward, 30 | } 31 | 32 | impl AccountType { 33 | pub fn as_bytes(&self) -> Vec { 34 | match self { 35 | Self::Bonded => vec![0u8], 36 | Self::Reward => vec![1u8], 37 | } 38 | } 39 | } 40 | 41 | impl std::fmt::Display for AccountType { 42 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 43 | match self { 44 | Self::Bonded => write!(f, "bonded"), 45 | Self::Reward => write!(f, "reward"), 46 | } 47 | } 48 | } 49 | 50 | pub fn nomination_pool_account(account_type: AccountType, pool_id: u32) -> AccountId32 { 51 | // NOTE: nomination pools pallet id could be retrieved 52 | // from metadata constants nomination_pools.pallet_id() 53 | let pallet_module = b"modlpy/nopls"; 54 | // concatenate all information 55 | let mut buffer = Vec::::new(); 56 | buffer.extend(pallet_module); 57 | buffer.extend(account_type.as_bytes()); 58 | buffer.extend(pool_id.to_le_bytes()); 59 | buffer.extend(vec![0u8; 15]); 60 | // convert to hex 61 | let buffer_hex = buffer.encode_hex::(); 62 | // NOTE: subxt::utils::AccountId32 currently doesn't support from hex conversion 63 | let acc = sp_core::crypto::AccountId32::from_str(&buffer_hex).unwrap(); 64 | AccountId32::from_str(&acc.to_string()).unwrap() 65 | } 66 | 67 | #[test] 68 | fn test_pools() { 69 | assert_eq!( 70 | nomination_pool_account(AccountType::Reward, 2), 71 | AccountId32::from_str("13UVJyLnbVp8c4FQeiGUcWddfDNNLSajaPyfpYzx9QbrvLfR") 72 | .unwrap() 73 | ); 74 | assert_eq!( 75 | nomination_pool_account(AccountType::Bonded, 2), 76 | AccountId32::from_str("13UVJyLnbVp8c4FQeiGCsV63YihAstUrqj3AGcK7gaj8eubS") 77 | .unwrap() 78 | ); 79 | } 80 | -------------------------------------------------------------------------------- /packages/support/chain-specs/people-westend.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Westend People", 3 | "id": "people-westend", 4 | "chainType": "Live", 5 | "bootNodes": [ 6 | "/dns/westend-people-collator-node-0.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWDcLjDLTu9fNhmas9DTWtqdv8eUbFMWQzVwvXRK7QcjHD", 7 | "/dns/westend-people-collator-node-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWM56JbKWAXsDyWh313z73aKYVMp1Hj2nSnAKY3q6MnoC9", 8 | "/dns/westend-people-collator-node-0.parity-testnet.parity.io/tcp/30335/ws/p2p/12D3KooWDcLjDLTu9fNhmas9DTWtqdv8eUbFMWQzVwvXRK7QcjHD", 9 | "/dns/westend-people-collator-node-1.parity-testnet.parity.io/tcp/30335/ws/p2p/12D3KooWM56JbKWAXsDyWh313z73aKYVMp1Hj2nSnAKY3q6MnoC9", 10 | "/dns/westend-people-collator-node-0.parity-testnet.parity.io/tcp/443/wss/p2p/12D3KooWDcLjDLTu9fNhmas9DTWtqdv8eUbFMWQzVwvXRK7QcjHD", 11 | "/dns/westend-people-collator-node-1.parity-testnet.parity.io/tcp/443/wss/p2p/12D3KooWM56JbKWAXsDyWh313z73aKYVMp1Hj2nSnAKY3q6MnoC9", 12 | "/dns/identity-westend.bootnodes.polkadotters.com/tcp/30532/p2p/12D3KooWKr9San6KTM7REJ95cBaDoiciGcWnW8TTftEJgxGF5Ehb", 13 | "/dns/identity-westend.bootnodes.polkadotters.com/tcp/30534/wss/p2p/12D3KooWKr9San6KTM7REJ95cBaDoiciGcWnW8TTftEJgxGF5Ehb", 14 | "/dns/people-westend-bootnode.radiumblock.com/tcp/30333/p2p/12D3KooWHb7bp7fvxCwR1i6m8xn4j1ZSVZ6a49TVYbrWSC2sJhn4", 15 | "/dns/people-westend-bootnode.radiumblock.com/tcp/30336/wss/p2p/12D3KooWHb7bp7fvxCwR1i6m8xn4j1ZSVZ6a49TVYbrWSC2sJhn4", 16 | "/dns/boot-node.helikon.io/tcp/9520/p2p/12D3KooWHhZk21Wzvsd3Un1Cp63diXqr6idbG1MEiUWaitUZuX4c", 17 | "/dns/boot-node.helikon.io/tcp/9522/wss/p2p/12D3KooWHhZk21Wzvsd3Un1Cp63diXqr6idbG1MEiUWaitUZuX4c", 18 | "/dns/boot.metaspan.io/tcp/35068/p2p/12D3KooWAtw8ybFXNmNdTUsvt2gfKwtuea9wDQT2b8FpbVNKYGwc", 19 | "/dns/boot.metaspan.io/tcp/35069/wss/p2p/12D3KooWAtw8ybFXNmNdTUsvt2gfKwtuea9wDQT2b8FpbVNKYGwc", 20 | "/dns/boot.stake.plus/tcp/46333/p2p/12D3KooWLNWUF4H5WE3dy2rPB56gVcR48XY2rHwEaZ6pGTK6HYFi", 21 | "/dns/boot.stake.plus/tcp/46334/wss/p2p/12D3KooWLNWUF4H5WE3dy2rPB56gVcR48XY2rHwEaZ6pGTK6HYFi", 22 | "/dns/boot.gatotech.network/tcp/33340/p2p/12D3KooWHwURYtEHpexfrZa8k8hVgVi5FTFr4N8HBnn9kPDsWfgA", 23 | "/dns/boot.gatotech.network/tcp/35340/wss/p2p/12D3KooWHwURYtEHpexfrZa8k8hVgVi5FTFr4N8HBnn9kPDsWfgA", 24 | "/dns/people-westend.bootnode.amforc.com/tcp/30333/wss/p2p/12D3KooWQrMQFAXxJJJCtVr8nViBR6EDsuT1RyqU3eoCMebRQxTf", 25 | "/dns/people-westend.bootnode.amforc.com/tcp/30346/p2p/12D3KooWQrMQFAXxJJJCtVr8nViBR6EDsuT1RyqU3eoCMebRQxTf", 26 | "/dns/people-westend-bootnode.turboflakes.io/tcp/30650/p2p/12D3KooWQEhmZg3uMkuxVUx3jbsD84zEX4dUKtvHfmCoBWMhybKW", 27 | "/dns/people-westend-bootnode.turboflakes.io/tcp/30750/wss/p2p/12D3KooWQEhmZg3uMkuxVUx3jbsD84zEX4dUKtvHfmCoBWMhybKW", 28 | "/dns/people-westend-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWBdCpCabhgBpLn67LWcXE2JJCCTMhuJHrfDNiTiCCr3KX", 29 | "/dns/people-westend-boot-ng.dwellir.com/tcp/30355/p2p/12D3KooWBdCpCabhgBpLn67LWcXE2JJCCTMhuJHrfDNiTiCCr3KX" 30 | ], 31 | "telemetryEndpoints": null, 32 | "protocolId": null, 33 | "properties": { 34 | "ss58Format": 42, 35 | "tokenDecimals": 12, 36 | "tokenSymbol": "WND" 37 | }, 38 | "relay_chain": "westend", 39 | "para_id": 1004, 40 | "codeSubstitutes": {}, 41 | "genesis": { 42 | "stateRootHash": "0xec27848f8a0ece913132d150b34739e3412dbd10fd869ac8cbeda765c399d973" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/support/chain-specs/asset-hub-polkadot.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Polkadot Asset Hub", 3 | "id": "asset-hub-polkadot", 4 | "chainType": "Live", 5 | "bootNodes": [ 6 | "/dns/polkadot-asset-hub-connect-0.polkadot.io/tcp/30334/p2p/12D3KooWLHqbcQtoBygf7GJgVjVa3TaeLuf7VbicNdooaCmQM2JZ", 7 | "/dns/polkadot-asset-hub-connect-0.polkadot.io/tcp/443/wss/p2p/12D3KooWLHqbcQtoBygf7GJgVjVa3TaeLuf7VbicNdooaCmQM2JZ", 8 | "/dns/polkadot-asset-hub-connect-1.polkadot.io/tcp/30334/p2p/12D3KooWNDrKSayoZXGGE2dRSFW2g1iGPq3fTZE2U39ma9yZGKd3", 9 | "/dns/polkadot-asset-hub-connect-1.polkadot.io/tcp/443/wss/p2p/12D3KooWNDrKSayoZXGGE2dRSFW2g1iGPq3fTZE2U39ma9yZGKd3", 10 | "/dns/asset-hub-polkadot.boot.stake.plus/tcp/30332/wss/p2p/12D3KooWJzTrFcc11AZKTMUmmLr5XLJ9qKVupZXkwHUMx4ULbwm2", 11 | "/dns/asset-hub-polkadot.boot.stake.plus/tcp/31332/wss/p2p/12D3KooWNWF2zwxWDuZfXEzL29yeXAAubFy8tCCMjPwWLCZdLRqc", 12 | "/dns/boot.metaspan.io/tcp/16052/p2p/12D3KooWLwiJuvqQUB4kYaSjLenFKH9dWZhGZ4qi7pSb3sUYU651", 13 | "/dns/boot.metaspan.io/tcp/16056/wss/p2p/12D3KooWLwiJuvqQUB4kYaSjLenFKH9dWZhGZ4qi7pSb3sUYU651", 14 | "/dns/boot.gatotech.network/tcp/33110/p2p/12D3KooWKgwQfAeDoJARdtxFNNWfbYmcu6s4yUuSifnNoDgzHZgm", 15 | "/dns/boot.gatotech.network/tcp/35110/wss/p2p/12D3KooWKgwQfAeDoJARdtxFNNWfbYmcu6s4yUuSifnNoDgzHZgm", 16 | "/dns/statemint-bootnode.turboflakes.io/tcp/30315/p2p/12D3KooWL8CyLww3m3pRySQGGYGNJhWDMqko3j5xi67ckP7hDUvo", 17 | "/dns/statemint-bootnode.turboflakes.io/tcp/30415/wss/p2p/12D3KooWL8CyLww3m3pRySQGGYGNJhWDMqko3j5xi67ckP7hDUvo", 18 | "/dns/boot-node.helikon.io/tcp/10220/p2p/12D3KooW9uybhguhDjVJc3U3kgZC3i8rWmAnSpbnJkmuR7C6ZsRW", 19 | "/dns/boot-node.helikon.io/tcp/10222/wss/p2p/12D3KooW9uybhguhDjVJc3U3kgZC3i8rWmAnSpbnJkmuR7C6ZsRW", 20 | "/dns/asset-hub-polkadot.bootnode.amforc.com/tcp/30007/p2p/12D3KooWDLxPXYnSHjNwq9ibqgxuzRni5VViuGNSjNe3ueqVgqE3", 21 | "/dns/asset-hub-polkadot.bootnode.amforc.com/tcp/29999/wss/p2p/12D3KooWDLxPXYnSHjNwq9ibqgxuzRni5VViuGNSjNe3ueqVgqE3", 22 | "/dns/statemint-boot-ng.dwellir.com/tcp/30344/p2p/12D3KooWEFrNuNk8fPdQS2hf34Gmqi6dGSvrETshGJUrqrvfRDZr", 23 | "/dns/statemint-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWEFrNuNk8fPdQS2hf34Gmqi6dGSvrETshGJUrqrvfRDZr", 24 | "/dns/statemint-bootnode.radiumblock.com/tcp/30336/wss/p2p/12D3KooWLKxHom7f3XawRJqrF8RwiKK5Sj3qZqz5c7hF6eJeXhTx", 25 | "/dns/statemint-bootnode.radiumblock.com/tcp/30333/p2p/12D3KooWLKxHom7f3XawRJqrF8RwiKK5Sj3qZqz5c7hF6eJeXhTx", 26 | "/dns/asset-hub-polkadot.boot.rotko.net/tcp/31011/p2p/12D3KooWKkzLjYF6M5eEs7nYiqEtRqY8SGVouoCwo3nCWsRnThDW", 27 | "/dns/asset-hub-polkadot.boot.rotko.net/tcp/30435/wss/p2p/12D3KooWKkzLjYF6M5eEs7nYiqEtRqY8SGVouoCwo3nCWsRnThDW", 28 | "/dns/asset-hub-polkadot.bootnodes.polkadotters.com/tcp/30508/p2p/12D3KooWKbfY9a9oywxMJKiALmt7yhrdQkjXMtvxhhDDN23vG93R", 29 | "/dns/asset-hub-polkadot.bootnodes.polkadotters.com/tcp/30510/wss/p2p/12D3KooWKbfY9a9oywxMJKiALmt7yhrdQkjXMtvxhhDDN23vG93R", 30 | "/dns/boot-polkadot-assethub.luckyfriday.io/tcp/443/wss/p2p/12D3KooWDR9M7CjV1xdjCRbRwkFn1E7sjMaL4oYxGyDWxuLrFc2J", 31 | "/dns/asset-hub-polkadot-01.bootnode.stkd.io/tcp/30633/wss/p2p/12D3KooWJUhizuk3crSvpyKLGycHBtnP93rwjksVueveU6x6k6RY" 32 | ], 33 | "telemetryEndpoints": null, 34 | "protocolId": null, 35 | "properties": { 36 | "ss58Format": 0, 37 | "tokenDecimals": 10, 38 | "tokenSymbol": "DOT" 39 | }, 40 | "relay_chain": "polkadot", 41 | "para_id": 1000, 42 | "codeSubstitutes": {}, 43 | "genesis": { 44 | "stateRootHash": "0xc1ef26b567de07159e4ecd415fbbb0340c56a09c4d72c82516d0f3bc2b782c80" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/support/chain-specs/asset-hub-kusama.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Kusama Asset Hub", 3 | "id": "asset-hub-kusama", 4 | "chainType": "Live", 5 | "bootNodes": [ 6 | "/dns/kusama-asset-hub-connect-0.polkadot.io/tcp/30334/p2p/12D3KooWMzvdGcUXxacLdMQzRVrsP1mJrZHcrz8LtGbhLzve84Qx", 7 | "/dns/kusama-asset-hub-connect-0.polkadot.io/tcp/443/wss/p2p/12D3KooWMzvdGcUXxacLdMQzRVrsP1mJrZHcrz8LtGbhLzve84Qx", 8 | "/dns/kusama-asset-hub-connect-1.polkadot.io/tcp/30334/p2p/12D3KooWQmGf5z3DU1kKcZoLzMNgdbP31ybjuwxS1VGLKMUjq5ez", 9 | "/dns/kusama-asset-hub-connect-1.polkadot.io/tcp/443/wss/p2p/12D3KooWQmGf5z3DU1kKcZoLzMNgdbP31ybjuwxS1VGLKMUjq5ez", 10 | "/dns/asset-hub-kusama.boot.stake.plus/tcp/30332/wss/p2p/12D3KooWGfJsBTxWttMwFkyBi6ZvEzAU3mvcVAzE7yFXMZuasicr", 11 | "/dns/asset-hub-kusama.boot.stake.plus/tcp/31332/wss/p2p/12D3KooWK7hHQbFEhhgZURY7V4LzY6BkqLsnCCpEJ69eUF7ucPcE", 12 | "/dns/boot.metaspan.io/tcp/26052/p2p/12D3KooW9z9hKqe3mqYAp5UJMhZiCqhkTHyiR43fegnGmTJ3JAba", 13 | "/dns/boot.metaspan.io/tcp/26056/wss/p2p/12D3KooW9z9hKqe3mqYAp5UJMhZiCqhkTHyiR43fegnGmTJ3JAba", 14 | "/dns/boot.gatotech.network/tcp/33210/p2p/12D3KooWRMUYeWMPkadDG8baX9j1e95fspfp8MhPGym5BQza7Fm5", 15 | "/dns/boot.gatotech.network/tcp/35210/wss/p2p/12D3KooWRMUYeWMPkadDG8baX9j1e95fspfp8MhPGym5BQza7Fm5", 16 | "/dns/statemine-bootnode.turboflakes.io/tcp/30320/p2p/12D3KooWN2Qqvp5wWgjbBMpbqhKgvSibSHfomP5VWVD9VCn3VrV4", 17 | "/dns/statemine-bootnode.turboflakes.io/tcp/30420/wss/p2p/12D3KooWN2Qqvp5wWgjbBMpbqhKgvSibSHfomP5VWVD9VCn3VrV4", 18 | "/dns/boot-node.helikon.io/tcp/10210/p2p/12D3KooWFXRQce3aMgZMn5SxvHtYH4PsR63TZLf8LrnBsEVTyzdr", 19 | "/dns/boot-node.helikon.io/tcp/10212/wss/p2p/12D3KooWFXRQce3aMgZMn5SxvHtYH4PsR63TZLf8LrnBsEVTyzdr", 20 | "/dns/asset-hub-kusama.bootnode.amforc.com/tcp/30007/p2p/12D3KooWHy1CPndZYphwdVqMb295KPC6LRt17Ae3zNSr7evzeF5a", 21 | "/dns/asset-hub-kusama.bootnode.amforc.com/tcp/29999/wss/p2p/12D3KooWHy1CPndZYphwdVqMb295KPC6LRt17Ae3zNSr7evzeF5a", 22 | "/dns/statemine-boot-ng.dwellir.com/tcp/30343/p2p/12D3KooWQNJKBaNfW6Nn7HZDi5pSSEFmHL2Qz7chr9RksQUDR1Wk", 23 | "/dns/statemine-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWQNJKBaNfW6Nn7HZDi5pSSEFmHL2Qz7chr9RksQUDR1Wk", 24 | "/dns/statemine-bootnode.radiumblock.com/tcp/30333/p2p/12D3KooWCKUrE5uaXQ288ko3Ex3zCyozyJLG47KEYTopinnXNtYL", 25 | "/dns/statemine-bootnode.radiumblock.com/tcp/30336/wss/p2p/12D3KooWCKUrE5uaXQ288ko3Ex3zCyozyJLG47KEYTopinnXNtYL", 26 | "/dns/asset-hub-kusama.boot.rotko.net/tcp/32011/p2p/12D3KooWJUFnjR2PNbsJhudwPVaWCoZy1acPGKjM2cSuGj345BBu", 27 | "/dns/asset-hub-kusama.boot.rotko.net/tcp/30435/wss/p2p/12D3KooWJUFnjR2PNbsJhudwPVaWCoZy1acPGKjM2cSuGj345BBu", 28 | "/dns/asset-hub-kusama.bootnodes.polkadotters.com/tcp/30511/p2p/12D3KooWDpk7wVH7RgjErEvbvAZ2kY5VeaAwRJP5ojmn1e8b8UbU", 29 | "/dns/asset-hub-kusama.bootnodes.polkadotters.com/tcp/30513/wss/p2p/12D3KooWDpk7wVH7RgjErEvbvAZ2kY5VeaAwRJP5ojmn1e8b8UbU", 30 | "/dns/boot-kusama-assethub.luckyfriday.io/tcp/443/wss/p2p/12D3KooWSwaeFs6FNgpgh54fdoxSDAA4nJNaPE3PAcse2GRrG7b3", 31 | "/dns/asset-hub-kusama-01.bootnode.stkd.io/tcp/30633/wss/p2p/12D3KooWNCg821LyWDVrAJ2mG6ScDeeBFuDPiJtLYc9jCGNCyMoq" 32 | ], 33 | "telemetryEndpoints": null, 34 | "protocolId": null, 35 | "properties": { 36 | "ss58Format": 2, 37 | "tokenDecimals": 12, 38 | "tokenSymbol": "KSM" 39 | }, 40 | "relay_chain": "ksmcc3", 41 | "para_id": 1000, 42 | "codeSubstitutes": {}, 43 | "genesis": { 44 | "stateRootHash": "0x299ac939ea4722d1fcf7f4873675040ebc83d144620c76206dbeb7468cf30cdf" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/error/src/lib.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Aukbit Ltd. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | use codec; 23 | use reqwest; 24 | use std::{str::Utf8Error, string::String}; 25 | use subxt::{ 26 | error::{DispatchError, MetadataError}, 27 | lightclient::LightClientError, 28 | }; 29 | 30 | use thiserror::Error; 31 | 32 | /// Crunch specific error messages 33 | #[derive(Error, Debug)] 34 | pub enum CrunchError { 35 | #[error("Subxt error: {0}")] 36 | SubxtError(#[from] subxt::Error), 37 | #[error("SubxtCore error: {0}")] 38 | SubxtCoreError(#[from] subxt::ext::subxt_core::Error), 39 | #[error("LightClient error: {0}")] 40 | LightClientError(#[from] LightClientError), 41 | #[error("Codec error: {0}")] 42 | CodecError(#[from] codec::Error), 43 | #[error("Utf8 error: {0}")] 44 | Utf8Error(#[from] Utf8Error), 45 | #[error("Metadata error: {0}")] 46 | MetadataError(#[from] MetadataError), 47 | #[error("Dispatch error: {0}")] 48 | DispatchError(#[from] DispatchError), 49 | #[error("RPC error: {0}")] 50 | RpcError(#[from] subxt::ext::subxt_rpcs::Error), 51 | #[error("Matrix error: {0}")] 52 | MatrixError(#[from] crunch_matrix::error::MatrixError), 53 | #[error("Subscription finished")] 54 | SubscriptionFinished, 55 | #[error("Reqwest error: {0}")] 56 | ReqwestError(#[from] reqwest::Error), 57 | #[error("ParseError error: {0}")] 58 | ParseError(#[from] url::ParseError), 59 | #[error("SubxtSignerError error: {0}")] 60 | SubxtSignerError(#[from] subxt_signer::sr25519::Error), 61 | #[error("SecretError error: {0}")] 62 | SecretError(#[from] subxt_signer::SecretUriError), 63 | #[error("IOError error: {0}")] 64 | IOError(#[from] std::io::Error), 65 | #[error("Genesis mismatch: {0}")] 66 | GenesisError(String), 67 | #[error("Weight exceeded: {0}")] 68 | MaxWeightExceeded(String), 69 | #[error("Weight exceeded for one extrinsic")] 70 | MaxWeightExceededForOneExtrinsic, 71 | #[error("DryRunError: {0}")] 72 | DryRunError(String), 73 | #[error("Insufficient balance: {0}")] 74 | InsufficientBalance(String), 75 | #[error("RuntimeUpgradeDetected: {0} -> {1}")] 76 | RuntimeUpgradeDetected(u32, u32), 77 | #[error("Other error: {0}")] 78 | Other(String), 79 | } 80 | 81 | /// Convert &str to CrunchError 82 | impl From<&str> for CrunchError { 83 | fn from(error: &str) -> Self { 84 | CrunchError::Other(error.into()) 85 | } 86 | } 87 | 88 | /// Convert String to CrunchError 89 | impl From for CrunchError { 90 | fn from(error: String) -> Self { 91 | CrunchError::Other(error) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /packages/support/chain-specs/asset-hub-westend.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Westend Asset Hub", 3 | "id": "asset-hub-westend", 4 | "chainType": "Live", 5 | "bootNodes": [ 6 | "/dns/westend-asset-hub-bootnode-0.polkadot.io/tcp/30333/p2p/12D3KooWJaAfPyiye7ZQBuHengTJJoMrcaz7Jj1UzHiKdNxA1Nkd", 7 | "/dns/westend-asset-hub-bootnode-1.polkadot.io/tcp/30333/p2p/12D3KooWGL3hpWycWyeqyL9gHNnmmsL474WkPZdqraBHu4L6fQrW", 8 | "/dns/westend-asset-hub-bootnode-0.polkadot.io/tcp/30335/ws/p2p/12D3KooWJaAfPyiye7ZQBuHengTJJoMrcaz7Jj1UzHiKdNxA1Nkd", 9 | "/dns/westend-asset-hub-bootnode-1.polkadot.io/tcp/30335/ws/p2p/12D3KooWGL3hpWycWyeqyL9gHNnmmsL474WkPZdqraBHu4L6fQrW", 10 | "/dns/westend-asset-hub-connect-0.polkadot.io/tcp/443/wss/p2p/12D3KooWJaAfPyiye7ZQBuHengTJJoMrcaz7Jj1UzHiKdNxA1Nkd", 11 | "/dns/westend-asset-hub-connect-1.polkadot.io/tcp/443/wss/p2p/12D3KooWGL3hpWycWyeqyL9gHNnmmsL474WkPZdqraBHu4L6fQrW", 12 | "/dns/asset-hub-westend.boot.stake.plus/tcp/30332/wss/p2p/12D3KooWG4YUe7AfSxVwyLQBRRMU99krssmGAUghqUFoVY1iPkQs", 13 | "/dns/asset-hub-westend.boot.stake.plus/tcp/31332/wss/p2p/12D3KooWFLR2UN6PMAUwNAjiWBAiEDoYcWRrtjDrUfRkdUssge4v", 14 | "/dns/boot.metaspan.io/tcp/36052/p2p/12D3KooWBCqfNb6Y39DXTr4UBWXyjuS3hcZM1qTbHhDXxF6HkAJJ", 15 | "/dns/boot.metaspan.io/tcp/36056/wss/p2p/12D3KooWBCqfNb6Y39DXTr4UBWXyjuS3hcZM1qTbHhDXxF6HkAJJ", 16 | "/dns/boot.gatotech.network/tcp/33310/p2p/12D3KooWMSW6hr8KcNBhGFN1bg8kYC76o67PnuDEbxRhxacW6dui", 17 | "/dns/boot.gatotech.network/tcp/35310/wss/p2p/12D3KooWMSW6hr8KcNBhGFN1bg8kYC76o67PnuDEbxRhxacW6dui", 18 | "/dns/westmint-bootnode.turboflakes.io/tcp/30325/p2p/12D3KooWHU4qqSyqKdbXdrCTMXUJxxueaZjqpqSaQqYiFPw6XqEx", 19 | "/dns/westmint-bootnode.turboflakes.io/tcp/30425/wss/p2p/12D3KooWHU4qqSyqKdbXdrCTMXUJxxueaZjqpqSaQqYiFPw6XqEx", 20 | "/dns/boot-node.helikon.io/tcp/10200/p2p/12D3KooWMRY8wb7rMT81LLuivvsy6ahUxKHQgYJw4zm1hC1uYLxb", 21 | "/dns/boot-node.helikon.io/tcp/10202/wss/p2p/12D3KooWMRY8wb7rMT81LLuivvsy6ahUxKHQgYJw4zm1hC1uYLxb", 22 | "/dns/asset-hub-westend.bootnode.amforc.com/tcp/30004/p2p/12D3KooWDfepM7kqUHMXdGqJw3ZmtvAcE2CjPcnYjT2tTfAw3ZBd", 23 | "/dns/asset-hub-westend.bootnode.amforc.com/tcp/29999/wss/p2p/12D3KooWDfepM7kqUHMXdGqJw3ZmtvAcE2CjPcnYjT2tTfAw3ZBd", 24 | "/dns/westmint-boot-ng.dwellir.com/tcp/30345/p2p/12D3KooWFZ9xqApB1wnFYkbe1qJ5Jqwxe2f3i8W25F3tKNXy59ux", 25 | "/dns/westmint-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWFZ9xqApB1wnFYkbe1qJ5Jqwxe2f3i8W25F3tKNXy59ux", 26 | "/dns/westmint-bootnode.radiumblock.com/tcp/30336/wss/p2p/12D3KooWDoq4PVdWm5nzRSvEz3DSSKjVgRhWVUaKyi5JMKwJKYbk", 27 | "/dns/westmint-bootnode.radiumblock.com/tcp/30333/p2p/12D3KooWDoq4PVdWm5nzRSvEz3DSSKjVgRhWVUaKyi5JMKwJKYbk", 28 | "/dns/asset-hub-westend.boot.rotko.net/tcp/33011/p2p/12D3KooWE4UDXqgtTcMCyUQ8S4uvaT8VMzzTBA6NWmKuYwTacWuN", 29 | "/dns/asset-hub-westend.boot.rotko.net/tcp/30435/wss/p2p/12D3KooWE4UDXqgtTcMCyUQ8S4uvaT8VMzzTBA6NWmKuYwTacWuN", 30 | "/dns/asset-hub-westend.bootnodes.polkadotters.com/tcp/30514/p2p/12D3KooWNFYysCqmojxqjjaTfD2VkWBNngfyUKWjcR4WFixfHNTk", 31 | "/dns/asset-hub-westend.bootnodes.polkadotters.com/tcp/30516/wss/p2p/12D3KooWNFYysCqmojxqjjaTfD2VkWBNngfyUKWjcR4WFixfHNTk", 32 | "/dns/asset-hub-westend-01.bootnode.stkd.io/tcp/30633/wss/p2p/12D3KooWDUPyF2q8b6fVFEuwxBbRV3coAy1kzuCPU3D9TRiLnUfE" 33 | ], 34 | "telemetryEndpoints": null, 35 | "protocolId": null, 36 | "relay_chain": "westend", 37 | "properties": { 38 | "tokenDecimals": 12, 39 | "tokenSymbol": "WND" 40 | }, 41 | "para_id": 1000, 42 | "consensusEngine": null, 43 | "genesis": { 44 | "stateRootHash": "0x1d0ce8ef6a9eef1685c4715925cf91ed1af635a752eac4f9945b1a1a4e2b6e51" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/stats/src/lib.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Aukbit Ltd. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | pub fn mean(list: &Vec) -> f64 { 23 | if list.len() == 0 { 24 | return 0.0; 25 | } 26 | let sum: f64 = list.iter().sum(); 27 | sum / (list.len() as f64) 28 | } 29 | 30 | pub fn standard_deviation(list: &Vec) -> f64 { 31 | let m = mean(list); 32 | let mut variance: Vec = 33 | list.iter().map(|&score| (score - m).powf(2.0)).collect(); 34 | mean(&mut variance).sqrt() 35 | } 36 | 37 | pub fn median(list: &mut Vec) -> u32 { 38 | if list.len() == 0 { 39 | return 0; 40 | } 41 | list.sort(); 42 | let mid = list.len() / 2; 43 | list[mid] 44 | } 45 | 46 | // Calculate 95% confidence interval 47 | pub fn _confidence_interval_95(list: &Vec) -> (f64, f64) { 48 | confidence_interval(list, 1.96) 49 | } 50 | 51 | // Calculate 99% confidence interval 52 | pub fn _confidence_interval_99(list: &Vec) -> (f64, f64) { 53 | confidence_interval(list, 2.576) 54 | } 55 | 56 | // Calculate 99.9% confidence interval 57 | pub fn confidence_interval_99_9(list: &Vec) -> (f64, f64) { 58 | confidence_interval(list, 3.291) 59 | } 60 | 61 | // https://www.mathsisfun.com/data/confidence-interval.html 62 | pub fn confidence_interval(list: &Vec, z: f64) -> (f64, f64) { 63 | let m = mean(list); 64 | let sd = standard_deviation(list); 65 | let v = z * (sd / ((list.len() as f64).sqrt())); 66 | (m - v, m + v) 67 | } 68 | // Find outliers by Interquartile Range(IQR) 69 | // https://www.statisticshowto.com/statistics-basics/find-outliers/ 70 | pub fn iqr_interval(list: &mut Vec) -> (f64, f64) { 71 | if list.len() == 0 { 72 | return (0.0, 0.0); 73 | } 74 | list.sort(); 75 | let q1 = median(&mut (&list[..&list.len() / 2]).into()); 76 | let q3 = median(&mut (&list[&list.len() - (&list.len() / 2)..]).into()); 77 | let iqr = q3 - q1; 78 | ( 79 | (q1 as f64) - (iqr as f64 * 1.5), 80 | (q3 as f64) + (iqr as f64 * 1.5), 81 | ) 82 | } 83 | 84 | #[cfg(test)] 85 | mod tests { 86 | use super::*; 87 | 88 | #[test] 89 | fn calculate_mean() { 90 | let v = vec![1.0, 2.0, 3.0, 4.0, 5.0, 4.0, 2.0, 6.0]; 91 | assert_eq!(mean(&v), 3.375); 92 | } 93 | 94 | #[test] 95 | fn calculate_median() { 96 | let mut v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9]; 97 | assert_eq!(median(&mut v), 5); 98 | } 99 | 100 | #[test] 101 | fn calculate_confidence_interval_99_9() { 102 | let v = vec![1.0, 2.0, 3.0, 4.0, 5.0, 4.0, 2.0, 6.0]; 103 | assert_eq!( 104 | confidence_interval_99_9(&v), 105 | (1.5410332231275632, 5.208966776872437) 106 | ); 107 | } 108 | 109 | #[test] 110 | fn calculate_iqr_interval() { 111 | let mut v = vec![1, 2, 3, 4, 5, 4, 2, 6, 3]; 112 | assert_eq!(iqr_interval(&mut v), (-2.5, 9.5)); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /update-metadata.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # > make a file executable 4 | # chmod +x ./update-metadata.sh 5 | # 6 | # > subxt-cli must be installed to update metadata 7 | # cargo install subxt-cli --force 8 | # 9 | # Relay Chains 10 | # subxt metadata --url wss://westend.rpc.turboflakes.io:443 -f bytes > packages/chains/relay-chain-westend/metadata/westend_metadata.scale 11 | subxt metadata --url wss://westend.rpc.turboflakes.io:443 --pallets Session -f bytes > packages/chains/relay-chain-westend/metadata/westend_metadata_small.scale 12 | # subxt metadata --url wss://paseo.rpc.turboflakes.io:443 -f bytes > packages/chains/relay-chain-paseo/metadata/paseo_metadata.scale 13 | subxt metadata --url wss://paseo.rpc.turboflakes.io:443 --pallets Session -f bytes > packages/chains/relay-chain-paseo/metadata/paseo_metadata_small.scale 14 | # subxt metadata --url wss://kusama.rpc.turboflakes.io:443 -f bytes > packages/chains/relay-chain-kusama/metadata/kusama_metadata.scale 15 | subxt metadata --url wss://kusama.rpc.turboflakes.io:443 --pallets Session -f bytes > packages/chains/relay-chain-kusama/metadata/kusama_metadata_small.scale 16 | #subxt metadata --url wss://polkadot.rpc.turboflakes.io:443 -f bytes > packages/chains/relay-chain-polkadot/metadata/polkadot_metadata.scale 17 | subxt metadata --url wss://polkadot.rpc.turboflakes.io:443 --pallets Session -f bytes > packages/chains/relay-chain-polkadot/metadata/polkadot_metadata_small.scale 18 | 19 | # AssetHub Chains 20 | subxt metadata --url wss://asset-hub-westend.rpc.turboflakes.io:443 --pallets System,Balances,Staking,Utility,NominationPools -f bytes > packages/chains/asset-hub-westend/metadata/asset_hub_westend_metadata_small.scale 21 | subxt metadata --url wss://asset-hub-paseo.rpc.turboflakes.io:443 --pallets System,Balances,Staking,Utility,NominationPools -f bytes > packages/chains/asset-hub-paseo/metadata/asset_hub_paseo_metadata_small.scale 22 | subxt metadata --url wss://asset-hub-kusama.rpc.turboflakes.io:443 --pallets System,Balances,Staking,Utility,NominationPools -f bytes > packages/chains/asset-hub-kusama/metadata/asset_hub_kusama_metadata_small.scale 23 | subxt metadata --url wss://asset-hub-polkadot.rpc.turboflakes.io:443 --pallets System,Balances,Staking,Utility,NominationPools -f bytes > packages/chains/asset-hub-polkadot/metadata/asset_hub_polkadot_metadata_small.scale 24 | 25 | # People Chains 26 | subxt metadata --url wss://people-westend.rpc.turboflakes.io:443 --pallets Identity -f bytes > packages/chains/people-westend/metadata/people_westend_metadata_small.scale 27 | subxt metadata --url wss://people-paseo.rpc.turboflakes.io:443 --pallets Identity -f bytes > packages/chains/people-paseo/metadata/people_paseo_metadata_small.scale 28 | subxt metadata --url wss://people-kusama.rpc.turboflakes.io:443 --pallets Identity -f bytes > packages/chains/people-kusama/metadata/people_kusama_metadata_small.scale 29 | subxt metadata --url wss://people-polkadot.rpc.turboflakes.io:443 --pallets Identity -f bytes > packages/chains/people-polkadot/metadata/people_polkadot_metadata_small.scale 30 | 31 | # Generate runtime API client code from metadata. 32 | 33 | # ```bash 34 | # subxt codegen --url wss://rpc.turboflakes.io:443/westend | rustfmt --edition=2018 --emit=stdout > westend_metadata.rs 35 | # subxt codegen --url wss://rpc.turboflakes.io:443/kusama | rustfmt --edition=2018 --emit=stdout > kusama_runtime.rs 36 | # subxt codegen --url wss://asset-hub-paseo.rpc.turboflakes.io:443 | rustfmt --edition=2018 --emit=stdout > asset_hub_paseo_runtime.rs 37 | # subxt codegen --url wss://paseo.rpc.turboflakes.io:443 | rustfmt --edition=2018 --emit=stdout > paseo_runtime.rs 38 | # subxt codegen --url wss://polkadot.rpc.turboflakes.io:443 | rustfmt --edition=2018 --emit=stdout > polkadot_runtime.rs 39 | # subxt codegen --url wss://sys.turboflakes.io:443/people-kusama | rustfmt --edition=2018 --emit=stdout > people_kusama_runtime.rs 40 | # subxt codegen --url wss://sys.turboflakes.io:443/people-polkadot | rustfmt --edition=2018 --emit=stdout > people_polkadot_runtime.rs 41 | # subxt codegen --url wss://asset-hub-polkadot.rpc.turboflakes.io:443 | rustfmt --edition=2018 --emit=stdout > packages/chains/asset-hub-polkadot/metadata/asset_hub_polkadot_metadata_small.rs 42 | # ``` 43 | 44 | # Generate relay chain specs from subxt to be used under lightclient 45 | 46 | # ```bash 47 | # cargo run --features chain-spec-pruning --bin subxt chain-spec --url wss://rpc.turboflakes.io:443/westend --output-file artifacts/demo_chain_specs/westend.json --state-root-hash --remove-substitutes 48 | # cargo run --features chain-spec-pruning --bin subxt chain-spec --url wss://rpc.turboflakes.io:443/kusama --output-file artifacts/demo_chain_specs/kusama.json --state-root-hash --remove-substitutes 49 | # cargo run --features chain-spec-pruning --bin subxt chain-spec --url wss://rpc.turboflakes.io:443/polkadot --output-file artifacts/demo_chain_specs/polkadot.json --state-root-hash --remove-substitutes 50 | # cargo run --features chain-spec-pruning --bin subxt chain-spec --url wss://rpc.turboflakes.io:443/paseo --output-file artifacts/demo_chain_specs/paseo.json --state-root-hash --remove-substitutes 51 | # ``` 52 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------- 2 | # crunch CLI configuration variables 3 | # ---------------------------------------------------------------- 4 | # [CRUNCH_STASHES] Validator stash addresses for which 'crunch flakes', 'crunch rewards' 5 | # or 'crunch view' will be applied. 6 | # If needed specify more than one (e.g. stash_1,stash_2,stash_3). 7 | CRUNCH_STASHES=5GTD7ZeD823BjpmZBCSzBQp7cvHR1Gunq7oDkurZr9zUev2n 8 | # 9 | # [CRUNCH_STASHES_URL] Additionally the list of stashes could be defined and available in a remote file. 10 | # `crunch` will try to fetch the stashes from the endpoint predefined here before triggering the respective payouts 11 | # Please have a look at the file '.remote.stashes.example' as an example 12 | #CRUNCH_STASHES_URL=https://raw.githubusercontent.com/turboflakes/crunch/main/.remote.stashes.example 13 | # 14 | # [CRUNCH_GITHUB_PAT] Define a 'Github personal access token' with at least readonly access to the remote file 15 | # containing the list of stashes in the github private file defined at 'CRUNCH_STASHES_URL' 16 | # Check how to create a personal access token here: 17 | # https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens 18 | # 19 | #CRUNCH_GITHUB_PAT=github_pat_123ABC... 20 | # 21 | # [CRUNCH_LIGHT_CLIENT_ENABLED] Enable lightweight client to connect to substrate-based chains. 22 | # With this option enabled there is no need to specify specific RPCs endpoints for 'substrate-ws-url' or 'substrate-people-ws-url' 23 | #CRUNCH_LIGHT_CLIENT_ENABLED=false 24 | # 25 | # [CRUNCH_SUBSTRATE_WS_URL] Substrate websocket endpoint for which 'crunch' will try to 26 | # connect. (e.g. wss://kusama.rpc.turboflakes.io:443) (NOTE: substrate_ws_url takes precedence 27 | # than argument) 28 | CRUNCH_SUBSTRATE_WS_URL=wss://kusama.rpc.turboflakes.io:443 29 | # 30 | # [CRUNCH_SUBSTRATE_PEOPLE_WS_URL] Substrate websocket endpoint for which 'crunch' will try to 31 | # connect and retrieve identities from. (e.g. wss://people-kusama.rpc.turboflakes.io:443) 32 | #CRUNCH_SUBSTRATE_PEOPLE_WS_URL=wss://people-kusama.rpc.turboflakes.io:443 33 | # 34 | # NOTE: CRUNCH_SUBSTRATE_ASSET_HUB_WS_URL is ONLY relevant for chains where AHM already took place (Kusama, Paseo, Westend) 35 | # [CRUNCH_SUBSTRATE_ASSET_HUB_WS_URL] Substrate websocket endpoint for which 'crunch' will try to 36 | # connect, trigger payouts or view pending rewards. (e.g. wss://asset-hub-kusama.rpc.turboflakes.io:443) 37 | CRUNCH_SUBSTRATE_ASSET_HUB_WS_URL=wss://asset-hub-kusama.rpc.turboflakes.io:443 38 | # 39 | # NOTE: CRUNCH_SUBSTRATE_ASSET_HUB_WS_URL is ONLY relevant for chains where AHM already took place (Kusama, Paseo, Westend) 40 | # [CRUNCH_SUBSTRATE_ASSET_HUB_WS_URL] Substrate websocket endpoint for which 'crunch' will try to 41 | # connect, trigger payouts or view pending rewards. (e.g. wss://asset-hub-kusama.rpc.turboflakes.io:443) 42 | # 43 | # [CRUNCH_MAXIMUM_PAYOUTS] Maximum number of unclaimed eras for which an extrinsic payout 44 | # will be submitted. (e.g. a value of 4 means that if there are unclaimed eras in the last 45 | # 84 the maximum unclaimed payout calls for each stash address will be 4). [default: 16] 46 | CRUNCH_MAXIMUM_PAYOUTS=16 47 | # 48 | # [CRUNCH_MAXIMUM_HISTORY_ERAS] Maximum number of history eras for which crunch will look for 49 | # unclaimed rewards. The maximum value supported is the one defined by constant history_depth 50 | # (e.g. a value of 4 means that crunch will only check in the latest 4 eras if there are any 51 | # unclaimed rewards for each stash address). [default: 4] 52 | CRUNCH_MAXIMUM_HISTORY_ERAS=4 53 | # 54 | # 55 | # 56 | # NOTE: CRUNCH_MAXIMUM_CALLS is being DEPRECATED. Crunch tryes to optimize the number of calls that fit in a single 57 | # batch call on the fly. 58 | # [CRUNCH_MAXIMUM_CALLS] Maximum number of calls in a single batch. [default: 4] 59 | # CRUNCH_MAXIMUM_CALLS=3 60 | # 61 | # [CRUNCH_SEED_PATH] File path containing the private seed phrase to Sign the extrinsic 62 | # payout call. [default: .private.seed] 63 | #CRUNCH_SEED_PATH=.private.seed.example 64 | # 65 | # ---------------------------------------------------------------- 66 | # Matrix configuration variables 67 | # ---------------------------------------------------------------- 68 | # [CRUNCH_MATRIX_DISABLED] Disable matrix bot, unless specified otherwise matrix is enabled and respective 69 | # matrix variables below need to be correctly defined. 70 | #CRUNCH_MATRIX_DISABLED=false 71 | # 72 | # [CRUNCH_MATRIX_PUBLIC_ROOM_DISABLED] Disable public matrix room, unless specified otherwise messages will also 73 | # be sent to the public room. 74 | #CRUNCH_MATRIX_PUBLIC_ROOM_DISABLED=true 75 | # 76 | CRUNCH_MATRIX_USER=@your-regular-matrix-account:matrix.org 77 | CRUNCH_MATRIX_BOT_USER=@your-own-crunch-bot-account:matrix.org 78 | # NOTE: type the bot password within "" so that any special character could be parsed correctly into a string. 79 | CRUNCH_MATRIX_BOT_PASSWORD="anotthateasypassword" 80 | # 81 | # ---------------------------------------------------------------- 82 | # ONE-T configuration variables 83 | # ---------------------------------------------------------------- 84 | # Note: If ONET_API_ENABLED equals true, by default Crunch will try to fetch the validator grade from the respective 85 | # network it is connected to. 86 | #CRUNCH_ONET_API_ENABLED=true 87 | # 88 | # [CRUNCH_ONET_API_URL] Define a custom ONET backend endpoint 89 | #CRUNCH_ONET_API_URL=https://polkadot-onet-api.turboflakes.io 90 | # 91 | # [CRUNCH_ONET_API_KEY] Define a custom ONET api key. 92 | #CRUNCH_ONET_API_KEY=crunch-101 93 | # 94 | # [CRUNCH_ONET_NUMBER_LAST_SESSIONS] Define the number of last sessions the grade is evaluated. Default is 6. 95 | #CRUNCH_ONET_NUMBER_LAST_SESSIONS=6 96 | # 97 | # ---------------------------------------------------------------- 98 | # Nomination Pools configuration variables 99 | # ---------------------------------------------------------------- 100 | # [CRUNCH_POOL_IDS] Additionally the list of stashes could be defined from a single or more Nomination Pool Ids. 101 | # `crunch` will try to fetch the nominees of the respective pool id predefined here before triggering the respective payouts 102 | CRUNCH_POOL_IDS=2 103 | # 104 | # [CRUNCH_POOL_COMPOUND_THRESHOLD] Define minimum pending rewards threshold in PLANCKS. 105 | # Note: only pending rewards above the threshold are included in the auto-compound batch. 106 | # 1 DOT = 10000000000 PLANCKS 107 | # 1 KSM = 1000000000000 PLANCKS 108 | CRUNCH_POOL_COMPOUND_THRESHOLD=10000000000 109 | # 110 | # [CRUNCH_POOL_MEMBERS_COMPOUND_ENABLED] Enable auto-compound rewards for every member that belongs to the pools 111 | # previously selected by CRUNCH_POOL_IDS. Note that members have to have their permissions 112 | # set as PermissionlessCompound or PermissionlessAll. 113 | #CRUNCH_POOL_MEMBERS_COMPOUND_ENABLED=true 114 | # 115 | # [CRUNCH_POOL_ONLY_OPERATOR_COMPOUND_ENABLED] Enable auto-compound rewards for the pool operator member that belongs to the pools 116 | # previously selected by CRUNCH_POOL_IDS. Note that operator member account have to have their permissions 117 | # set as PermissionlessCompound or PermissionlessAll. 118 | CRUNCH_POOL_ONLY_OPERATOR_COMPOUND_ENABLED=true 119 | # 120 | # [CRUNCH_POOL_ACTIVE_NOMINEES_PAYOUT_ENABLED] Enable payouts only for ACTIVE nominees assigned to the pools 121 | # previously selected by CRUNCH_POOL_IDS. 122 | #CRUNCH_POOL_ACTIVE_NOMINEES_PAYOUT_ENABLED=true 123 | # 124 | # [CRUNCH_POOL_ALL_NOMINEES_PAYOUT_ENABLED] Enable payouts for ALL nominees assigned to the pools 125 | # previously selected by CRUNCH_POOL_IDS. 126 | #CRUNCH_POOL_ALL_NOMINEES_PAYOUT_ENABLED=true 127 | # 128 | # [CRUNCH_POOL_CLAIM_COMMISSION_ENABLED] Enable permissionless claim pool commission 129 | # NOTE: Is only possible for the pool commission to be claimed permissionless, if the nomination pool root account 130 | # explicitly sets this feature via extrinsic `set_commission_claim_permission`. 131 | CRUNCH_POOL_CLAIM_COMMISSION_ENABLED=true 132 | # 133 | # ---------------------------------------------------------------- 134 | # Transaction configuration variables 135 | # ---------------------------------------------------------------- 136 | # 137 | # [CRUNCH_TX_TIP] Define a tip in PLANCKS for the block author. 138 | # 1 DOT = 10000000000 PLANCKS 139 | # 1 KSM = 1000000000000 PLANCKS 140 | #CRUNCH_TX_TIP=10 141 | # 142 | # [CRUNCH_TX_MORTAL_PERIOD] Define the number of blocks the transaction is mortal for. 143 | #CRUNCH_TX_MORTAL_PERIOD=32 144 | -------------------------------------------------------------------------------- /packages/crunch/src/main.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Aukbit Ltd. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | use async_std::task; 23 | use crunch_config::{RunMode, CONFIG}; 24 | use crunch_core::Crunch; 25 | use crunch_error::CrunchError; 26 | use crunch_support::SupportedRuntime; 27 | use log::{error, info, warn}; 28 | use std::{env, result::Result, thread, time}; 29 | 30 | fn main() { 31 | let config = CONFIG.clone(); 32 | if config.is_debug { 33 | env::set_var("RUST_LOG", "crunch=debug,subxt=debug"); 34 | } else { 35 | env::set_var("RUST_LOG", "crunch=info"); 36 | } 37 | env_logger::try_init().unwrap_or_default(); 38 | 39 | info!( 40 | "{} v{} * {}", 41 | env!("CARGO_PKG_NAME"), 42 | env!("CARGO_PKG_VERSION"), 43 | env!("CARGO_PKG_DESCRIPTION") 44 | ); 45 | 46 | if config.only_view { 47 | return spawn_crunch_view(); 48 | } 49 | 50 | match config.run_mode { 51 | RunMode::Once => spawn_crunch_once(), 52 | RunMode::Daily | RunMode::Turbo => spawn_and_restart_crunch_flakes_on_error(), 53 | RunMode::Era => spawn_and_restart_subscription_on_error(), 54 | } 55 | } 56 | 57 | fn spawn_crunch_view() { 58 | let crunch_task = task::spawn(async { 59 | let crunch: Crunch = Crunch::new().await; 60 | if let Err(e) = inspect(&crunch).await { 61 | error!("{}", e); 62 | }; 63 | }); 64 | task::block_on(crunch_task); 65 | } 66 | 67 | fn spawn_crunch_once() { 68 | let crunch_task = task::spawn(async { 69 | let crunch: Crunch = Crunch::new().await; 70 | if let Err(e) = try_run_batch(&crunch).await { 71 | error!("{}", e); 72 | }; 73 | }); 74 | task::block_on(crunch_task); 75 | } 76 | 77 | fn spawn_and_restart_crunch_flakes_on_error() { 78 | let t = task::spawn(async { 79 | let config = CONFIG.clone(); 80 | let mut n = 1_u32; 81 | loop { 82 | let crunch: Crunch = Crunch::new().await; 83 | if let Err(e) = try_run_batch(&crunch).await { 84 | let sleep_min = u32::pow(config.error_interval, n); 85 | match e { 86 | CrunchError::MatrixError(_) => warn!("Matrix message skipped!"), 87 | CrunchError::DryRunError(err) => { 88 | let sleep_min = u32::pow(config.error_interval, n); 89 | warn!( 90 | "DryRunError: {}, on hold for {} secs", 91 | err, 92 | 60 * sleep_min 93 | ); 94 | thread::sleep(time::Duration::from_secs((60 * sleep_min).into())); 95 | } 96 | _ => { 97 | error!("{}", e); 98 | let message = format!("On hold for {} min!", sleep_min); 99 | let formatted_message = format!("
🚨 An error was raised -> crunch on hold for {} min while rescue is on the way 🚁 🚒 🚑 🚓

", sleep_min); 100 | if let Err(matrix_err) = 101 | crunch.send_message(&message, &formatted_message).await 102 | { 103 | warn!( 104 | "Failed to send error notification message: {}", 105 | matrix_err 106 | ); 107 | } 108 | } 109 | } 110 | thread::sleep(time::Duration::from_secs((60 * sleep_min).into())); 111 | n += 1; 112 | continue; 113 | }; 114 | thread::sleep(time::Duration::from_secs(config.interval)); 115 | } 116 | }); 117 | task::block_on(t); 118 | } 119 | 120 | fn spawn_and_restart_subscription_on_error() { 121 | let t = task::spawn(async { 122 | let config = CONFIG.clone(); 123 | let mut n = 1_u32; 124 | loop { 125 | let crunch: Crunch = Crunch::new().await; 126 | if let Err(e) = run_and_subscribe_era_paid_events(&crunch).await { 127 | match e { 128 | CrunchError::SubscriptionFinished 129 | | CrunchError::MatrixError(_) 130 | | CrunchError::RpcError(_) 131 | | CrunchError::RuntimeUpgradeDetected(_, _) => { 132 | warn!("{} - On hold for 30 secs!", e); 133 | thread::sleep(time::Duration::from_secs(30)); 134 | } 135 | CrunchError::DryRunError(_) => { 136 | let sleep_min = u32::pow(config.error_interval, n); 137 | warn!("{} - On hold for {} secs!", e, 60 * sleep_min); 138 | thread::sleep(time::Duration::from_secs((60 * sleep_min).into())); 139 | } 140 | CrunchError::SubxtError(ref subxt_err) 141 | if subxt_err.to_string().contains("connection was lost") => 142 | { 143 | warn!("{} - On hold for 30 secs!", subxt_err); 144 | thread::sleep(time::Duration::from_secs(30)); 145 | } 146 | _ => { 147 | error!("{}", e); 148 | let mut sleep_min = u32::pow(config.error_interval, n); 149 | if sleep_min > config.maximum_error_interval { 150 | sleep_min = config.maximum_error_interval; 151 | } 152 | let message = format!("On hold for {} min!", sleep_min); 153 | let formatted_message = format!("
🚨 An error was raised -> crunch on hold for {} min while rescue is on the way 🚁 🚒 🚑 🚓

", sleep_min); 154 | if let Err(matrix_err) = 155 | crunch.send_message(&message, &formatted_message).await 156 | { 157 | warn!( 158 | "Failed to send error notification message: {}", 159 | matrix_err 160 | ); 161 | } 162 | thread::sleep(time::Duration::from_secs((60 * sleep_min).into())); 163 | n += 1; 164 | continue; 165 | } 166 | } 167 | thread::sleep(time::Duration::from_secs(1)); 168 | }; 169 | } 170 | }); 171 | task::block_on(t); 172 | } 173 | 174 | async fn inspect(crunch: &Crunch) -> Result<(), CrunchError> { 175 | crunch.validate_genesis().await?; 176 | match crunch.runtime() { 177 | SupportedRuntime::Polkadot => crunch_polkadot::inspect(crunch).await, 178 | SupportedRuntime::Kusama => crunch_kusama::inspect(crunch).await, 179 | SupportedRuntime::Paseo => crunch_paseo::inspect(crunch).await, 180 | SupportedRuntime::Westend => crunch_westend::inspect(crunch).await, 181 | // _ => panic!("Unsupported runtime"), 182 | } 183 | } 184 | 185 | async fn try_run_batch(crunch: &Crunch) -> Result<(), CrunchError> { 186 | crunch.validate_genesis().await?; 187 | match crunch.runtime() { 188 | SupportedRuntime::Polkadot => crunch_polkadot::try_crunch(crunch).await, 189 | SupportedRuntime::Kusama => crunch_kusama::try_crunch(crunch).await, 190 | SupportedRuntime::Paseo => crunch_paseo::try_crunch(crunch).await, 191 | SupportedRuntime::Westend => crunch_westend::try_crunch(crunch).await, 192 | // _ => panic!("Unsupported runtime"), 193 | } 194 | } 195 | 196 | async fn run_and_subscribe_era_paid_events(crunch: &Crunch) -> Result<(), CrunchError> { 197 | crunch.validate_genesis().await?; 198 | match crunch.runtime() { 199 | SupportedRuntime::Polkadot => { 200 | crunch_polkadot::run_and_subscribe_era_paid_events(crunch).await 201 | } 202 | SupportedRuntime::Kusama => { 203 | crunch_kusama::run_and_subscribe_era_paid_events(crunch).await 204 | } 205 | SupportedRuntime::Paseo => { 206 | crunch_paseo::run_and_subscribe_era_paid_events(crunch).await 207 | } 208 | SupportedRuntime::Westend => { 209 | crunch_westend::run_and_subscribe_era_paid_events(crunch).await 210 | } // _ => panic!("Unsupported runtime"), 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /packages/chains/people-kusama/src/lib.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Aukbit Ltd. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | use async_recursion::async_recursion; 23 | use crunch_core::Crunch; 24 | use crunch_error::CrunchError; 25 | use crunch_report::{replace_emoji_lowercase, Validators}; 26 | use log::debug; 27 | use std::result::Result; 28 | use subxt::utils::AccountId32; 29 | 30 | #[subxt::subxt( 31 | runtime_metadata_path = "metadata/people_kusama_metadata_small.scale", 32 | derive_for_all_types = "Clone, PartialEq" 33 | )] 34 | mod people_metadata {} 35 | 36 | // Recursive function that looks up the identity of a validator given its stash, 37 | // outputs a tuple with [primary identity/ sub-identity], primary identity and whether 38 | // an identity is present. 39 | #[async_recursion] 40 | pub async fn get_display_name( 41 | crunch: &Crunch, 42 | stash: &AccountId32, 43 | sub_account_name: Option, 44 | ) -> Result<(String, String, bool), CrunchError> { 45 | if let Some(api) = crunch.people_client().clone() { 46 | let identity_of_addr = people_metadata::storage() 47 | .identity() 48 | .identity_of(stash.clone()); 49 | match api 50 | .storage() 51 | .at_latest() 52 | .await? 53 | .fetch(&identity_of_addr) 54 | .await? 55 | { 56 | Some(identity) => { 57 | debug!("identity {:?}", identity); 58 | let parent = parse_identity_data(identity.info.display); 59 | let name = match sub_account_name { 60 | Some(child) => format!("{}/{}", &parent, child), 61 | None => parent.clone(), 62 | }; 63 | Ok((name, parent.clone(), true)) 64 | } 65 | None => { 66 | let super_of_addr = people_metadata::storage() 67 | .identity() 68 | .super_of(stash.clone()); 69 | if let Some((parent_account, data)) = api 70 | .storage() 71 | .at_latest() 72 | .await? 73 | .fetch(&super_of_addr) 74 | .await? 75 | { 76 | let sub_account_name = parse_identity_data(data); 77 | return get_display_name( 78 | &crunch, 79 | &parent_account, 80 | Some(sub_account_name.to_string()), 81 | ) 82 | .await; 83 | } else { 84 | let s = &stash.to_string(); 85 | let stash_address = format!("{}...{}", &s[..6], &s[s.len() - 6..]); 86 | Ok((stash_address, "".to_string(), false)) 87 | } 88 | } 89 | } 90 | } else { 91 | let s = &stash.to_string(); 92 | let stash_address = format!("{}...{}", &s[..6], &s[s.len() - 6..]); 93 | Ok((stash_address, "".to_string(), false)) 94 | } 95 | } 96 | 97 | // 98 | fn parse_identity_data( 99 | data: people_metadata::runtime_types::pallet_identity::types::Data, 100 | ) -> String { 101 | match data { 102 | people_metadata::runtime_types::pallet_identity::types::Data::Raw0(bytes) => { 103 | str(bytes.to_vec()) 104 | } 105 | people_metadata::runtime_types::pallet_identity::types::Data::Raw1(bytes) => { 106 | str(bytes.to_vec()) 107 | } 108 | people_metadata::runtime_types::pallet_identity::types::Data::Raw2(bytes) => { 109 | str(bytes.to_vec()) 110 | } 111 | people_metadata::runtime_types::pallet_identity::types::Data::Raw3(bytes) => { 112 | str(bytes.to_vec()) 113 | } 114 | people_metadata::runtime_types::pallet_identity::types::Data::Raw4(bytes) => { 115 | str(bytes.to_vec()) 116 | } 117 | people_metadata::runtime_types::pallet_identity::types::Data::Raw5(bytes) => { 118 | str(bytes.to_vec()) 119 | } 120 | people_metadata::runtime_types::pallet_identity::types::Data::Raw6(bytes) => { 121 | str(bytes.to_vec()) 122 | } 123 | people_metadata::runtime_types::pallet_identity::types::Data::Raw7(bytes) => { 124 | str(bytes.to_vec()) 125 | } 126 | people_metadata::runtime_types::pallet_identity::types::Data::Raw8(bytes) => { 127 | str(bytes.to_vec()) 128 | } 129 | people_metadata::runtime_types::pallet_identity::types::Data::Raw9(bytes) => { 130 | str(bytes.to_vec()) 131 | } 132 | people_metadata::runtime_types::pallet_identity::types::Data::Raw10(bytes) => { 133 | str(bytes.to_vec()) 134 | } 135 | people_metadata::runtime_types::pallet_identity::types::Data::Raw11(bytes) => { 136 | str(bytes.to_vec()) 137 | } 138 | people_metadata::runtime_types::pallet_identity::types::Data::Raw12(bytes) => { 139 | str(bytes.to_vec()) 140 | } 141 | people_metadata::runtime_types::pallet_identity::types::Data::Raw13(bytes) => { 142 | str(bytes.to_vec()) 143 | } 144 | people_metadata::runtime_types::pallet_identity::types::Data::Raw14(bytes) => { 145 | str(bytes.to_vec()) 146 | } 147 | people_metadata::runtime_types::pallet_identity::types::Data::Raw15(bytes) => { 148 | str(bytes.to_vec()) 149 | } 150 | people_metadata::runtime_types::pallet_identity::types::Data::Raw16(bytes) => { 151 | str(bytes.to_vec()) 152 | } 153 | people_metadata::runtime_types::pallet_identity::types::Data::Raw17(bytes) => { 154 | str(bytes.to_vec()) 155 | } 156 | people_metadata::runtime_types::pallet_identity::types::Data::Raw18(bytes) => { 157 | str(bytes.to_vec()) 158 | } 159 | people_metadata::runtime_types::pallet_identity::types::Data::Raw19(bytes) => { 160 | str(bytes.to_vec()) 161 | } 162 | people_metadata::runtime_types::pallet_identity::types::Data::Raw20(bytes) => { 163 | str(bytes.to_vec()) 164 | } 165 | people_metadata::runtime_types::pallet_identity::types::Data::Raw21(bytes) => { 166 | str(bytes.to_vec()) 167 | } 168 | people_metadata::runtime_types::pallet_identity::types::Data::Raw22(bytes) => { 169 | str(bytes.to_vec()) 170 | } 171 | people_metadata::runtime_types::pallet_identity::types::Data::Raw23(bytes) => { 172 | str(bytes.to_vec()) 173 | } 174 | people_metadata::runtime_types::pallet_identity::types::Data::Raw24(bytes) => { 175 | str(bytes.to_vec()) 176 | } 177 | people_metadata::runtime_types::pallet_identity::types::Data::Raw25(bytes) => { 178 | str(bytes.to_vec()) 179 | } 180 | people_metadata::runtime_types::pallet_identity::types::Data::Raw26(bytes) => { 181 | str(bytes.to_vec()) 182 | } 183 | people_metadata::runtime_types::pallet_identity::types::Data::Raw27(bytes) => { 184 | str(bytes.to_vec()) 185 | } 186 | people_metadata::runtime_types::pallet_identity::types::Data::Raw28(bytes) => { 187 | str(bytes.to_vec()) 188 | } 189 | people_metadata::runtime_types::pallet_identity::types::Data::Raw29(bytes) => { 190 | str(bytes.to_vec()) 191 | } 192 | people_metadata::runtime_types::pallet_identity::types::Data::Raw30(bytes) => { 193 | str(bytes.to_vec()) 194 | } 195 | people_metadata::runtime_types::pallet_identity::types::Data::Raw31(bytes) => { 196 | str(bytes.to_vec()) 197 | } 198 | people_metadata::runtime_types::pallet_identity::types::Data::Raw32(bytes) => { 199 | str(bytes.to_vec()) 200 | } 201 | _ => format!("???"), 202 | } 203 | } 204 | 205 | fn str(bytes: Vec) -> String { 206 | format!("{}", String::from_utf8(bytes).expect("Identity not utf-8")) 207 | } 208 | 209 | // Provides a distinct and sorted vector of parent identities by string 210 | // where there are entries without identities, these are placed to the end of the vector 211 | pub fn get_distinct_parent_identites(validators: Validators) -> Vec { 212 | // Obtains a sorted distinct list of valid identities 213 | let mut parent_identities: Vec = validators 214 | .clone() 215 | .iter() 216 | .filter(|val| val.has_identity) 217 | .map(|val| replace_emoji_lowercase(&val.parent_identity)) 218 | .collect(); 219 | parent_identities.sort(); 220 | parent_identities.dedup(); 221 | 222 | // Note: If there are stashes/validators without identity they should be grouped in the bottom of the list 223 | let none_counter = validators 224 | .clone() 225 | .iter() 226 | .filter(|val| !val.has_identity) 227 | .count(); 228 | 229 | if none_counter > 0 { 230 | parent_identities.push("".to_string()); 231 | } 232 | 233 | parent_identities 234 | } 235 | -------------------------------------------------------------------------------- /packages/chains/people-paseo/src/lib.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Aukbit Ltd. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | use async_recursion::async_recursion; 23 | use crunch_core::Crunch; 24 | use crunch_error::CrunchError; 25 | use crunch_report::{replace_emoji_lowercase, Validators}; 26 | use log::debug; 27 | use std::result::Result; 28 | use subxt::utils::AccountId32; 29 | 30 | #[subxt::subxt( 31 | runtime_metadata_path = "metadata/people_paseo_metadata_small.scale", 32 | derive_for_all_types = "Clone, PartialEq" 33 | )] 34 | mod people_metadata {} 35 | 36 | // Recursive function that looks up the identity of a validator given its stash, 37 | // outputs a tuple with [primary identity/ sub-identity], primary identity and whether 38 | // an identity is present. 39 | #[async_recursion] 40 | pub async fn get_display_name( 41 | crunch: &Crunch, 42 | stash: &AccountId32, 43 | sub_account_name: Option, 44 | ) -> Result<(String, String, bool), CrunchError> { 45 | if let Some(api) = crunch.people_client().clone() { 46 | let identity_of_addr = people_metadata::storage() 47 | .identity() 48 | .identity_of(stash.clone()); 49 | match api 50 | .storage() 51 | .at_latest() 52 | .await? 53 | .fetch(&identity_of_addr) 54 | .await? 55 | { 56 | Some(identity) => { 57 | debug!("identity {:?}", identity); 58 | let parent = parse_identity_data(identity.info.display); 59 | let name = match sub_account_name { 60 | Some(child) => format!("{}/{}", &parent, child), 61 | None => parent.clone(), 62 | }; 63 | Ok((name, parent.clone(), true)) 64 | } 65 | None => { 66 | let super_of_addr = people_metadata::storage() 67 | .identity() 68 | .super_of(stash.clone()); 69 | if let Some((parent_account, data)) = api 70 | .storage() 71 | .at_latest() 72 | .await? 73 | .fetch(&super_of_addr) 74 | .await? 75 | { 76 | let sub_account_name = parse_identity_data(data); 77 | return get_display_name( 78 | &crunch, 79 | &parent_account, 80 | Some(sub_account_name.to_string()), 81 | ) 82 | .await; 83 | } else { 84 | let s = &stash.to_string(); 85 | let stash_address = format!("{}...{}", &s[..6], &s[s.len() - 6..]); 86 | Ok((stash_address, "".to_string(), false)) 87 | } 88 | } 89 | } 90 | } else { 91 | let s = &stash.to_string(); 92 | let stash_address = format!("{}...{}", &s[..6], &s[s.len() - 6..]); 93 | Ok((stash_address, "".to_string(), false)) 94 | } 95 | } 96 | 97 | // 98 | fn parse_identity_data( 99 | data: people_metadata::runtime_types::pallet_identity::types::Data, 100 | ) -> String { 101 | match data { 102 | people_metadata::runtime_types::pallet_identity::types::Data::Raw0(bytes) => { 103 | str(bytes.to_vec()) 104 | } 105 | people_metadata::runtime_types::pallet_identity::types::Data::Raw1(bytes) => { 106 | str(bytes.to_vec()) 107 | } 108 | people_metadata::runtime_types::pallet_identity::types::Data::Raw2(bytes) => { 109 | str(bytes.to_vec()) 110 | } 111 | people_metadata::runtime_types::pallet_identity::types::Data::Raw3(bytes) => { 112 | str(bytes.to_vec()) 113 | } 114 | people_metadata::runtime_types::pallet_identity::types::Data::Raw4(bytes) => { 115 | str(bytes.to_vec()) 116 | } 117 | people_metadata::runtime_types::pallet_identity::types::Data::Raw5(bytes) => { 118 | str(bytes.to_vec()) 119 | } 120 | people_metadata::runtime_types::pallet_identity::types::Data::Raw6(bytes) => { 121 | str(bytes.to_vec()) 122 | } 123 | people_metadata::runtime_types::pallet_identity::types::Data::Raw7(bytes) => { 124 | str(bytes.to_vec()) 125 | } 126 | people_metadata::runtime_types::pallet_identity::types::Data::Raw8(bytes) => { 127 | str(bytes.to_vec()) 128 | } 129 | people_metadata::runtime_types::pallet_identity::types::Data::Raw9(bytes) => { 130 | str(bytes.to_vec()) 131 | } 132 | people_metadata::runtime_types::pallet_identity::types::Data::Raw10(bytes) => { 133 | str(bytes.to_vec()) 134 | } 135 | people_metadata::runtime_types::pallet_identity::types::Data::Raw11(bytes) => { 136 | str(bytes.to_vec()) 137 | } 138 | people_metadata::runtime_types::pallet_identity::types::Data::Raw12(bytes) => { 139 | str(bytes.to_vec()) 140 | } 141 | people_metadata::runtime_types::pallet_identity::types::Data::Raw13(bytes) => { 142 | str(bytes.to_vec()) 143 | } 144 | people_metadata::runtime_types::pallet_identity::types::Data::Raw14(bytes) => { 145 | str(bytes.to_vec()) 146 | } 147 | people_metadata::runtime_types::pallet_identity::types::Data::Raw15(bytes) => { 148 | str(bytes.to_vec()) 149 | } 150 | people_metadata::runtime_types::pallet_identity::types::Data::Raw16(bytes) => { 151 | str(bytes.to_vec()) 152 | } 153 | people_metadata::runtime_types::pallet_identity::types::Data::Raw17(bytes) => { 154 | str(bytes.to_vec()) 155 | } 156 | people_metadata::runtime_types::pallet_identity::types::Data::Raw18(bytes) => { 157 | str(bytes.to_vec()) 158 | } 159 | people_metadata::runtime_types::pallet_identity::types::Data::Raw19(bytes) => { 160 | str(bytes.to_vec()) 161 | } 162 | people_metadata::runtime_types::pallet_identity::types::Data::Raw20(bytes) => { 163 | str(bytes.to_vec()) 164 | } 165 | people_metadata::runtime_types::pallet_identity::types::Data::Raw21(bytes) => { 166 | str(bytes.to_vec()) 167 | } 168 | people_metadata::runtime_types::pallet_identity::types::Data::Raw22(bytes) => { 169 | str(bytes.to_vec()) 170 | } 171 | people_metadata::runtime_types::pallet_identity::types::Data::Raw23(bytes) => { 172 | str(bytes.to_vec()) 173 | } 174 | people_metadata::runtime_types::pallet_identity::types::Data::Raw24(bytes) => { 175 | str(bytes.to_vec()) 176 | } 177 | people_metadata::runtime_types::pallet_identity::types::Data::Raw25(bytes) => { 178 | str(bytes.to_vec()) 179 | } 180 | people_metadata::runtime_types::pallet_identity::types::Data::Raw26(bytes) => { 181 | str(bytes.to_vec()) 182 | } 183 | people_metadata::runtime_types::pallet_identity::types::Data::Raw27(bytes) => { 184 | str(bytes.to_vec()) 185 | } 186 | people_metadata::runtime_types::pallet_identity::types::Data::Raw28(bytes) => { 187 | str(bytes.to_vec()) 188 | } 189 | people_metadata::runtime_types::pallet_identity::types::Data::Raw29(bytes) => { 190 | str(bytes.to_vec()) 191 | } 192 | people_metadata::runtime_types::pallet_identity::types::Data::Raw30(bytes) => { 193 | str(bytes.to_vec()) 194 | } 195 | people_metadata::runtime_types::pallet_identity::types::Data::Raw31(bytes) => { 196 | str(bytes.to_vec()) 197 | } 198 | people_metadata::runtime_types::pallet_identity::types::Data::Raw32(bytes) => { 199 | str(bytes.to_vec()) 200 | } 201 | _ => format!("???"), 202 | } 203 | } 204 | 205 | fn str(bytes: Vec) -> String { 206 | format!("{}", String::from_utf8(bytes).expect("Identity not utf-8")) 207 | } 208 | 209 | // Provides a distinct and sorted vector of parent identities by string 210 | // where there are entries without identities, these are placed to the end of the vector 211 | pub fn get_distinct_parent_identites(validators: Validators) -> Vec { 212 | // Obtains a sorted distinct list of valid identities 213 | let mut parent_identities: Vec = validators 214 | .clone() 215 | .iter() 216 | .filter(|val| val.has_identity) 217 | .map(|val| replace_emoji_lowercase(&val.parent_identity)) 218 | .collect(); 219 | parent_identities.sort(); 220 | parent_identities.dedup(); 221 | 222 | // Note: If there are stashes/validators without identity they should be grouped in the bottom of the list 223 | let none_counter = validators 224 | .clone() 225 | .iter() 226 | .filter(|val| !val.has_identity) 227 | .count(); 228 | 229 | if none_counter > 0 { 230 | parent_identities.push("".to_string()); 231 | } 232 | 233 | parent_identities 234 | } 235 | -------------------------------------------------------------------------------- /packages/chains/people-polkadot/src/lib.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Aukbit Ltd. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | use async_recursion::async_recursion; 23 | use crunch_core::Crunch; 24 | use crunch_error::CrunchError; 25 | use crunch_report::{replace_emoji_lowercase, Validators}; 26 | use log::debug; 27 | use std::result::Result; 28 | use subxt::utils::AccountId32; 29 | 30 | #[subxt::subxt( 31 | runtime_metadata_path = "metadata/people_polkadot_metadata_small.scale", 32 | derive_for_all_types = "Clone, PartialEq" 33 | )] 34 | mod people_metadata {} 35 | 36 | // Recursive function that looks up the identity of a validator given its stash, 37 | // outputs a tuple with [primary identity/ sub-identity], primary identity and whether 38 | // an identity is present. 39 | #[async_recursion] 40 | pub async fn get_display_name( 41 | crunch: &Crunch, 42 | stash: &AccountId32, 43 | sub_account_name: Option, 44 | ) -> Result<(String, String, bool), CrunchError> { 45 | if let Some(api) = crunch.people_client().clone() { 46 | let identity_of_addr = people_metadata::storage() 47 | .identity() 48 | .identity_of(stash.clone()); 49 | match api 50 | .storage() 51 | .at_latest() 52 | .await? 53 | .fetch(&identity_of_addr) 54 | .await? 55 | { 56 | Some(identity) => { 57 | debug!("identity {:?}", identity); 58 | let parent = parse_identity_data(identity.info.display); 59 | let name = match sub_account_name { 60 | Some(child) => format!("{}/{}", &parent, child), 61 | None => parent.clone(), 62 | }; 63 | Ok((name, parent.clone(), true)) 64 | } 65 | None => { 66 | let super_of_addr = people_metadata::storage() 67 | .identity() 68 | .super_of(stash.clone()); 69 | if let Some((parent_account, data)) = api 70 | .storage() 71 | .at_latest() 72 | .await? 73 | .fetch(&super_of_addr) 74 | .await? 75 | { 76 | let sub_account_name = parse_identity_data(data); 77 | return get_display_name( 78 | &crunch, 79 | &parent_account, 80 | Some(sub_account_name.to_string()), 81 | ) 82 | .await; 83 | } else { 84 | let s = &stash.to_string(); 85 | let stash_address = format!("{}...{}", &s[..6], &s[s.len() - 6..]); 86 | Ok((stash_address, "".to_string(), false)) 87 | } 88 | } 89 | } 90 | } else { 91 | let s = &stash.to_string(); 92 | let stash_address = format!("{}...{}", &s[..6], &s[s.len() - 6..]); 93 | Ok((stash_address, "".to_string(), false)) 94 | } 95 | } 96 | 97 | // 98 | fn parse_identity_data( 99 | data: people_metadata::runtime_types::pallet_identity::types::Data, 100 | ) -> String { 101 | match data { 102 | people_metadata::runtime_types::pallet_identity::types::Data::Raw0(bytes) => { 103 | str(bytes.to_vec()) 104 | } 105 | people_metadata::runtime_types::pallet_identity::types::Data::Raw1(bytes) => { 106 | str(bytes.to_vec()) 107 | } 108 | people_metadata::runtime_types::pallet_identity::types::Data::Raw2(bytes) => { 109 | str(bytes.to_vec()) 110 | } 111 | people_metadata::runtime_types::pallet_identity::types::Data::Raw3(bytes) => { 112 | str(bytes.to_vec()) 113 | } 114 | people_metadata::runtime_types::pallet_identity::types::Data::Raw4(bytes) => { 115 | str(bytes.to_vec()) 116 | } 117 | people_metadata::runtime_types::pallet_identity::types::Data::Raw5(bytes) => { 118 | str(bytes.to_vec()) 119 | } 120 | people_metadata::runtime_types::pallet_identity::types::Data::Raw6(bytes) => { 121 | str(bytes.to_vec()) 122 | } 123 | people_metadata::runtime_types::pallet_identity::types::Data::Raw7(bytes) => { 124 | str(bytes.to_vec()) 125 | } 126 | people_metadata::runtime_types::pallet_identity::types::Data::Raw8(bytes) => { 127 | str(bytes.to_vec()) 128 | } 129 | people_metadata::runtime_types::pallet_identity::types::Data::Raw9(bytes) => { 130 | str(bytes.to_vec()) 131 | } 132 | people_metadata::runtime_types::pallet_identity::types::Data::Raw10(bytes) => { 133 | str(bytes.to_vec()) 134 | } 135 | people_metadata::runtime_types::pallet_identity::types::Data::Raw11(bytes) => { 136 | str(bytes.to_vec()) 137 | } 138 | people_metadata::runtime_types::pallet_identity::types::Data::Raw12(bytes) => { 139 | str(bytes.to_vec()) 140 | } 141 | people_metadata::runtime_types::pallet_identity::types::Data::Raw13(bytes) => { 142 | str(bytes.to_vec()) 143 | } 144 | people_metadata::runtime_types::pallet_identity::types::Data::Raw14(bytes) => { 145 | str(bytes.to_vec()) 146 | } 147 | people_metadata::runtime_types::pallet_identity::types::Data::Raw15(bytes) => { 148 | str(bytes.to_vec()) 149 | } 150 | people_metadata::runtime_types::pallet_identity::types::Data::Raw16(bytes) => { 151 | str(bytes.to_vec()) 152 | } 153 | people_metadata::runtime_types::pallet_identity::types::Data::Raw17(bytes) => { 154 | str(bytes.to_vec()) 155 | } 156 | people_metadata::runtime_types::pallet_identity::types::Data::Raw18(bytes) => { 157 | str(bytes.to_vec()) 158 | } 159 | people_metadata::runtime_types::pallet_identity::types::Data::Raw19(bytes) => { 160 | str(bytes.to_vec()) 161 | } 162 | people_metadata::runtime_types::pallet_identity::types::Data::Raw20(bytes) => { 163 | str(bytes.to_vec()) 164 | } 165 | people_metadata::runtime_types::pallet_identity::types::Data::Raw21(bytes) => { 166 | str(bytes.to_vec()) 167 | } 168 | people_metadata::runtime_types::pallet_identity::types::Data::Raw22(bytes) => { 169 | str(bytes.to_vec()) 170 | } 171 | people_metadata::runtime_types::pallet_identity::types::Data::Raw23(bytes) => { 172 | str(bytes.to_vec()) 173 | } 174 | people_metadata::runtime_types::pallet_identity::types::Data::Raw24(bytes) => { 175 | str(bytes.to_vec()) 176 | } 177 | people_metadata::runtime_types::pallet_identity::types::Data::Raw25(bytes) => { 178 | str(bytes.to_vec()) 179 | } 180 | people_metadata::runtime_types::pallet_identity::types::Data::Raw26(bytes) => { 181 | str(bytes.to_vec()) 182 | } 183 | people_metadata::runtime_types::pallet_identity::types::Data::Raw27(bytes) => { 184 | str(bytes.to_vec()) 185 | } 186 | people_metadata::runtime_types::pallet_identity::types::Data::Raw28(bytes) => { 187 | str(bytes.to_vec()) 188 | } 189 | people_metadata::runtime_types::pallet_identity::types::Data::Raw29(bytes) => { 190 | str(bytes.to_vec()) 191 | } 192 | people_metadata::runtime_types::pallet_identity::types::Data::Raw30(bytes) => { 193 | str(bytes.to_vec()) 194 | } 195 | people_metadata::runtime_types::pallet_identity::types::Data::Raw31(bytes) => { 196 | str(bytes.to_vec()) 197 | } 198 | people_metadata::runtime_types::pallet_identity::types::Data::Raw32(bytes) => { 199 | str(bytes.to_vec()) 200 | } 201 | _ => format!("???"), 202 | } 203 | } 204 | 205 | fn str(bytes: Vec) -> String { 206 | format!("{}", String::from_utf8(bytes).expect("Identity not utf-8")) 207 | } 208 | 209 | // Provides a distinct and sorted vector of parent identities by string 210 | // where there are entries without identities, these are placed to the end of the vector 211 | pub fn get_distinct_parent_identites(validators: Validators) -> Vec { 212 | // Obtains a sorted distinct list of valid identities 213 | let mut parent_identities: Vec = validators 214 | .clone() 215 | .iter() 216 | .filter(|val| val.has_identity) 217 | .map(|val| replace_emoji_lowercase(&val.parent_identity)) 218 | .collect(); 219 | parent_identities.sort(); 220 | parent_identities.dedup(); 221 | 222 | // Note: If there are stashes/validators without identity they should be grouped in the bottom of the list 223 | let none_counter = validators 224 | .clone() 225 | .iter() 226 | .filter(|val| !val.has_identity) 227 | .count(); 228 | 229 | if none_counter > 0 { 230 | parent_identities.push("".to_string()); 231 | } 232 | 233 | parent_identities 234 | } 235 | -------------------------------------------------------------------------------- /packages/chains/people-westend/src/lib.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Aukbit Ltd. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | use async_recursion::async_recursion; 23 | use crunch_core::Crunch; 24 | use crunch_error::CrunchError; 25 | use crunch_report::{replace_emoji_lowercase, Validators}; 26 | use log::debug; 27 | use std::result::Result; 28 | use subxt::utils::AccountId32; 29 | 30 | #[subxt::subxt( 31 | runtime_metadata_path = "metadata/people_westend_metadata_small.scale", 32 | derive_for_all_types = "Clone, PartialEq" 33 | )] 34 | mod people_metadata {} 35 | 36 | // Recursive function that looks up the identity of a validator given its stash, 37 | // outputs a tuple with [primary identity/ sub-identity], primary identity and whether 38 | // an identity is present. 39 | #[async_recursion] 40 | pub async fn get_display_name( 41 | crunch: &Crunch, 42 | stash: &AccountId32, 43 | sub_account_name: Option, 44 | ) -> Result<(String, String, bool), CrunchError> { 45 | if let Some(api) = crunch.people_client().clone() { 46 | let identity_of_addr = people_metadata::storage() 47 | .identity() 48 | .identity_of(stash.clone()); 49 | match api 50 | .storage() 51 | .at_latest() 52 | .await? 53 | .fetch(&identity_of_addr) 54 | .await? 55 | { 56 | Some(identity) => { 57 | debug!("identity {:?}", identity); 58 | let parent = parse_identity_data(identity.info.display); 59 | let name = match sub_account_name { 60 | Some(child) => format!("{}/{}", &parent, child), 61 | None => parent.clone(), 62 | }; 63 | Ok((name, parent.clone(), true)) 64 | } 65 | None => { 66 | let super_of_addr = people_metadata::storage() 67 | .identity() 68 | .super_of(stash.clone()); 69 | if let Some((parent_account, data)) = api 70 | .storage() 71 | .at_latest() 72 | .await? 73 | .fetch(&super_of_addr) 74 | .await? 75 | { 76 | let sub_account_name = parse_identity_data(data); 77 | return get_display_name( 78 | &crunch, 79 | &parent_account, 80 | Some(sub_account_name.to_string()), 81 | ) 82 | .await; 83 | } else { 84 | let s = &stash.to_string(); 85 | let stash_address = format!("{}...{}", &s[..6], &s[s.len() - 6..]); 86 | Ok((stash_address, "".to_string(), false)) 87 | } 88 | } 89 | } 90 | } else { 91 | let s = &stash.to_string(); 92 | let stash_address = format!("{}...{}", &s[..6], &s[s.len() - 6..]); 93 | Ok((stash_address, "".to_string(), false)) 94 | } 95 | } 96 | 97 | // 98 | fn parse_identity_data( 99 | data: people_metadata::runtime_types::pallet_identity::types::Data, 100 | ) -> String { 101 | match data { 102 | people_metadata::runtime_types::pallet_identity::types::Data::Raw0(bytes) => { 103 | str(bytes.to_vec()) 104 | } 105 | people_metadata::runtime_types::pallet_identity::types::Data::Raw1(bytes) => { 106 | str(bytes.to_vec()) 107 | } 108 | people_metadata::runtime_types::pallet_identity::types::Data::Raw2(bytes) => { 109 | str(bytes.to_vec()) 110 | } 111 | people_metadata::runtime_types::pallet_identity::types::Data::Raw3(bytes) => { 112 | str(bytes.to_vec()) 113 | } 114 | people_metadata::runtime_types::pallet_identity::types::Data::Raw4(bytes) => { 115 | str(bytes.to_vec()) 116 | } 117 | people_metadata::runtime_types::pallet_identity::types::Data::Raw5(bytes) => { 118 | str(bytes.to_vec()) 119 | } 120 | people_metadata::runtime_types::pallet_identity::types::Data::Raw6(bytes) => { 121 | str(bytes.to_vec()) 122 | } 123 | people_metadata::runtime_types::pallet_identity::types::Data::Raw7(bytes) => { 124 | str(bytes.to_vec()) 125 | } 126 | people_metadata::runtime_types::pallet_identity::types::Data::Raw8(bytes) => { 127 | str(bytes.to_vec()) 128 | } 129 | people_metadata::runtime_types::pallet_identity::types::Data::Raw9(bytes) => { 130 | str(bytes.to_vec()) 131 | } 132 | people_metadata::runtime_types::pallet_identity::types::Data::Raw10(bytes) => { 133 | str(bytes.to_vec()) 134 | } 135 | people_metadata::runtime_types::pallet_identity::types::Data::Raw11(bytes) => { 136 | str(bytes.to_vec()) 137 | } 138 | people_metadata::runtime_types::pallet_identity::types::Data::Raw12(bytes) => { 139 | str(bytes.to_vec()) 140 | } 141 | people_metadata::runtime_types::pallet_identity::types::Data::Raw13(bytes) => { 142 | str(bytes.to_vec()) 143 | } 144 | people_metadata::runtime_types::pallet_identity::types::Data::Raw14(bytes) => { 145 | str(bytes.to_vec()) 146 | } 147 | people_metadata::runtime_types::pallet_identity::types::Data::Raw15(bytes) => { 148 | str(bytes.to_vec()) 149 | } 150 | people_metadata::runtime_types::pallet_identity::types::Data::Raw16(bytes) => { 151 | str(bytes.to_vec()) 152 | } 153 | people_metadata::runtime_types::pallet_identity::types::Data::Raw17(bytes) => { 154 | str(bytes.to_vec()) 155 | } 156 | people_metadata::runtime_types::pallet_identity::types::Data::Raw18(bytes) => { 157 | str(bytes.to_vec()) 158 | } 159 | people_metadata::runtime_types::pallet_identity::types::Data::Raw19(bytes) => { 160 | str(bytes.to_vec()) 161 | } 162 | people_metadata::runtime_types::pallet_identity::types::Data::Raw20(bytes) => { 163 | str(bytes.to_vec()) 164 | } 165 | people_metadata::runtime_types::pallet_identity::types::Data::Raw21(bytes) => { 166 | str(bytes.to_vec()) 167 | } 168 | people_metadata::runtime_types::pallet_identity::types::Data::Raw22(bytes) => { 169 | str(bytes.to_vec()) 170 | } 171 | people_metadata::runtime_types::pallet_identity::types::Data::Raw23(bytes) => { 172 | str(bytes.to_vec()) 173 | } 174 | people_metadata::runtime_types::pallet_identity::types::Data::Raw24(bytes) => { 175 | str(bytes.to_vec()) 176 | } 177 | people_metadata::runtime_types::pallet_identity::types::Data::Raw25(bytes) => { 178 | str(bytes.to_vec()) 179 | } 180 | people_metadata::runtime_types::pallet_identity::types::Data::Raw26(bytes) => { 181 | str(bytes.to_vec()) 182 | } 183 | people_metadata::runtime_types::pallet_identity::types::Data::Raw27(bytes) => { 184 | str(bytes.to_vec()) 185 | } 186 | people_metadata::runtime_types::pallet_identity::types::Data::Raw28(bytes) => { 187 | str(bytes.to_vec()) 188 | } 189 | people_metadata::runtime_types::pallet_identity::types::Data::Raw29(bytes) => { 190 | str(bytes.to_vec()) 191 | } 192 | people_metadata::runtime_types::pallet_identity::types::Data::Raw30(bytes) => { 193 | str(bytes.to_vec()) 194 | } 195 | people_metadata::runtime_types::pallet_identity::types::Data::Raw31(bytes) => { 196 | str(bytes.to_vec()) 197 | } 198 | people_metadata::runtime_types::pallet_identity::types::Data::Raw32(bytes) => { 199 | str(bytes.to_vec()) 200 | } 201 | _ => format!("???"), 202 | } 203 | } 204 | 205 | fn str(bytes: Vec) -> String { 206 | format!("{}", String::from_utf8(bytes).expect("Identity not utf-8")) 207 | } 208 | 209 | // Provides a distinct and sorted vector of parent identities by string 210 | // where there are entries without identities, these are placed to the end of the vector 211 | pub fn get_distinct_parent_identites(validators: Validators) -> Vec { 212 | // Obtains a sorted distinct list of valid identities 213 | let mut parent_identities: Vec = validators 214 | .clone() 215 | .iter() 216 | .filter(|val| val.has_identity) 217 | .map(|val| replace_emoji_lowercase(&val.parent_identity)) 218 | .collect(); 219 | parent_identities.sort(); 220 | parent_identities.dedup(); 221 | 222 | // Note: If there are stashes/validators without identity they should be grouped in the bottom of the list 223 | let none_counter = validators 224 | .clone() 225 | .iter() 226 | .filter(|val| !val.has_identity) 227 | .count(); 228 | 229 | if none_counter > 0 { 230 | parent_identities.push("".to_string()); 231 | } 232 | 233 | parent_identities 234 | } 235 | -------------------------------------------------------------------------------- /.github/workflows/create_release.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | # Sequence of patterns matched against refs/tags 4 | tags: 5 | - "v*" # Push events to matching v*, i.e. v1.0, v20.15.10 6 | 7 | name: Rust CI - Create Release 8 | 9 | jobs: 10 | build-ubuntu-latest: 11 | name: Build on Ubuntu latest 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Install Rust latest stable 17 | uses: actions-rs/toolchain@v1 18 | with: 19 | profile: minimal 20 | toolchain: stable 21 | override: true 22 | components: rustfmt, clippy 23 | 24 | - name: Run cargo test 25 | uses: actions-rs/cargo@v1 26 | env: 27 | CRUNCH_CONFIG_FILENAME: .env.example 28 | with: 29 | command: test 30 | 31 | - name: Clean cargo target 32 | uses: actions-rs/cargo@v1 33 | with: 34 | command: clean 35 | 36 | - name: Run cargo build 37 | uses: actions-rs/cargo@v1 38 | env: 39 | CRUNCH_CONFIG_FILENAME: .env.example 40 | with: 41 | command: build 42 | args: --release 43 | 44 | - name: Generate SHA-256 hash file 45 | run: | 46 | cd target/release 47 | sha256sum crunch > crunch.sha256 48 | 49 | # Upload the binary and sha256 file as artifacts 50 | - uses: actions/upload-artifact@v4 51 | with: 52 | name: crunch 53 | path: target/release 54 | 55 | - uses: actions/upload-artifact@v4 56 | with: 57 | name: crunch.sha256 58 | path: target/release 59 | 60 | build-ubuntu-2204: 61 | name: Build on Ubuntu 22.04 62 | runs-on: ubuntu-22.04 63 | steps: 64 | - uses: actions/checkout@v4 65 | 66 | - name: Install Rust latest stable 67 | uses: actions-rs/toolchain@v1 68 | with: 69 | profile: minimal 70 | toolchain: stable 71 | override: true 72 | components: rustfmt, clippy 73 | 74 | - name: Run cargo test 75 | uses: actions-rs/cargo@v1 76 | env: 77 | CRUNCH_CONFIG_FILENAME: .env.example 78 | with: 79 | command: test 80 | 81 | - name: Clean cargo target 82 | uses: actions-rs/cargo@v1 83 | with: 84 | command: clean 85 | 86 | - name: Run cargo build 87 | uses: actions-rs/cargo@v1 88 | env: 89 | CRUNCH_CONFIG_FILENAME: .env.example 90 | with: 91 | command: build 92 | args: --release 93 | 94 | - name: Generate SHA-256 hash file 95 | run: | 96 | cd ./target/release 97 | sha256sum crunch > crunch.sha256 98 | 99 | - name: Rename the binary and sha256 file to include the target version 100 | run: | 101 | mv target/release/crunch target/release/crunch.ubuntu-2204 102 | mv target/release/crunch.sha256 target/release/crunch.sha256.ubuntu-2204 103 | 104 | # Upload the binary and sha256 file as artifacts 105 | - uses: actions/upload-artifact@v4 106 | with: 107 | name: crunch.ubuntu-2204 108 | path: target/release 109 | 110 | - uses: actions/upload-artifact@v4 111 | with: 112 | name: crunch.sha256.ubuntu-2204 113 | path: target/release 114 | 115 | build-linux-musl: 116 | name: Build on Ubuntu and target Linux musl 117 | runs-on: ubuntu-latest 118 | steps: 119 | - uses: actions/checkout@v4 120 | 121 | - name: Install Rust latest stable 122 | uses: actions-rs/toolchain@v1 123 | with: 124 | profile: minimal 125 | toolchain: stable 126 | override: true 127 | components: rustfmt, clippy 128 | 129 | - name: Run cargo test 130 | uses: actions-rs/cargo@v1 131 | env: 132 | CRUNCH_CONFIG_FILENAME: .env.example 133 | with: 134 | command: test 135 | 136 | - name: Add musl dependencies 137 | run: | 138 | rustup target add x86_64-unknown-linux-musl 139 | sudo apt install musl-tools 140 | 141 | - name: Clean cargo target 142 | uses: actions-rs/cargo@v1 143 | with: 144 | command: clean 145 | 146 | - name: Run cargo build 147 | uses: actions-rs/cargo@v1 148 | env: 149 | CRUNCH_CONFIG_FILENAME: .env.example 150 | CC: musl-gcc 151 | RUSTFLAGS: "-C target-feature=+crt-static" 152 | with: 153 | command: build 154 | args: --release --target=x86_64-unknown-linux-musl 155 | 156 | - name: Generate SHA-256 hash file 157 | run: | 158 | cd target/x86_64-unknown-linux-musl/release 159 | sha256sum crunch > crunch.sha256 160 | 161 | - name: Rename the binary and sha256 file to include the target version 162 | run: | 163 | mv target/x86_64-unknown-linux-musl/release/crunch target/release/crunch.linux-musl 164 | mv target/x86_64-unknown-linux-musl/release/crunch.sha256 target/release/crunch.sha256.linux-musl 165 | 166 | # Upload the binary and sha256 file as artifacts 167 | - uses: actions/upload-artifact@v4 168 | with: 169 | name: crunch.linux-musl 170 | path: target/release 171 | 172 | - uses: actions/upload-artifact@v4 173 | with: 174 | name: crunch.sha256.linux-musl 175 | path: target/release 176 | 177 | create-release: 178 | needs: 179 | - build-ubuntu-latest 180 | - build-ubuntu-2204 181 | - build-linux-musl 182 | runs-on: ubuntu-latest 183 | steps: 184 | - uses: actions/checkout@v4 185 | 186 | - name: Install Rust latest stable 187 | uses: actions-rs/toolchain@v1 188 | with: 189 | profile: minimal 190 | toolchain: stable 191 | override: true 192 | components: rustfmt, clippy 193 | 194 | - name: Get Rustc version 195 | id: get_rustc 196 | run: echo "rustc=$(rustc -V)" >> $GITHUB_OUTPUT 197 | 198 | - name: Get Tag version 199 | id: get_tag 200 | run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT 201 | 202 | - name: Create release 203 | id: create_release 204 | uses: actions/create-release@v1 205 | env: 206 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 207 | with: 208 | tag_name: ${{ steps.get_tag.outputs.tag }} 209 | release_name: Crunch ${{ steps.get_tag.outputs.tag }} 210 | body: "Note: This release was built using `${{ steps.get_rustc.outputs.rustc }}`" 211 | draft: true 212 | prerelease: false 213 | 214 | # Download the artifacts 215 | - uses: actions/download-artifact@v4 216 | with: 217 | name: crunch 218 | path: target/release 219 | 220 | - uses: actions/download-artifact@v4 221 | with: 222 | name: crunch.sha256 223 | path: target/release 224 | 225 | - name: Upload crunch binary on Ubuntu latest 226 | uses: actions/upload-release-asset@v1 227 | env: 228 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 229 | with: 230 | upload_url: ${{ steps.create_release.outputs.upload_url }} 231 | asset_path: target/release/crunch 232 | asset_name: crunch 233 | asset_content_type: application/octet-stream 234 | 235 | - name: Upload crunch sha256 on Ubuntu latest 236 | uses: actions/upload-release-asset@v1.0.1 237 | env: 238 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 239 | with: 240 | upload_url: ${{ steps.create_release.outputs.upload_url }} 241 | asset_path: target/release/crunch.sha256 242 | asset_name: crunch.sha256 243 | asset_content_type: text/plain 244 | 245 | - uses: actions/download-artifact@v4 246 | with: 247 | name: crunch.ubuntu-2204 248 | path: target/release 249 | 250 | - uses: actions/download-artifact@v4 251 | with: 252 | name: crunch.sha256.ubuntu-2204 253 | path: target/release 254 | 255 | - name: Upload crunch binary on Ubuntu 22.04 256 | uses: actions/upload-release-asset@v1 257 | env: 258 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 259 | with: 260 | upload_url: ${{ steps.create_release.outputs.upload_url }} 261 | asset_path: target/release/crunch.ubuntu-2204 262 | asset_name: crunch.ubuntu-2204 263 | asset_content_type: application/octet-stream 264 | 265 | - name: Upload crunch sha256 on Ubuntu 22.04 266 | uses: actions/upload-release-asset@v1.0.1 267 | env: 268 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 269 | with: 270 | upload_url: ${{ steps.create_release.outputs.upload_url }} 271 | asset_path: target/release/crunch.sha256.ubuntu-2204 272 | asset_name: crunch.sha256.ubuntu-2204 273 | asset_content_type: text/plain 274 | 275 | - uses: actions/download-artifact@v4 276 | with: 277 | name: crunch.linux-musl 278 | path: target/release 279 | 280 | - uses: actions/download-artifact@v4 281 | with: 282 | name: crunch.sha256.linux-musl 283 | path: target/release 284 | 285 | - name: Upload crunch binary target linux musl 286 | uses: actions/upload-release-asset@v1 287 | env: 288 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 289 | with: 290 | upload_url: ${{ steps.create_release.outputs.upload_url }} 291 | asset_path: target/release/crunch.linux-musl 292 | asset_name: crunch.linux-musl 293 | asset_content_type: application/octet-stream 294 | 295 | - name: Upload crunch sha256 target linux musl 296 | uses: actions/upload-release-asset@v1.0.1 297 | env: 298 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 299 | with: 300 | upload_url: ${{ steps.create_release.outputs.upload_url }} 301 | asset_path: target/release/crunch.sha256.linux-musl 302 | asset_name: crunch.sha256.linux-musl 303 | asset_content_type: text/plain 304 | -------------------------------------------------------------------------------- /packages/support/src/lib.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Aukbit Ltd. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | use crunch_config::CONFIG; 23 | use serde_json::{Result, Value}; 24 | use std::str::FromStr; 25 | use subxt::utils::H256; 26 | pub type ChainPrefix = u16; 27 | pub type ChainTokenSymbol = String; 28 | 29 | pub const POLKADOT_SPEC: &str = include_str!("../chain-specs/polkadot.json"); 30 | pub const ASSET_HUB_POLKADOT_SPEC: &str = 31 | include_str!("../chain-specs/asset-hub-polkadot.json"); 32 | pub const PEOPLE_POLKADOT_SPEC: &str = 33 | include_str!("../chain-specs/people-polkadot.json"); 34 | 35 | pub const KUSAMA_SPEC: &str = include_str!("../chain-specs/kusama.json"); 36 | pub const ASSET_HUB_KUSAMA_SPEC: &str = 37 | include_str!("../chain-specs/asset-hub-kusama.json"); 38 | pub const PEOPLE_KUSAMA_SPEC: &str = include_str!("../chain-specs/people-kusama.json"); 39 | 40 | pub const PASEO_SPEC: &str = include_str!("../chain-specs/paseo.json"); 41 | pub const ASSET_HUB_PASEO_SPEC: &str = 42 | include_str!("../chain-specs/asset-hub-paseo.json"); 43 | pub const PEOPLE_PASEO_SPEC: &str = include_str!("../chain-specs/people-paseo.json"); 44 | 45 | pub const WESTEND_SPEC: &str = include_str!("../chain-specs/westend.json"); 46 | pub const ASSET_HUB_WESTEND_SPEC: &str = 47 | include_str!("../chain-specs/asset-hub-westend.json"); 48 | pub const PEOPLE_WESTEND_SPEC: &str = include_str!("../chain-specs/people-westend.json"); 49 | 50 | #[derive(Debug, Clone, Copy, PartialEq)] 51 | pub enum SupportedRuntime { 52 | Polkadot, 53 | Kusama, 54 | Westend, 55 | Paseo, 56 | } 57 | 58 | impl SupportedRuntime { 59 | pub fn asset_hub_runtime(&self) -> Option { 60 | match &self { 61 | Self::Polkadot => Some(SupportedParasRuntime::AssetHubPolkadot), 62 | Self::Kusama => Some(SupportedParasRuntime::AssetHubKusama), 63 | Self::Westend => Some(SupportedParasRuntime::AssetHubWestend), 64 | Self::Paseo => Some(SupportedParasRuntime::AssetHubPaseo), 65 | } 66 | } 67 | 68 | pub fn people_runtime(&self) -> Option { 69 | match &self { 70 | Self::Polkadot => Some(SupportedParasRuntime::PeoplePolkadot), 71 | Self::Kusama => Some(SupportedParasRuntime::PeopleKusama), 72 | Self::Westend => Some(SupportedParasRuntime::PeopleWestend), 73 | Self::Paseo => Some(SupportedParasRuntime::PeoplePaseo), 74 | } 75 | } 76 | 77 | pub fn chain_specs(&self) -> &str { 78 | match &self { 79 | Self::Polkadot => POLKADOT_SPEC, 80 | Self::Kusama => KUSAMA_SPEC, 81 | Self::Westend => WESTEND_SPEC, 82 | Self::Paseo => PASEO_SPEC, 83 | // _ => panic!("Unsupported chain"), 84 | } 85 | } 86 | 87 | pub fn chain_state_root_hash(&self) -> H256 { 88 | match &self { 89 | Self::Polkadot => get_state_root_hash(POLKADOT_SPEC), 90 | Self::Kusama => get_state_root_hash(KUSAMA_SPEC), 91 | Self::Westend => get_state_root_hash(WESTEND_SPEC), 92 | Self::Paseo => get_state_root_hash(PASEO_SPEC), 93 | // _ => panic!("Unsupported chain"), 94 | } 95 | } 96 | 97 | // Useful to be used as the subdomain in Subscan hyperlinks 98 | pub fn subdomain(&self, is_staking_on_asset_hub: bool) -> String { 99 | if is_staking_on_asset_hub { 100 | return match &self { 101 | Self::Polkadot => "assethub-polkadot".to_string(), 102 | Self::Kusama => "assethub-kusama".to_string(), 103 | Self::Westend => "assethub-westend".to_string(), 104 | Self::Paseo => "assethub-paseo".to_string(), 105 | }; 106 | } 107 | match &self { 108 | Self::Polkadot => "polkadot".to_string(), 109 | Self::Kusama => "kusama".to_string(), 110 | Self::Westend => "westend".to_string(), 111 | Self::Paseo => "paseo".to_string(), 112 | } 113 | } 114 | } 115 | 116 | impl From for SupportedRuntime { 117 | fn from(v: ChainPrefix) -> Self { 118 | match v { 119 | 0 => Self::Polkadot, 120 | 2 => Self::Kusama, 121 | 42 => Self::Westend, 122 | // TODO: Add Paseo for completeness 123 | _ => unimplemented!("Chain prefix not supported"), 124 | } 125 | } 126 | } 127 | 128 | impl From<&str> for SupportedRuntime { 129 | fn from(s: &str) -> Self { 130 | match s { 131 | "DOT" => Self::Polkadot, 132 | "polkadot" => Self::Polkadot, 133 | "KSM" => Self::Kusama, 134 | "kusama" => Self::Kusama, 135 | "WND" => Self::Westend, 136 | "westend" => Self::Westend, 137 | "PAS" => Self::Paseo, 138 | "paseo" => Self::Paseo, 139 | _ => unimplemented!("Chain not supported"), 140 | } 141 | } 142 | } 143 | 144 | impl From for SupportedRuntime { 145 | fn from(v: String) -> Self { 146 | match v.as_str() { 147 | "DOT" => Self::Polkadot, 148 | "polkadot" => Self::Polkadot, 149 | "KSM" => Self::Kusama, 150 | "kusama" => Self::Kusama, 151 | "WND" => Self::Westend, 152 | "westend" => Self::Westend, 153 | "PAS" => Self::Paseo, 154 | "paseo" => Self::Paseo, 155 | _ => unimplemented!("Chain not supported"), 156 | } 157 | } 158 | } 159 | 160 | impl std::fmt::Display for SupportedRuntime { 161 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 162 | match self { 163 | Self::Polkadot => write!(f, "Polkadot"), 164 | Self::Kusama => write!(f, "Kusama"), 165 | Self::Westend => write!(f, "Westend"), 166 | Self::Paseo => write!(f, "Paseo"), 167 | } 168 | } 169 | } 170 | 171 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 172 | pub enum SupportedParasRuntime { 173 | PeoplePolkadot, 174 | PeopleKusama, 175 | PeopleWestend, 176 | PeoplePaseo, 177 | AssetHubPolkadot, 178 | AssetHubKusama, 179 | AssetHubPaseo, 180 | AssetHubWestend, 181 | } 182 | 183 | impl SupportedParasRuntime { 184 | pub fn rpc_url(&self) -> String { 185 | let config = CONFIG.clone(); 186 | match &self { 187 | Self::PeoplePolkadot 188 | | Self::PeopleKusama 189 | | Self::PeopleWestend 190 | | Self::PeoplePaseo => config.substrate_people_ws_url, 191 | Self::AssetHubPolkadot 192 | | Self::AssetHubKusama 193 | | Self::AssetHubWestend 194 | | Self::AssetHubPaseo => config.substrate_asset_hub_ws_url, 195 | } 196 | } 197 | 198 | pub fn chain_specs(&self) -> &str { 199 | match &self { 200 | Self::PeoplePolkadot => PEOPLE_POLKADOT_SPEC, 201 | Self::PeopleKusama => PEOPLE_KUSAMA_SPEC, 202 | Self::PeopleWestend => PEOPLE_WESTEND_SPEC, 203 | Self::PeoplePaseo => PEOPLE_PASEO_SPEC, 204 | Self::AssetHubPolkadot => ASSET_HUB_POLKADOT_SPEC, 205 | Self::AssetHubKusama => ASSET_HUB_KUSAMA_SPEC, 206 | Self::AssetHubWestend => ASSET_HUB_WESTEND_SPEC, 207 | Self::AssetHubPaseo => ASSET_HUB_PASEO_SPEC, 208 | // _ => panic!("Unsupported chain"), 209 | } 210 | } 211 | 212 | pub fn chain_state_root_hash(&self) -> H256 { 213 | match &self { 214 | Self::PeoplePolkadot => get_state_root_hash(PEOPLE_POLKADOT_SPEC), 215 | Self::PeopleKusama => get_state_root_hash(PEOPLE_KUSAMA_SPEC), 216 | Self::PeopleWestend => get_state_root_hash(PEOPLE_WESTEND_SPEC), 217 | Self::PeoplePaseo => get_state_root_hash(PEOPLE_PASEO_SPEC), 218 | Self::AssetHubPolkadot => get_state_root_hash(ASSET_HUB_POLKADOT_SPEC), 219 | Self::AssetHubKusama => get_state_root_hash(ASSET_HUB_KUSAMA_SPEC), 220 | Self::AssetHubWestend => get_state_root_hash(ASSET_HUB_WESTEND_SPEC), 221 | Self::AssetHubPaseo => get_state_root_hash(ASSET_HUB_PASEO_SPEC), 222 | // _ => panic!("Unsupported chain"), 223 | } 224 | } 225 | } 226 | 227 | impl std::fmt::Display for SupportedParasRuntime { 228 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 229 | match self { 230 | Self::PeoplePolkadot => write!(f, "People Polkadot"), 231 | Self::PeopleKusama => write!(f, "People Kusama"), 232 | Self::PeopleWestend => write!(f, "People Westend"), 233 | Self::PeoplePaseo => write!(f, "People Paseo"), 234 | Self::AssetHubPolkadot => write!(f, "Asset Hub Polkadot"), 235 | Self::AssetHubKusama => write!(f, "Asset Hub Kusama"), 236 | Self::AssetHubWestend => write!(f, "Asset Hub Westend"), 237 | Self::AssetHubPaseo => write!(f, "Asset Hub Paseo"), 238 | } 239 | } 240 | } 241 | 242 | fn get_state_root_hash(chain_specs: &str) -> H256 { 243 | let spec: Result = serde_json::from_str(chain_specs); 244 | match spec { 245 | Ok(json) => { 246 | let state_root = json["genesis"]["stateRootHash"] 247 | .as_str() 248 | .expect("chain spec does not contain state root hash"); 249 | return H256::from_str(state_root).expect("invalid state root hash"); 250 | } 251 | Err(err) => panic!("Failed to parse JSON: {}", err), 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /packages/chains/paseo/src/lib.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Aukbit Ltd. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | use crunch_asset_hub_paseo::{ 23 | ah_metadata::staking::events::EraPaid, ah_metadata::system::events::CodeUpdated, 24 | fetch_active_era_index, fetch_claimed_or_unclaimed_pages_per_era, fetch_controller, 25 | get_era_index_start, get_signer_details, get_stashes, try_run_batch_payouts, 26 | try_run_batch_pool_members, 27 | }; 28 | use crunch_config::CONFIG; 29 | use crunch_core::{get_keypair_from_seed_file, random_wait, try_fetch_onet_data, Crunch}; 30 | use crunch_error::CrunchError; 31 | use crunch_people_paseo::{get_display_name, get_distinct_parent_identites}; 32 | use crunch_relay_chain_paseo::fetch_authorities; 33 | use crunch_report::{ 34 | replace_emoji_lowercase, EraIndex, Network, NominationPoolsSummary, RawData, Report, 35 | Validator, Validators, 36 | }; 37 | use log::{debug, info, warn}; 38 | use std::{convert::TryInto, result::Result, str::FromStr, thread, time}; 39 | use subxt::utils::AccountId32; 40 | use subxt_signer::sr25519::Keypair; 41 | 42 | pub async fn run_and_subscribe_era_paid_events( 43 | crunch: &Crunch, 44 | ) -> Result<(), CrunchError> { 45 | info!("Inspect and `crunch` unclaimed payout rewards"); 46 | // Run once before start subscription 47 | try_crunch(&crunch).await?; 48 | let mut latest_block_number_processed: Option = Some(0); 49 | info!("Subscribe 'EraPaid' on-chain finalized event"); 50 | let api = crunch 51 | .asset_hub_client() 52 | .as_ref() 53 | .expect("AH API to be available"); 54 | let rpc = crunch 55 | .asset_hub_rpc() 56 | .as_ref() 57 | .expect("AH Legacy API to be available"); 58 | 59 | // Keep track of the last known runtime version 60 | let last_spec_version = api.runtime_version().spec_version; 61 | 62 | let mut block_sub = api.blocks().subscribe_finalized().await?; 63 | while let Some(block) = block_sub.next().await { 64 | // Fetch current runtime version before trying to decode anything 65 | let current_spec_version = api.runtime_version().spec_version; 66 | 67 | // If a runtime upgrade occurred, raise known error so all clients could be 68 | // gracefully recreated 69 | if current_spec_version != last_spec_version { 70 | return Err(CrunchError::RuntimeUpgradeDetected( 71 | last_spec_version, 72 | current_spec_version, 73 | )); 74 | } 75 | 76 | // Silently handle RPC disconnection and wait for the next block as soon as 77 | // reconnection is available 78 | let block = match block { 79 | Ok(b) => b, 80 | Err(e) => { 81 | if e.is_disconnected_will_reconnect() { 82 | warn!("The RPC connection was dropped will try to reconnect."); 83 | continue; 84 | } 85 | return Err(e.into()); 86 | } 87 | }; 88 | 89 | // Process blocks that might have been dropped while reconnecting 90 | while let Some(processed_block_number) = latest_block_number_processed { 91 | if block.number() == processed_block_number || processed_block_number == 0 { 92 | latest_block_number_processed = None; 93 | } else { 94 | let block_number = processed_block_number + 1; 95 | 96 | // Skip current block and fetch only blocks that have not yet been processed 97 | if block.number() - block_number > 0 { 98 | if let Some(block_hash) = 99 | rpc.chain_get_block_hash(Some(block_number.into())).await? 100 | { 101 | let events = api.events().at(block_hash).await?; 102 | 103 | // Event --> staking::EraPaid 104 | if let Some(_event) = events.find_first::()? { 105 | let wait: u64 = random_wait(240); 106 | info!("Waiting {} seconds before run batch", wait); 107 | thread::sleep(time::Duration::from_secs(wait)); 108 | try_crunch(&crunch).await?; 109 | } 110 | } 111 | } 112 | 113 | latest_block_number_processed = Some(block_number); 114 | } 115 | } 116 | 117 | let events = block.events().await?; 118 | 119 | // Event --> staking::EraPaid 120 | if let Some(_event) = events.find_first::()? { 121 | let wait: u64 = random_wait(240); 122 | info!("Waiting {} seconds before run batch", wait); 123 | thread::sleep(time::Duration::from_secs(wait)); 124 | try_crunch(&crunch).await?; 125 | } 126 | 127 | // Event --> system::CodeUpdated 128 | if let Some(_event) = events.find_first::()? { 129 | return Err(CrunchError::RuntimeUpgradeDetected( 130 | last_spec_version, 131 | current_spec_version, 132 | )); 133 | } 134 | 135 | latest_block_number_processed = Some(block.number()); 136 | } 137 | // If subscription has closed for some reason await and subscribe again 138 | Err(CrunchError::SubscriptionFinished) 139 | } 140 | 141 | async fn collect_validators_data( 142 | crunch: &Crunch, 143 | era_index: EraIndex, 144 | ) -> Result { 145 | // Get unclaimed eras for the stash addresses 146 | let active_validators = fetch_authorities(&crunch).await?; 147 | debug!("active_validators {:?}", active_validators); 148 | 149 | let mut validators: Validators = Vec::new(); 150 | 151 | let stashes = get_stashes(&crunch).await?; 152 | 153 | for (_i, stash_str) in stashes.iter().enumerate() { 154 | let stash = AccountId32::from_str(stash_str).map_err(|e| { 155 | CrunchError::Other(format!("Invalid account: {stash_str} error: {e:?}")) 156 | })?; 157 | 158 | let controller = fetch_controller(&crunch, &stash, &mut validators).await?; 159 | 160 | if controller.is_none() { 161 | continue; 162 | } 163 | 164 | // Instantiates a new validator struct 165 | let mut v = Validator::new(stash.clone()); 166 | 167 | // Set controller 168 | v.controller = controller.clone(); 169 | 170 | // Get validator name 171 | (v.name, v.parent_identity, v.has_identity) = 172 | get_display_name(&crunch, &stash, None).await?; 173 | 174 | // Check if validator is in active set 175 | v.is_active = active_validators.contains(&stash); 176 | 177 | // Look for unclaimed eras, starting on current_era - maximum_eras 178 | let start_index = get_era_index_start(&crunch, era_index).await?; 179 | 180 | // Find unclaimed eras in previous 84 eras (reverse order) 181 | for era in (start_index..era_index).rev() { 182 | fetch_claimed_or_unclaimed_pages_per_era(&crunch, &stash, era, &mut v) 183 | .await?; 184 | } 185 | validators.push(v); 186 | } 187 | 188 | // Sort validators by identity, than by non-identity and push the stashes 189 | // with warnings to bottom 190 | let mut validators_with_warnings = validators 191 | .clone() 192 | .into_iter() 193 | .filter(|v| v.warnings.len() > 0) 194 | .collect::>(); 195 | 196 | validators_with_warnings.sort_by(|a, b| { 197 | replace_emoji_lowercase(&a.name) 198 | .partial_cmp(&replace_emoji_lowercase(&b.name)) 199 | .unwrap() 200 | }); 201 | 202 | let validators_with_no_identity = validators 203 | .clone() 204 | .into_iter() 205 | .filter(|v| v.warnings.len() == 0 && !v.has_identity) 206 | .collect::>(); 207 | 208 | let mut validators = validators 209 | .into_iter() 210 | .filter(|v| v.warnings.len() == 0 && v.has_identity) 211 | .collect::>(); 212 | 213 | validators.sort_by(|a, b| { 214 | replace_emoji_lowercase(&a.name) 215 | .partial_cmp(&replace_emoji_lowercase(&b.name)) 216 | .unwrap() 217 | }); 218 | validators.extend(validators_with_no_identity); 219 | validators.extend(validators_with_warnings); 220 | 221 | debug!("validators {:?}", validators); 222 | Ok(validators) 223 | } 224 | 225 | pub async fn try_crunch(crunch: &Crunch) -> Result<(), CrunchError> { 226 | let config = CONFIG.clone(); 227 | 228 | let signer_keypair: Keypair = get_keypair_from_seed_file()?; 229 | let seed_account_id: AccountId32 = signer_keypair.public_key().into(); 230 | 231 | let signer_details = get_signer_details(&crunch, &seed_account_id).await?; 232 | 233 | // Get Network name 234 | let chain_name = crunch.rpc().system_chain().await?; 235 | 236 | // Get Era index 237 | let active_era_index = fetch_active_era_index(&crunch).await?; 238 | 239 | let properties = crunch.rpc().system_properties().await?; 240 | 241 | // Get Token symbol 242 | let token_symbol: String = if let Some(token_symbol) = properties.get("tokenSymbol") { 243 | token_symbol.as_str().unwrap_or_default().to_string() 244 | } else { 245 | "ND".to_string() 246 | }; 247 | 248 | // Get Token decimals 249 | let token_decimals: u8 = if let Some(token_decimals) = properties.get("tokenDecimals") 250 | { 251 | token_decimals 252 | .as_u64() 253 | .unwrap_or_default() 254 | .try_into() 255 | .unwrap() 256 | } else { 257 | 12 258 | }; 259 | 260 | let subdomain = crunch.subdomain(true); 261 | 262 | // Set network info 263 | let network = Network { 264 | name: chain_name.clone(), 265 | subdomain, 266 | active_era: active_era_index, 267 | token_symbol, 268 | token_decimals, 269 | }; 270 | debug!("network {:?}", network); 271 | 272 | // Check if group by identity is enabled by user to change the behaviour of how stashes are processed 273 | if config.group_identity_enabled { 274 | // Try run payouts in batches 275 | let mut all_validators = 276 | collect_validators_data(&crunch, active_era_index).await?; 277 | 278 | let parent_identities: Vec = 279 | get_distinct_parent_identites(all_validators.clone()); 280 | 281 | for parent in parent_identities { 282 | // Filter validators by parent identity 283 | let mut validators = all_validators 284 | .clone() 285 | .into_iter() 286 | .filter(|v| replace_emoji_lowercase(&v.parent_identity) == parent) 287 | .collect::(); 288 | 289 | // Remove all processed validators from original vec so it don't get looked up again 290 | all_validators 291 | .retain(|v| replace_emoji_lowercase(&v.parent_identity) != parent); 292 | 293 | if validators.len() > 0 { 294 | // Try run payouts in batches 295 | let payout_summary = 296 | try_run_batch_payouts(&crunch, &signer_keypair, &mut validators) 297 | .await?; 298 | 299 | // Try fetch ONE-T grade data 300 | for v in &mut validators { 301 | v.onet = 302 | try_fetch_onet_data(chain_name.to_lowercase(), v.stash.clone()) 303 | .await?; 304 | } 305 | 306 | // NOTE: In the last iteration try to batch pools if any and include them in the report 307 | // TODO: Eventually we could do a separate message containing only the pools report 308 | let pools_summary: Option = 309 | if all_validators.len() == 0 { 310 | // Try run pool members in batches 311 | Some(try_run_batch_pool_members(&crunch, &signer_keypair).await?) 312 | } else { 313 | None 314 | }; 315 | 316 | let data = RawData { 317 | network: network.clone(), 318 | signer_details: signer_details.clone(), 319 | validators, 320 | payout_summary, 321 | pools_summary, 322 | }; 323 | 324 | let report = Report::from(data); 325 | crunch 326 | .send_message(&report.message(), &report.formatted_message()) 327 | .await?; 328 | } 329 | // NOTE: To prevent too many request from matrix API set a sleep here of 5 seconds before trying another identity payout 330 | thread::sleep(time::Duration::from_secs(5)); 331 | } 332 | } else { 333 | let mut validators = collect_validators_data(&crunch, active_era_index).await?; 334 | 335 | // Try run payouts in batches 336 | let payout_summary = 337 | try_run_batch_payouts(&crunch, &signer_keypair, &mut validators).await?; 338 | 339 | // Try fetch ONE-T grade data 340 | for v in &mut validators { 341 | v.onet = 342 | try_fetch_onet_data(chain_name.to_lowercase(), v.stash.clone()).await?; 343 | } 344 | 345 | // Try run members in batches 346 | let pools_summary = try_run_batch_pool_members(&crunch, &signer_keypair).await?; 347 | 348 | let data = RawData { 349 | network, 350 | signer_details, 351 | validators, 352 | payout_summary, 353 | pools_summary: Some(pools_summary), 354 | }; 355 | 356 | let report = Report::from(data); 357 | crunch 358 | .send_message(&report.message(), &report.formatted_message()) 359 | .await?; 360 | } 361 | 362 | Ok(()) 363 | } 364 | 365 | pub async fn inspect(crunch: &Crunch) -> Result<(), CrunchError> { 366 | crunch_asset_hub_paseo::inspect(crunch).await 367 | } 368 | -------------------------------------------------------------------------------- /packages/chains/kusama/src/lib.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Aukbit Ltd. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | use crunch_asset_hub_kusama::{ 23 | ah_metadata::staking::events::EraPaid, ah_metadata::system::events::CodeUpdated, 24 | fetch_active_era_index, fetch_claimed_or_unclaimed_pages_per_era, fetch_controller, 25 | get_era_index_start, get_signer_details, get_stashes, try_run_batch_payouts, 26 | try_run_batch_pool_members, 27 | }; 28 | use crunch_config::CONFIG; 29 | use crunch_core::{get_keypair_from_seed_file, random_wait, try_fetch_onet_data, Crunch}; 30 | use crunch_error::CrunchError; 31 | use crunch_people_kusama::{get_display_name, get_distinct_parent_identites}; 32 | use crunch_relay_chain_kusama::fetch_authorities; 33 | use crunch_report::{ 34 | replace_emoji_lowercase, EraIndex, Network, NominationPoolsSummary, RawData, Report, 35 | Validator, Validators, 36 | }; 37 | use log::{debug, info, warn}; 38 | use std::{convert::TryInto, result::Result, str::FromStr, thread, time}; 39 | use subxt::utils::AccountId32; 40 | use subxt_signer::sr25519::Keypair; 41 | 42 | pub async fn run_and_subscribe_era_paid_events( 43 | crunch: &Crunch, 44 | ) -> Result<(), CrunchError> { 45 | info!("Inspect and `crunch` unclaimed payout rewards"); 46 | // Run once before start subscription 47 | try_crunch(&crunch).await?; 48 | let mut latest_block_number_processed: Option = Some(0); 49 | info!("Subscribe 'EraPaid' on-chain finalized event"); 50 | let api = crunch 51 | .asset_hub_client() 52 | .as_ref() 53 | .expect("AH API to be available"); 54 | let rpc = crunch 55 | .asset_hub_rpc() 56 | .as_ref() 57 | .expect("AH Legacy API to be available"); 58 | 59 | // Keep track of the last known runtime version 60 | let last_spec_version = api.runtime_version().spec_version; 61 | 62 | let mut block_sub = api.blocks().subscribe_finalized().await?; 63 | while let Some(block) = block_sub.next().await { 64 | // Fetch current runtime version before trying to decode anything 65 | let current_spec_version = api.runtime_version().spec_version; 66 | 67 | // If a runtime upgrade occurred, raise known error so all clients could be 68 | // gracefully recreated 69 | if current_spec_version != last_spec_version { 70 | return Err(CrunchError::RuntimeUpgradeDetected( 71 | last_spec_version, 72 | current_spec_version, 73 | )); 74 | } 75 | 76 | // Silently handle RPC disconnection and wait for the next block as soon as 77 | // reconnection is available 78 | let block = match block { 79 | Ok(b) => b, 80 | Err(e) => { 81 | if e.is_disconnected_will_reconnect() { 82 | warn!("The RPC connection was dropped will try to reconnect."); 83 | continue; 84 | } 85 | return Err(e.into()); 86 | } 87 | }; 88 | 89 | // Process blocks that might have been dropped while reconnecting 90 | while let Some(processed_block_number) = latest_block_number_processed { 91 | if block.number() == processed_block_number || processed_block_number == 0 { 92 | latest_block_number_processed = None; 93 | } else { 94 | let block_number = processed_block_number + 1; 95 | 96 | // Skip current block and fetch only blocks that have not yet been processed 97 | if block.number() - block_number > 0 { 98 | if let Some(block_hash) = 99 | rpc.chain_get_block_hash(Some(block_number.into())).await? 100 | { 101 | let events = api.events().at(block_hash).await?; 102 | 103 | // Event --> staking::EraPaid 104 | if let Some(_event) = events.find_first::()? { 105 | let wait: u64 = random_wait(240); 106 | info!("Waiting {} seconds before run batch", wait); 107 | thread::sleep(time::Duration::from_secs(wait)); 108 | try_crunch(&crunch).await?; 109 | } 110 | } 111 | } 112 | 113 | latest_block_number_processed = Some(block_number); 114 | } 115 | } 116 | 117 | let events = block.events().await?; 118 | 119 | // Event --> staking::EraPaid 120 | if let Some(_event) = events.find_first::()? { 121 | let wait: u64 = random_wait(240); 122 | info!("Waiting {} seconds before run batch", wait); 123 | thread::sleep(time::Duration::from_secs(wait)); 124 | try_crunch(&crunch).await?; 125 | } 126 | 127 | // Event --> system::CodeUpdated 128 | if let Some(_event) = events.find_first::()? { 129 | return Err(CrunchError::RuntimeUpgradeDetected( 130 | last_spec_version, 131 | current_spec_version, 132 | )); 133 | } 134 | 135 | latest_block_number_processed = Some(block.number()); 136 | } 137 | // If subscription has closed for some reason await and subscribe again 138 | Err(CrunchError::SubscriptionFinished) 139 | } 140 | 141 | async fn collect_validators_data( 142 | crunch: &Crunch, 143 | era_index: EraIndex, 144 | ) -> Result { 145 | // Get unclaimed eras for the stash addresses 146 | let active_validators = fetch_authorities(&crunch).await?; 147 | debug!("active_validators {:?}", active_validators); 148 | 149 | let mut validators: Validators = Vec::new(); 150 | 151 | let stashes = get_stashes(&crunch).await?; 152 | 153 | for (_i, stash_str) in stashes.iter().enumerate() { 154 | let stash = AccountId32::from_str(stash_str).map_err(|e| { 155 | CrunchError::Other(format!("Invalid account: {stash_str} error: {e:?}")) 156 | })?; 157 | 158 | let controller = fetch_controller(&crunch, &stash, &mut validators).await?; 159 | 160 | if controller.is_none() { 161 | continue; 162 | } 163 | 164 | // Instantiates a new validator struct 165 | let mut v = Validator::new(stash.clone()); 166 | 167 | // Set controller 168 | v.controller = controller.clone(); 169 | 170 | // Get validator name 171 | (v.name, v.parent_identity, v.has_identity) = 172 | get_display_name(&crunch, &stash, None).await?; 173 | 174 | // Check if validator is in active set 175 | v.is_active = active_validators.contains(&stash); 176 | 177 | // Look for unclaimed eras, starting on current_era - maximum_eras 178 | let start_index = get_era_index_start(&crunch, era_index).await?; 179 | 180 | // Find unclaimed eras in previous 84 eras (reverse order) 181 | for era in (start_index..era_index).rev() { 182 | fetch_claimed_or_unclaimed_pages_per_era(&crunch, &stash, era, &mut v) 183 | .await?; 184 | } 185 | validators.push(v); 186 | } 187 | 188 | // Sort validators by identity, than by non-identity and push the stashes 189 | // with warnings to bottom 190 | let mut validators_with_warnings = validators 191 | .clone() 192 | .into_iter() 193 | .filter(|v| v.warnings.len() > 0) 194 | .collect::>(); 195 | 196 | validators_with_warnings.sort_by(|a, b| { 197 | replace_emoji_lowercase(&a.name) 198 | .partial_cmp(&replace_emoji_lowercase(&b.name)) 199 | .unwrap() 200 | }); 201 | 202 | let validators_with_no_identity = validators 203 | .clone() 204 | .into_iter() 205 | .filter(|v| v.warnings.len() == 0 && !v.has_identity) 206 | .collect::>(); 207 | 208 | let mut validators = validators 209 | .into_iter() 210 | .filter(|v| v.warnings.len() == 0 && v.has_identity) 211 | .collect::>(); 212 | 213 | validators.sort_by(|a, b| { 214 | replace_emoji_lowercase(&a.name) 215 | .partial_cmp(&replace_emoji_lowercase(&b.name)) 216 | .unwrap() 217 | }); 218 | validators.extend(validators_with_no_identity); 219 | validators.extend(validators_with_warnings); 220 | 221 | debug!("validators {:?}", validators); 222 | Ok(validators) 223 | } 224 | 225 | pub async fn try_crunch(crunch: &Crunch) -> Result<(), CrunchError> { 226 | let config = CONFIG.clone(); 227 | 228 | let signer_keypair: Keypair = get_keypair_from_seed_file()?; 229 | let seed_account_id: AccountId32 = signer_keypair.public_key().into(); 230 | 231 | let signer_details = get_signer_details(&crunch, &seed_account_id).await?; 232 | 233 | // Get Network name 234 | let chain_name = crunch.rpc().system_chain().await?; 235 | 236 | // Get Era index 237 | let active_era_index = fetch_active_era_index(&crunch).await?; 238 | 239 | let properties = crunch.rpc().system_properties().await?; 240 | 241 | // Get Token symbol 242 | let token_symbol: String = if let Some(token_symbol) = properties.get("tokenSymbol") { 243 | token_symbol.as_str().unwrap_or_default().to_string() 244 | } else { 245 | "ND".to_string() 246 | }; 247 | 248 | // Get Token decimals 249 | let token_decimals: u8 = if let Some(token_decimals) = properties.get("tokenDecimals") 250 | { 251 | token_decimals 252 | .as_u64() 253 | .unwrap_or_default() 254 | .try_into() 255 | .unwrap() 256 | } else { 257 | 12 258 | }; 259 | 260 | let subdomain = crunch.subdomain(true); 261 | 262 | // Set network info 263 | let network = Network { 264 | name: chain_name.clone(), 265 | subdomain, 266 | active_era: active_era_index, 267 | token_symbol, 268 | token_decimals, 269 | }; 270 | debug!("network {:?}", network); 271 | 272 | // Check if group by identity is enabled by user to change the behaviour of how stashes are processed 273 | if config.group_identity_enabled { 274 | // Try run payouts in batches 275 | let mut all_validators = 276 | collect_validators_data(&crunch, active_era_index).await?; 277 | 278 | let parent_identities: Vec = 279 | get_distinct_parent_identites(all_validators.clone()); 280 | 281 | for parent in parent_identities { 282 | // Filter validators by parent identity 283 | let mut validators = all_validators 284 | .clone() 285 | .into_iter() 286 | .filter(|v| replace_emoji_lowercase(&v.parent_identity) == parent) 287 | .collect::(); 288 | 289 | // Remove all processed validators from original vec so it don't get looked up again 290 | all_validators 291 | .retain(|v| replace_emoji_lowercase(&v.parent_identity) != parent); 292 | 293 | if validators.len() > 0 { 294 | // Try run payouts in batches 295 | let payout_summary = 296 | try_run_batch_payouts(&crunch, &signer_keypair, &mut validators) 297 | .await?; 298 | 299 | // Try fetch ONE-T grade data 300 | for v in &mut validators { 301 | v.onet = 302 | try_fetch_onet_data(chain_name.to_lowercase(), v.stash.clone()) 303 | .await?; 304 | } 305 | 306 | // NOTE: In the last iteration try to batch pools if any and include them in the report 307 | // TODO: Eventually we could do a separate message containing only the pools report 308 | let pools_summary: Option = 309 | if all_validators.len() == 0 { 310 | // Try run pool members in batches 311 | Some(try_run_batch_pool_members(&crunch, &signer_keypair).await?) 312 | } else { 313 | None 314 | }; 315 | 316 | let data = RawData { 317 | network: network.clone(), 318 | signer_details: signer_details.clone(), 319 | validators, 320 | payout_summary, 321 | pools_summary, 322 | }; 323 | 324 | let report = Report::from(data); 325 | crunch 326 | .send_message(&report.message(), &report.formatted_message()) 327 | .await?; 328 | } 329 | // NOTE: To prevent too many request from matrix API set a sleep here of 5 seconds before trying another identity payout 330 | thread::sleep(time::Duration::from_secs(5)); 331 | } 332 | } else { 333 | let mut validators = collect_validators_data(&crunch, active_era_index).await?; 334 | 335 | // Try run payouts in batches 336 | let payout_summary = 337 | try_run_batch_payouts(&crunch, &signer_keypair, &mut validators).await?; 338 | 339 | // Try fetch ONE-T grade data 340 | for v in &mut validators { 341 | v.onet = 342 | try_fetch_onet_data(chain_name.to_lowercase(), v.stash.clone()).await?; 343 | } 344 | 345 | // Try run members in batches 346 | let pools_summary = try_run_batch_pool_members(&crunch, &signer_keypair).await?; 347 | 348 | let data = RawData { 349 | network, 350 | signer_details, 351 | validators, 352 | payout_summary, 353 | pools_summary: Some(pools_summary), 354 | }; 355 | 356 | let report = Report::from(data); 357 | crunch 358 | .send_message(&report.message(), &report.formatted_message()) 359 | .await?; 360 | } 361 | 362 | Ok(()) 363 | } 364 | 365 | pub async fn inspect(crunch: &Crunch) -> Result<(), CrunchError> { 366 | crunch_asset_hub_kusama::inspect(crunch).await 367 | } 368 | -------------------------------------------------------------------------------- /packages/chains/westend/src/lib.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // Copyright © 2021 Aukbit Ltd. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | use crunch_asset_hub_westend::{ 23 | ah_metadata::staking::events::EraPaid, ah_metadata::system::events::CodeUpdated, 24 | fetch_active_era_index, fetch_claimed_or_unclaimed_pages_per_era, fetch_controller, 25 | get_era_index_start, get_signer_details, get_stashes, try_run_batch_payouts, 26 | try_run_batch_pool_members, 27 | }; 28 | use crunch_config::CONFIG; 29 | use crunch_core::{get_keypair_from_seed_file, random_wait, try_fetch_onet_data, Crunch}; 30 | use crunch_error::CrunchError; 31 | use crunch_people_westend::{get_display_name, get_distinct_parent_identites}; 32 | use crunch_relay_chain_westend::fetch_authorities; 33 | use crunch_report::{ 34 | replace_emoji_lowercase, EraIndex, Network, NominationPoolsSummary, RawData, Report, 35 | Validator, Validators, 36 | }; 37 | use log::{debug, info, warn}; 38 | use std::{convert::TryInto, result::Result, str::FromStr, thread, time}; 39 | use subxt::utils::AccountId32; 40 | use subxt_signer::sr25519::Keypair; 41 | 42 | pub async fn run_and_subscribe_era_paid_events( 43 | crunch: &Crunch, 44 | ) -> Result<(), CrunchError> { 45 | info!("Inspect and `crunch` unclaimed payout rewards"); 46 | // Run once before start subscription 47 | try_crunch(&crunch).await?; 48 | let mut latest_block_number_processed: Option = Some(0); 49 | info!("Subscribe 'EraPaid' on-chain finalized event"); 50 | let api = crunch 51 | .asset_hub_client() 52 | .as_ref() 53 | .expect("AH API to be available"); 54 | let rpc = crunch 55 | .asset_hub_rpc() 56 | .as_ref() 57 | .expect("AH Legacy API to be available"); 58 | 59 | // Keep track of the last known runtime version 60 | let last_spec_version = api.runtime_version().spec_version; 61 | 62 | let mut block_sub = api.blocks().subscribe_finalized().await?; 63 | while let Some(block) = block_sub.next().await { 64 | // Fetch current runtime version before trying to decode anything 65 | let current_spec_version = api.runtime_version().spec_version; 66 | 67 | // If a runtime upgrade occurred, raise known error so all clients could be 68 | // gracefully recreated 69 | if current_spec_version != last_spec_version { 70 | return Err(CrunchError::RuntimeUpgradeDetected( 71 | last_spec_version, 72 | current_spec_version, 73 | )); 74 | } 75 | 76 | // Silently handle RPC disconnection and wait for the next block as soon as 77 | // reconnection is available 78 | let block = match block { 79 | Ok(b) => b, 80 | Err(e) => { 81 | if e.is_disconnected_will_reconnect() { 82 | warn!("The RPC connection was dropped will try to reconnect."); 83 | continue; 84 | } 85 | return Err(e.into()); 86 | } 87 | }; 88 | 89 | // Process blocks that might have been dropped while reconnecting 90 | while let Some(processed_block_number) = latest_block_number_processed { 91 | if block.number() == processed_block_number || processed_block_number == 0 { 92 | latest_block_number_processed = None; 93 | } else { 94 | let block_number = processed_block_number + 1; 95 | 96 | // Skip current block and fetch only blocks that have not yet been processed 97 | if block.number() - block_number > 0 { 98 | if let Some(block_hash) = 99 | rpc.chain_get_block_hash(Some(block_number.into())).await? 100 | { 101 | let events = api.events().at(block_hash).await?; 102 | 103 | // Event --> staking::EraPaid 104 | if let Some(_event) = events.find_first::()? { 105 | let wait: u64 = random_wait(240); 106 | info!("Waiting {} seconds before run batch", wait); 107 | thread::sleep(time::Duration::from_secs(wait)); 108 | try_crunch(&crunch).await?; 109 | } 110 | } 111 | } 112 | 113 | latest_block_number_processed = Some(block_number); 114 | } 115 | } 116 | 117 | let events = block.events().await?; 118 | 119 | // Event --> staking::EraPaid 120 | if let Some(_event) = events.find_first::()? { 121 | let wait: u64 = random_wait(240); 122 | info!("Waiting {} seconds before run batch", wait); 123 | thread::sleep(time::Duration::from_secs(wait)); 124 | try_crunch(&crunch).await?; 125 | } 126 | 127 | // Event --> system::CodeUpdated 128 | if let Some(_event) = events.find_first::()? { 129 | return Err(CrunchError::RuntimeUpgradeDetected( 130 | last_spec_version, 131 | current_spec_version, 132 | )); 133 | } 134 | 135 | latest_block_number_processed = Some(block.number()); 136 | } 137 | // If subscription has closed for some reason await and subscribe again 138 | Err(CrunchError::SubscriptionFinished) 139 | } 140 | 141 | async fn collect_validators_data( 142 | crunch: &Crunch, 143 | era_index: EraIndex, 144 | ) -> Result { 145 | // Get unclaimed eras for the stash addresses 146 | let active_validators = fetch_authorities(&crunch).await?; 147 | debug!("active_validators {:?}", active_validators); 148 | 149 | let mut validators: Validators = Vec::new(); 150 | 151 | let stashes = get_stashes(&crunch).await?; 152 | 153 | for (_i, stash_str) in stashes.iter().enumerate() { 154 | let stash = AccountId32::from_str(stash_str).map_err(|e| { 155 | CrunchError::Other(format!("Invalid account: {stash_str} error: {e:?}")) 156 | })?; 157 | 158 | let controller = fetch_controller(&crunch, &stash, &mut validators).await?; 159 | 160 | if controller.is_none() { 161 | continue; 162 | } 163 | 164 | // Instantiates a new validator struct 165 | let mut v = Validator::new(stash.clone()); 166 | 167 | // Set controller 168 | v.controller = controller.clone(); 169 | 170 | // Get validator name 171 | (v.name, v.parent_identity, v.has_identity) = 172 | get_display_name(&crunch, &stash, None).await?; 173 | 174 | // Check if validator is in active set 175 | v.is_active = active_validators.contains(&stash); 176 | 177 | // Look for unclaimed eras, starting on current_era - maximum_eras 178 | let start_index = get_era_index_start(&crunch, era_index).await?; 179 | 180 | // Find unclaimed eras in previous 84 eras (reverse order) 181 | for era in (start_index..era_index).rev() { 182 | fetch_claimed_or_unclaimed_pages_per_era(&crunch, &stash, era, &mut v) 183 | .await?; 184 | } 185 | validators.push(v); 186 | } 187 | 188 | // Sort validators by identity, than by non-identity and push the stashes 189 | // with warnings to bottom 190 | let mut validators_with_warnings = validators 191 | .clone() 192 | .into_iter() 193 | .filter(|v| v.warnings.len() > 0) 194 | .collect::>(); 195 | 196 | validators_with_warnings.sort_by(|a, b| { 197 | replace_emoji_lowercase(&a.name) 198 | .partial_cmp(&replace_emoji_lowercase(&b.name)) 199 | .unwrap() 200 | }); 201 | 202 | let validators_with_no_identity = validators 203 | .clone() 204 | .into_iter() 205 | .filter(|v| v.warnings.len() == 0 && !v.has_identity) 206 | .collect::>(); 207 | 208 | let mut validators = validators 209 | .into_iter() 210 | .filter(|v| v.warnings.len() == 0 && v.has_identity) 211 | .collect::>(); 212 | 213 | validators.sort_by(|a, b| { 214 | replace_emoji_lowercase(&a.name) 215 | .partial_cmp(&replace_emoji_lowercase(&b.name)) 216 | .unwrap() 217 | }); 218 | validators.extend(validators_with_no_identity); 219 | validators.extend(validators_with_warnings); 220 | 221 | debug!("validators {:?}", validators); 222 | Ok(validators) 223 | } 224 | 225 | pub async fn try_crunch(crunch: &Crunch) -> Result<(), CrunchError> { 226 | let config = CONFIG.clone(); 227 | 228 | let signer_keypair: Keypair = get_keypair_from_seed_file()?; 229 | let seed_account_id: AccountId32 = signer_keypair.public_key().into(); 230 | 231 | let signer_details = get_signer_details(&crunch, &seed_account_id).await?; 232 | 233 | // Get Network name 234 | let chain_name = crunch.rpc().system_chain().await?; 235 | 236 | // Get Era index 237 | let active_era_index = fetch_active_era_index(&crunch).await?; 238 | 239 | let properties = crunch.rpc().system_properties().await?; 240 | 241 | // Get Token symbol 242 | let token_symbol: String = if let Some(token_symbol) = properties.get("tokenSymbol") { 243 | token_symbol.as_str().unwrap_or_default().to_string() 244 | } else { 245 | "ND".to_string() 246 | }; 247 | 248 | // Get Token decimals 249 | let token_decimals: u8 = if let Some(token_decimals) = properties.get("tokenDecimals") 250 | { 251 | token_decimals 252 | .as_u64() 253 | .unwrap_or_default() 254 | .try_into() 255 | .unwrap() 256 | } else { 257 | 12 258 | }; 259 | 260 | let subdomain = crunch.subdomain(true); 261 | 262 | // Set network info 263 | let network = Network { 264 | name: chain_name.clone(), 265 | subdomain, 266 | active_era: active_era_index, 267 | token_symbol, 268 | token_decimals, 269 | }; 270 | debug!("network {:?}", network); 271 | 272 | // Check if group by identity is enabled by user to change the behaviour of how stashes are processed 273 | if config.group_identity_enabled { 274 | // Try run payouts in batches 275 | let mut all_validators = 276 | collect_validators_data(&crunch, active_era_index).await?; 277 | 278 | let parent_identities: Vec = 279 | get_distinct_parent_identites(all_validators.clone()); 280 | 281 | for parent in parent_identities { 282 | // Filter validators by parent identity 283 | let mut validators = all_validators 284 | .clone() 285 | .into_iter() 286 | .filter(|v| replace_emoji_lowercase(&v.parent_identity) == parent) 287 | .collect::(); 288 | 289 | // Remove all processed validators from original vec so it don't get looked up again 290 | all_validators 291 | .retain(|v| replace_emoji_lowercase(&v.parent_identity) != parent); 292 | 293 | if validators.len() > 0 { 294 | // Try run payouts in batches 295 | let payout_summary = 296 | try_run_batch_payouts(&crunch, &signer_keypair, &mut validators) 297 | .await?; 298 | 299 | // Try fetch ONE-T grade data 300 | for v in &mut validators { 301 | v.onet = 302 | try_fetch_onet_data(chain_name.to_lowercase(), v.stash.clone()) 303 | .await?; 304 | } 305 | 306 | // NOTE: In the last iteration try to batch pools if any and include them in the report 307 | // TODO: Eventually we could do a separate message containing only the pools report 308 | let pools_summary: Option = 309 | if all_validators.len() == 0 { 310 | // Try run pool members in batches 311 | Some(try_run_batch_pool_members(&crunch, &signer_keypair).await?) 312 | } else { 313 | None 314 | }; 315 | 316 | let data = RawData { 317 | network: network.clone(), 318 | signer_details: signer_details.clone(), 319 | validators, 320 | payout_summary, 321 | pools_summary, 322 | }; 323 | 324 | let report = Report::from(data); 325 | crunch 326 | .send_message(&report.message(), &report.formatted_message()) 327 | .await?; 328 | } 329 | // NOTE: To prevent too many request from matrix API set a sleep here of 5 seconds before trying another identity payout 330 | thread::sleep(time::Duration::from_secs(5)); 331 | } 332 | } else { 333 | let mut validators = collect_validators_data(&crunch, active_era_index).await?; 334 | 335 | // Try run payouts in batches 336 | let payout_summary = 337 | try_run_batch_payouts(&crunch, &signer_keypair, &mut validators).await?; 338 | 339 | // Try fetch ONE-T grade data 340 | for v in &mut validators { 341 | v.onet = 342 | try_fetch_onet_data(chain_name.to_lowercase(), v.stash.clone()).await?; 343 | } 344 | 345 | // Try run members in batches 346 | let pools_summary = try_run_batch_pool_members(&crunch, &signer_keypair).await?; 347 | 348 | let data = RawData { 349 | network, 350 | signer_details, 351 | validators, 352 | payout_summary, 353 | pools_summary: Some(pools_summary), 354 | }; 355 | 356 | let report = Report::from(data); 357 | crunch 358 | .send_message(&report.message(), &report.formatted_message()) 359 | .await?; 360 | } 361 | 362 | Ok(()) 363 | } 364 | 365 | pub async fn inspect(crunch: &Crunch) -> Result<(), CrunchError> { 366 | crunch_asset_hub_westend::inspect(crunch).await 367 | } 368 | --------------------------------------------------------------------------------