├── .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 |
--------------------------------------------------------------------------------