# StakePool

StakePool is a public blockchain infrastructure operator based in Brazil, dedicated to providing non-custodial delegation services. Our mission is to offer reliable solutions with high availability, ensuring the security and efficiency of operations.


# Avail

Avail is a Web3 infrastructure layer that allows modular execution layers to scale and interoperate in a trust minimized way.


# RPC

<table><thead><tr><th width="164">Name</th><th width="401">Chain</th></tr></thead><tbody><tr><td><strong>Avail</strong></td><td>mainnet</td></tr><tr><td>Wss</td><td><a href="wss://rpc.avail.stakepool.dev.br/ws"><mark style="color:green;">wss://rpc.avail.stakepool.dev.br/ws</mark></a></td></tr><tr><td>RPC</td><td><a href="https://rpc.avail.stakepool.dev.br/rpc"><mark style="color:green;">https://rpc.avail.stakepool.dev.br/rpc</mark></a></td></tr></tbody></table>

\
\\


# Faucet

[<mark style="color:green;">https://faucet.stakepool.dev.br/avail</mark>](https://faucet.stakepool.dev.br/avail)

\
\\


# Live Peers

<table><thead><tr><th width="164">Name</th><th width="468">Chain</th></tr></thead><tbody><tr><td><strong>Avail</strong></td><td>Mainnet</td></tr><tr><td>Peer</td><td>12D3KooWPiNHg5bHGgjuNpaL1VddoHFXKH5EGhfS2dhdcKYzAPyP@65.108.76.33:30333</td></tr></tbody></table>


# Snapshots


# Avail

*Snapshots are taken automatically every 1 week.*

<table><thead><tr><th width="102">Name</th><th width="129">Chain</th><th width="105">Age</th><th width="481">Snapshot</th></tr></thead><tbody><tr><td><strong>Avail</strong></td><td>mainnet</td><td>26-08-09</td><td><a href="https://snap.stakepool.work/snapshots-stakepool/avail.tar.lz4"><mark style="color:green;">https://snap.stakepool.work/snapshots-stakepool/avail.tar.lz4</mark></a></td></tr></tbody></table>

**Avail Mainnet**

**1** - Stop the service

```bash
sudo systemctl stop avail
```

Install lz4 if needed

```bash
sudo apt install lz4
```

**2** - Download the file and extract

```bash
curl -L https://snap.stakepool.work/snapshots-stakepool/avail.tar.lz4 | lz4 -d | tar -xf -
```

\\


# Katana

Deeper liquidity and higher yield. core defi apps and assets concentrate liquidity to give you the best user experience


# RPC

<table><thead><tr><th width="164">Name</th><th width="401">Chain</th></tr></thead><tbody><tr><td><strong>Katana</strong></td><td>mainnet</td></tr><tr><td>Wss</td><td><a href="wss://rpc.avail.stakepool.dev.br/ws"><mark style="color:green;">wss://rpc.katana.stakepool.dev.br/ws</mark></a></td></tr><tr><td>RPC</td><td><a href="https://rpc.avail.stakepool.dev.br/rpc"><mark style="color:green;">https://rpc.katana.stakepool.dev.br/rpc</mark></a></td></tr></tbody></table>

\
\\


# Faucet

[<mark style="color:green;">https://faucet.stakepool.dev.br/</mark>](https://faucet.stakepool.dev.br/avail)<mark style="color:green;">katana</mark>

\
\\


# Live Peers

<table><thead><tr><th width="164">Name</th><th width="468">Chain</th></tr></thead><tbody><tr><td><strong>Katana</strong></td><td>Mainnet</td></tr><tr><td>Peer</td><td>12D3KooWPiNHg5bHGgjuNpaL1VddoHFXKH5EGhfS2dhdcKYzAPyP@65.108.76.33:30333</td></tr></tbody></table>


# Snapshots


# Katana

*Snapshots are taken automatically every 1 week.*

<table><thead><tr><th width="102">Name</th><th width="129">Chain</th><th width="105">Age</th><th width="481">Snapshot</th></tr></thead><tbody><tr><td><strong>Katana</strong></td><td>mainnet</td><td>25-11-09</td><td><a href="https://snap.stakepool.work/snapshots-stakepool/avail.tar.lz4"><mark style="color:green;">https://snap.stakepool.work/snapshots-stakepool/katana.tar.lz4</mark></a></td></tr></tbody></table>

**Avail Mainnet**

**1** - Stop the service

```bash
sudo systemctl stop katana
```

Install lz4 if needed

```bash
sudo apt install lz4
```

**2** - Download the file and extract

```bash
curl -L https://snap.stakepool.work/snapshots-stakepool/katana.tar.lz4 | lz4 -d | tar -xf -
```

\\


# Celestia

Celestia is a modular data availability network.


# RPC

<table><thead><tr><th width="164">Name</th><th width="401">Chain</th></tr></thead><tbody><tr><td><strong>Celestia</strong></td><td><strong>celestia</strong></td></tr><tr><td>API</td><td><a href="https://rest.celestia.stakepool.dev.br"><mark style="color:green;">https://rest.celestia.stakepool.dev.br</mark></a></td></tr><tr><td>RPC</td><td><a href="https://rpc.celestia.stakepool.dev.br"><mark style="color:green;">https://rpc.celestia.stakepool.dev.br</mark></a></td></tr></tbody></table>

\
\\


# Faucet

[<mark style="color:green;">https://faucet.stakepool.dev.br/celestia</mark>](https://faucet.stakepool.dev.br/celestia)

\
\\


# Live Peers

<table><thead><tr><th width="164">Name</th><th width="468">Chain</th></tr></thead><tbody><tr><td><strong>Celestia</strong></td><td>c<strong>elestia</strong></td></tr><tr><td>Peer</td><td>6a94cbd07ac7095fb25b779b6c111bf1f77458b9@45.8.149.52:26656</td></tr></tbody></table>


# Snapshots


# Celestia

<table><thead><tr><th width="201">Chain</th><th>Age</th><th>Type</th></tr></thead><tbody><tr><td>Mainnet</td><td>24-11-10</td><td>pruned</td></tr><tr><td>Mainnet</td><td>24-11-10</td><td>archive</td></tr></tbody></table>

*Snapshots are taken automatically every 1 week.*

**Celestia Mainnet**

**1** - Stop the service

```bash
systemctl stop celestia-appd
```

**2** - Download the file

<pre class="language-bash"><code class="lang-bash"><strong>aria2c -j 22 -x 16 -c https://snapshots.stakepool.dev.br/celestia-pruned.tar.lz4
</strong></code></pre>

3 - Run:

```bash
lz4 -d celestia-pruned.tar.lz4 | tar xvf -
```

**Celestia Mainnet (Archive)**

**1** - Stop the service

```bash
 systemctl stop celestia-full
```

**2** - Download the file

```bash
aria2c -j 22 -x 16 -c https://snapshots.stakepool.dev.br/celestia-full.tar.lz4
```

3 - Run:

```bash
lz4 -d celestia-full.tar.lz4 | tar xvf -
```

\\


# Mantra

Permissionless chain for permissioned apps.


# RPC

<table><thead><tr><th width="164">Name</th><th width="401">Chain</th></tr></thead><tbody><tr><td><strong>Mantra</strong></td><td><strong>Dukong-testnet</strong></td></tr><tr><td>API</td><td><a href="https://rest.mantra.stakepool.dev.br"><mark style="color:green;">https://rest.mantra.stakepool.dev.br</mark></a></td></tr><tr><td>RPC</td><td><a href="https://rpc.mantra.stakepool.dev.br"><mark style="color:green;">https://rpc.mantra.stakepool.dev.br</mark></a></td></tr></tbody></table>

\
\\


# Faucet

[<mark style="color:green;">https://faucet.stakepool.dev.br/mantra</mark>](https://faucet.stakepool.dev.br/mantra)

\
\\


# Live Peers

<table><thead><tr><th width="164">Name</th><th width="468">Chain</th></tr></thead><tbody><tr><td><strong>Mantra</strong></td><td>Dukong-testnet</td></tr><tr><td>Peer</td><td>D4BF8D57139CEC51119AE354742689F3917E89FA@45.8.149.52:21457</td></tr></tbody></table>


# Snapshots


# Mantra Pruned

<table><thead><tr><th width="201">Chain</th><th>Age</th></tr></thead><tbody><tr><td>Dukong-Testnet</td><td>24-12-01</td></tr></tbody></table>

*Snapshots are taken automatically every 1 week.*

**Mantra Testnet**

**1** - Stop the service

```bash
systemctl stop mantrachaind
```

**2** - Download the file

<pre class="language-bash"><code class="lang-bash"><strong>aria2c -j 22 -x 16 -c https://snapshots.stakepool.dev.br/mantra-pruned.tar.lz4
</strong></code></pre>

3 - Run:

```bash
lz4 -d mantra-pruned.tar.lz4 | tar xvf -
```

\\


# Namada

Permissionless chain for permissioned apps.


# Dashboard

<table><thead><tr><th width="164">Name</th><th width="401">Chain</th></tr></thead><tbody><tr><td><strong>Namada</strong></td><td>namada.5f5de2dd1b88cba30586420</td></tr><tr><td>Explorer</td><td><a href="https://votingpower.stakepool.dev.br/"><mark style="color:green;">https://votingpower.stakepool.dev.br/</mark></a></td></tr></tbody></table>

\
\\


# RPC

<table><thead><tr><th width="164">Name</th><th width="401">Chain</th></tr></thead><tbody><tr><td><strong>Namada</strong></td><td>namada.5f5de2dd1b88cba30586420</td></tr><tr><td>API</td><td><a href="https://rpc.namada.stakepool.dev.br/"><mark style="color:green;">https://rpc.namada.stakepool.dev.br/</mark></a></td></tr><tr><td>Indexer</td><td><a href="https://indexer.namada.stakepool.dev.br/"><mark style="color:green;">https://indexer.namada.stakepool.dev.br/</mark></a></td></tr></tbody></table>

\
\\


# Live Peers

<table><thead><tr><th width="164">Name</th><th width="468">Chain</th></tr></thead><tbody><tr><td><strong>Namada</strong></td><td>namada.5f5de2dd1b88cba30586420</td></tr><tr><td>Peer</td><td>3f49c8aee42c84e1db42684b48823d436000390f@45.8.149.52:26656</td></tr></tbody></table>


# Snapshots


# Namada

*Snapshots are taken automatically every 1 week.*

<table><thead><tr><th width="102">Name</th><th width="129">Chain</th><th width="105">Age</th><th width="535">Snapshot</th></tr></thead><tbody><tr><td><strong>Namada</strong></td><td>mainnet</td><td>25-06-22</td><td><a href="https://snap.stakepool.work/snapshots-stakepool/avail.tar.lz4"><mark style="color:green;">https</mark></a><a href="https://snap.stakepool.work/snapshots-stakepool/namada.tar.lz4"><mark style="color:green;">://snap.stakepool.work/snapshots-stakepool/namada.tar.lz4</mark></a></td></tr></tbody></table>

**Namada Mainnet**

**1** - Stop the service

```bash
sudo systemctl stop namada 
```

Install lz4 if needed

```bash
sudo apt install lz4
```

**2** - Download the file and extract

```bash
curl -L https://snap.stakepool.work/snapshots-stakepool/namada.tar.lz4 | lz4 -d | tar -xf -
```

\\


# Polygon

Polygon, formerly known as Matic, is a scalability platform for blockchain networks based on Ethereum technology.


# Binaries

Precompiled binaries are executable versions of the software that can be installed directly on your operating system without the need for compilation. This saves time and simplifies the installation process for users who just want to use the software without dealing with software development.


# Run a Node

Hardware Requirements:[​](https://docs.celestia.org/nodes/bridge-validator-node#hardware-requirements)

The following hardware minimum requirements are recommended for running the node:

* Memory: 64 GB RAM
* CPU: 16-Core
* Disk: 6 TB SDD Storage
* Bandwidth: 1 Gbps for Download/100 Mbps for Upload

#### Setting Up Your Node:[​](https://docs.celestia.org/nodes/bridge-validator-node#setting-up-your-bridge-node) <a href="#setting-up-your-bridge-node" id="setting-up-your-bridge-node"></a>

The following tutorial is done on an Ubuntu Linux 20.04 (LTS) x64 instance machine.

#### Node Installation:

This guide shows how to install and run an Polygon full node.

#### How to install Polygon from source:

* Before you start, you might want to ensure your system is updated. You can also install a utility named `jq` to read and navigate in JSON files and outputs (other utilities could be installed too).

  ```
  sudo apt update && sudo apt upgrade -y && sudo apt install -y build-essential curl wget jq
  ```

To install **Heimdall**, run the below commands:

```
curl -L https://raw.githubusercontent.com/maticnetwork/install/main/heimdall.sh | bash -s -- <heimdall_version> <network_type> <node_type>
```

That will install the `heimdalld` and `heimdallcli` binaries. Verify the installation by checking the Heimdall version on your machine:

```
heimdalld version --long
```

#### Configure heimdall seeds (mainnet)[​](https://wiki.polygon.technology/docs/pos/operate/node/full-node-binaries/#configure-bor-seeds-mainnet) <a href="#configure-bor-seeds-mainnet" id="configure-bor-seeds-mainnet"></a>

```
sed -i 's|^seeds =.*|seeds = "1500161dd491b67fb1ac81868952be49e2509c9f@52.78.36.216:26656,dd4a3f1750af5765266231b9d8ac764599921736@3.36.224.80:26656,8ea4f592ad6cc38d7532aff418d1fb97052463af@34.240.245.39:26656,e772e1fb8c3492a9570a377a5eafdb1dc53cd778@54.194.245.5:26656,6726b826df45ac8e9afb4bdb2469c7771bd797f1@52.209.21.164:26656"|g' /var/lib/heimdall/config/config.toml
chown heimdall /var/lib/heimdall
```

#### Configure heimdall seeds (amoy)[​](https://wiki.polygon.technology/docs/pos/operate/node/full-node-binaries/#configure-bor-seeds-mumbai) <a href="#configure-bor-seeds-mumbai" id="configure-bor-seeds-mumbai"></a>

```
sed -i 's|^seeds =.*|seeds = "9df7ae4bf9b996c0e3436ed4cd3050dbc5742a28@43.200.206.40:26656,d9275750bc877b0276c374307f0fd7eae1d71e35@54.216.248.9:26656,1a3258eb2b69b235d4749cf9266a94567d6c0199@52.214.83.78:26656"|g' /var/lib/heimdall/config/config.toml
chown heimdall /var/lib/heimdall
```

**Open the P2P port (26656 by default)**

```
sudo ufw allow 26656
```

### To install **Bor**, run the below commands:

```
curl -L https://raw.githubusercontent.com/maticnetwork/install/main/bor.sh | bash -s -- <bor_version> <network_type> <node_type>
```

That will install the `bor` binary. Verify the installation by checking the Bor version on your machine:

```
bor version
```

#### Configure bor seeds (mainnet)[​](https://wiki.polygon.technology/docs/pos/operate/node/full-node-binaries/#configure-bor-seeds-mainnet) <a href="#configure-bor-seeds-mainnet" id="configure-bor-seeds-mainnet"></a>

```
sed -i 's|.*\[p2p.discovery\]|  \[p2p.discovery\] |g' /var/lib/bor/config.toml
sed -i 's|.*bootnodes =.*|    bootnodes = ["enode://b8f1cc9c5d4403703fbf377116469667d2b1823c0daf16b7250aa576bacf399e42c3930ccfcb02c5df6879565a2b8931335565f0e8d3f8e72385ecf4a4bf160a@3.36.224.80:30303", "enode://8729e0c825f3d9cad382555f3e46dcff21af323e89025a0e6312df541f4a9e73abfa562d64906f5e59c51fe6f0501b3e61b07979606c56329c020ed739910759@54.194.245.5:30303"]|g' /var/lib/bor/config.toml
chown bor /var/lib/bor
```

#### Configure bor seeds (amoy)[​](https://wiki.polygon.technology/docs/pos/operate/node/full-node-binaries/#configure-bor-seeds-mumbai) <a href="#configure-bor-seeds-mumbai" id="configure-bor-seeds-mumbai"></a>

```
sed -i 's|.*\[p2p.discovery\]|  \[p2p.discovery\] |g' /var/lib/bor/config.toml
sed -i 's|.*bootnodes =.*|    bootnodes = ["enode://bdcd4786a616a853b8a041f53496d853c68d99d54ff305615cd91c03cd56895e0a7f6e9f35dbf89131044e2114a9a782b792b5661e3aff07faf125a98606a071@43.200.206.40:30303", "enode://209aaf7ed549cf4a5700fd833da25413f80a1248bd3aa7fe2a87203e3f7b236dd729579e5c8df61c97bf508281bae4969d6de76a7393bcbd04a0af70270333b3@54.216.248.9:30303"]|g' /var/lib/bor/config.toml
chown bor /var/lib/bor
```

**Open the port (30303 by default)**

```
sudo ufw allow 30303
```

### Once the has been configured, we are ready to start the node and sync the data:

**Start Heimdall Service.**

```
sudo service heimdalld start
```

* Check logs in real tim&#x65;**.**

```
journalctl -u heimdalld.service -f
```

* To verify that the heimdall node is synced before proceeding.

```
curl -s localhost:26657/status | jq .result | jq .sync_info
```

Make sure that you have <mark style="color:orange;">`"catching_up": false`</mark>, otherwise leave it running until it is in sync.

Now once Heimdall is synced, run.

#### Start Bor.

```
sudo service bor start
```

* You can check Bor logs here:

```
journalctl -u bor.service -f
```

* To verify that the bor node is synced before proceeding.

```
curl -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' http://localhost:8545
```

Make sure that you have <mark style="color:orange;">"catching\_up": false</mark>, otherwise leave it running until it is in sync.


# Monitoring Grafana

<https://grafana.com/>

\
Grafana is an open-source platform for data analysis and monitoring. It enables you to query, visualize, and comprehend metrics from various systems, such as databases, servers, cloud services, and many others.

## Prometheus Install

#### 1 - Download latest software version

```
wget https://github.com/prometheus/prometheus/releases/download/v2.30.2/prometheus-2.30.2.linux-amd64
```

#### 2 - Unzip

```
tar -xvf prometheus-2.30.2.linux-amd64.tar.gz
```

#### 3 - Copying contents from the prometheus directory to the appropriate directory

```
sudo cp -r ./prometheus-2.30.2.linux-amd64 /usr/local/bin/prometheus
```

#### 4 - Remove downloaded files

```
rm prometheus-2.30.2.linux-amd64.tar.gz
rm -r prometheus-2.30.2.linux-amd64
```

### 5 - Creating service to run prometheus

#### 5.1 - Create Prometheus Service File

Create a file:

```
sudo vi /etc/systemd/system/prometheus.service
```

Put the following content inside it:

```
[Unit]
Description=Prometheus Service
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/prometheus/prometheus --config.file /usr/local/bin/prometheus/prometheus.yml

[Install]
WantedBy=multi-user.target
```

#### 5.2 - Reload daemons

```
sudo systemctl daemon-reload
```

#### 6 - Execute the prometheus

```
sudo systemctl start prometheus
```

#### 7 - Checking if prometheus is really running

```
sudo systemctl status prometheus
```

Something like this will appear:

```
● prometheus.service - Prometheus Service
     Loaded: loaded (/etc/systemd/system/prometheus.service; disabled; vendor preset: enabled)
     Active: active (running) since Sun 2021-10-03 04:55:46 UTC; 7min ago
   Main PID: 2388 (prometheus)
      Tasks: 6 (limit: 1071)
     Memory: 22.2M
     CGroup: /system.slice/prometheus.service
             └─2388 /usr/local/bin/prometheus/prometheus --config.file /usr/local/bin/prometheus/prometheus.yml
```

#### 8 - Enabling service in boot

```
sudo systemctl enable prometheus
```

## Node exporter install

#### 1 - Download latest software version

```
wget https://github.com/prometheus/node_exporter/releases/download/v1.3.1/node_exporter-1.3.1.linux-amd64.tar.gz
```

#### 2 - Unzip

```
tar -xvf node_exporter-1.3.1.linux-amd64.tar.gz
```

#### 3 - Copying contents from the node\_exporter directory to the appropriate directory

```
sudo cp -r ./node_exporter-1.3.1.linux-amd64 /usr/local/bin/node_exporter
```

#### 4 - Remove downloaded files

```
rm node_exporter-1.3.1.linux-amd64.tar.gz
rm -r node_exporter-1.3.1.linux-amd64
```

### 5 - Creating service to run node\_exporter

#### 5.1 - Create Node exporter Service File

Create a file:

```
sudo vi /etc/systemd/system/node_exporter.service
```

Put the following content inside it:

```
[Unit]
Description=Node Exporter Service
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/node_exporter/node_exporter

[Install]
WantedBy=multi-user.target
```

#### 5.2 - Reload daemons

```
sudo systemctl daemon-reload
```

#### 6 - Execute the node\_exporter

```
sudo systemctl start node_exporter
```

#### 7 - Checking if node\_exporter is really running

```
sudo systemctl status node_exporter
```

Something like this will appear:

```
sudo systemctl status node_exporter
● node_exporter.service - Node Exporter Service
     Loaded: loaded (/etc/systemd/system/node_exporter.service; disabled; vendor preset: enabled)
     Active: active (running) since Sun 2021-10-03 05:23:18 UTC; 14min ago
   Main PID: 2808 (node_exporter)
      Tasks: 3 (limit: 1071)
     Memory: 2.0M
     CGroup: /system.slice/node_exporter.service
             └─2808 /usr/local/bin/node_exporter/node_exporter
```

#### 8 - Enabling service in boot

```
sudo systemctl enable node_exporter
```

## Grafana Install

#### 1 - Download latest software version and install

```
sudo apt-get install -y adduser libfontconfig1
wget https://dl.grafana.com/oss/release/grafana_8.1.5_amd64.deb
sudo dpkg -i grafana_8.1.5_amd64.deb
```

#### 2 - Remove downloaded package

```
rm grafana_8.1.5_amd64.deb
```

#### 3 - Start grafana-server service

```
sudo systemctl start grafana-server
```

#### 4 - Checking if grafana-server is really running

```
sudo systemctl status grafana-server
```

Something like this will appear:

```
● grafana-server.service - Grafana instance
     Loaded: loaded (/lib/systemd/system/grafana-server.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2021-10-03 23:40:30 UTC; 2h 41min ago
       Docs: http://docs.grafana.org
   Main PID: 7047 (grafana-server)
      Tasks: 11 (limit: 4705)
     Memory: 32.7M
     CGroup: /system.slice/grafana-server.service
             └─7047 /usr/sbin/grafana-server --config=/etc/grafana/grafana.ini --pidfile=/var/run/grafana/grafana-server.pid --packaging=deb 
cfg:default.paths.logs=/var/log/grafana cfg:default.paths.data=/var/lib/grafana cfg:default.paths.plugins=/var/lib/grafana/plugins cfg:defaul
t.paths.provisioning=/etc/grafana/provisioning
```

#### 5 - Enabling service in boot

```
sudo systemctl enable grafana-server
```

#### 6 - Access Grafana

Open Grafana at the following URL: <http://host\\_ip:3000>

<figure><img src="/files/nu2yBCUeSoAbcmKHuB7K" alt=""><figcaption></figcaption></figure>

* Note: Open port 3000 in your firewall
* Grafana's default credentials are:
* username: admin password: admin

<figure><img src="/files/joY6Byaz2bQpEOOaN2PZ" alt=""><figcaption></figcaption></figure>

Note: a good security practice is to change the admin user password after the first login.

#### 7 - Define Data Source

Select Data source option in Configuration section in left panel.

<figure><img src="/files/aoue6fQ4wfYFoiCbN9O2" alt=""><figcaption></figcaption></figure>

Select Prometheus datasource type.

<figure><img src="/files/gMKAB8NJXURpQmraQSZU" alt=""><figcaption></figcaption></figure>

Define Prometheus name and URL.

URL: [http://localhost:9090](http://localhost:9090/)

<figure><img src="/files/8DzlXVbrPCN8QewGjQFu" alt=""><figcaption></figcaption></figure>

Click in Save and test button.

<figure><img src="/files/t70iTXavDcJxkr7Qsx2n" alt=""><figcaption></figcaption></figure>

#### 8 - Import Dashboard

<figure><img src="/files/tEVvtH77J4DbFAt4waNe" alt=""><figcaption></figcaption></figure>

Upload file and click in load button.

\\

<figure><img src="/files/kqeOlpiVjvDtR1MVtfsh" alt=""><figcaption></figcaption></figure>

Define name of Dashboard, select folder and click in import button.

<figure><img src="/files/fnMB2ZnfgpY5ECFQ46pV" alt=""><figcaption></figcaption></figure>

If everything happens normally, your dashboard will appear on the screen.

<figure><img src="/files/fd7d1qQcIpdDeklQhdMV" alt=""><figcaption></figcaption></figure>

## Grafana Alerts

#### 1 - Access the Alert menu and then click Admin

<figure><img src="/files/GXj7HAZqU0F7duCDRKBS" alt=""><figcaption></figcaption></figure>

#### 2 - Click the "Add Alertmanager" button

<figure><img src="/files/FkGNU3jhGmvOkFIEBu7k" alt=""><figcaption></figcaption></figure>

#### 3 - Set the URL where your external alertmanager instance is installed and click on the "Add alertmanagers" button

<figure><img src="/files/UcaxejTG7i1tlWD0u6G6" alt=""><figcaption></figcaption></figure>

#### 4 - Access the panel inside the Dashboard that you want to add the alert, right-click on the panel and then click "Edit"

<figure><img src="/files/Z0lJub9vXv5f6mOPtcFq" alt=""><figcaption></figcaption></figure>

#### 5 - Click the "Alert" tab and click the "Create alert rule from this panel" button to create an alert with a direct link to this panel.

<figure><img src="/files/uhHecVBgkkLjglDZx0Nl" alt=""><figcaption></figcaption></figure>

#### 6 - Define the name of the alert, the folder, the function, select the query and the limit that will be used for the alert.

<figure><img src="/files/OsGDcmNshXjmoL1jCOQ6" alt=""><figcaption></figcaption></figure>

#### 7 - Set the field "evaluate every" and "for", if necessary add a custom label and then click "Save and exit"

<figure><img src="/files/1hoOKdkVzTqMbHS9vEee" alt=""><figcaption></figcaption></figure>

## Nginx Install and enable HTTPS connections with Let's Encrypt

```
sudo apt install nginx
```

Enable Nginx in boot:

```
sudo systemctl enable nginx
```

Certbot snap version install:

```
sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo certbot --nginx
```

Configure /etc/nginx/sites-enabled/default :

```
location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ =404;
                proxy_pass http://localhost:3000;
        }


        location /api/live {
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "Upgrade";
                proxy_set_header Host $http_host;
                proxy_pass http://localhost:3000/;
        }

        location /grafana/ {
                proxy_pass         http://localhost:3000/;
                proxy_set_header   Host $Host;
                proxy_max_temp_file_size 0;
        }
```

Configure /etc/grafana/grafana.ini :

```
[server]

root_url = %(protocol)s://%(domain)s:/grafana
serve_from_sub_path = true
```

Restart nginx and grafana-server services :

```
sudo systemctl restart grafana-server
sudo systemctl restart nginx
```

## Configuring names in hosts file

#### 1 - Edit /etc/hosts file

Define the names that will be used for each host, and put them in the file /etc/hosts, follow exemple:

```
sudo nano /etc/hosts

nameofhost1 10.0.0.1
nameofhost2 10.0.0.2
nameofhost3 10.0.0.3
```

#### 2 - Test configuration with ping command

```
ping nameofhost
```

Something like this will appear:

```
PING localhost (10.0.0.1) 56(84) bytes of data.
64 bytes from ip6-localhost (10.0.0.1): icmp_seq=1 ttl=64 time=0.012 ms
64 bytes from ip6-localhost (10.0.0.1): icmp_seq=2 ttl=64 time=0.042 ms
```

if the IP address that returns after the command matches the name you put in the /etc/hosts file, the configuration is correct

Now in the configuration of prometheus you can configure the host name instead of the IP

```
  - job_name: "Validators"
    static_configs:
      - targets: ["nameofhost1:9100","nameofhost2:9100","nameofhost3:9100"]
```

## GitHub OAuth2 Authentication

#### 1 - Register your application with GitHub

To enable the GitHub OAuth2 you must register your application with GitHub. GitHub will generate a client ID and secret key for you to use. When you create the application you will need to specify a callback URL. Specify this as callback:

```
https://myrealdomain.com/grafana/login/github
```

Access your organization

<figure><img src="/files/23p2KLoyGNKFNGaWciGR" alt=""><figcaption></figcaption></figure>

Access Settings of your organization

<figure><img src="/files/FhpBa5HPQ0qj1wB1s9u3" alt=""><figcaption></figcaption></figure>

Access Developer section and select OAuth App

<figure><img src="/files/Q0AWUiZzlx49Sm3H5sjN" alt=""><figcaption></figcaption></figure>

Click in "New OAuth App"

<figure><img src="/files/Wqc06N4hFsPirxRPqm5p" alt=""><figcaption></figcaption></figure>

Define OAuth app info

<figure><img src="/files/qpkuYgShjKYN1JUqHVTm" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/cpKgsyq8DcXG7hEJtlSj" alt=""><figcaption></figcaption></figure>

Get Client ID and generate new client secret

<figure><img src="/files/mCRHejGBVm40vKBptezv" alt=""><figcaption></figcaption></figure>

Store your Client ID and Client secret for alertmanager configuration

#### 2 - Set root\_url in grafana.ini file

```
sudo nano /etc/grafana/grafana.ini
[server]
root_url = https://myrealdomain.com/grafana
serve_from_sub_path = true
```

#### 3 - Enable GitHub in Grafana in grafana.ini file

```
sudo nano /etc/grafana/grafana.ini
[auth.github]
enabled = true
allow_sign_up = true
client_id = YOUR_GITHUB_APP_CLIENT_ID
client_secret = YOUR_GITHUB_APP_CLIENT_SECRET
scopes = user:email,read:org
auth_url = https://github.com/login/oauth/authorize
token_url = https://github.com/login/oauth/access_token
api_url = https://api.github.com/user
# space-delimited organization names
allowed_organizations = organization1 organization2
```

#### 4 - Restart grafana-server service

```
sudo systemctl restart grafana-server
```

#### 5 - After logout we have a login option with github

<figure><img src="/files/0FdMxObNJPvZysqv07KU" alt=""><figcaption></figcaption></figure>


# Alert Message Api

## Discord and Slack custom messages with Message API

Software install

### 1 - Clone repository

```
git clone git@github.com:Michel-Leidson/alert-message-api.git
```

### 2 - Enter the project directory

```
cd alert-message-api
```

### 3 - Install dependencies

```
npm install
```

### 4 - Create and configure config.json file in root directory of aplication

```
nano config.json

{
    "slack": {
        "url":"<YOUR SLACK URL WEBHOOK HERE>"
    },
    "discord":{
        "url":"<YOUR DISCORD URL WEBHOOK HERE>"
    },
    "grafana":{
        "url":"YOUR GRAFANA URL HERE"
    },
    "alertmanager": {
        "url":"YOUR ALERTMANAGER URL HERE"
    }
}
```

### 5 - Running the server.js file with PM2

```
pm2 start server.js
```

Configuring external Alertmanager in Grafana

#### 1 - Select Alert Admin menu in Grafana

<figure><img src="/files/VRJEzLzJUzfVPFlu4doW" alt=""><figcaption></figcaption></figure>

#### 2 - Click in "Add Alertmanager"

<figure><img src="/files/7bdWPDNk7CJuHL0PljDG" alt=""><figcaption></figcaption></figure>

#### 3 - Type URL alermanager and click in "Add Alertmanagers" button

<figure><img src="/files/fONYAjvXPiYRqzVxELyY" alt=""><figcaption></figcaption></figure>

Configuring webhook routes in alertmanager config file

#### 1 - Configuring alertmanager config file

```
sudo nano /usr/local/alertmanager/alertmanager.yml

route:
    
  group_by: ['alertname']
  group_wait: 30s
  group_interval: 1m
  repeat_interval: 12h
  receiver: "apialert"

  routes:

    - receiver: "apialert"
      group_wait: 30s
      group_interval: 1m
      repeat_interval: 12h
      continue: true

    - receiver: "apialertslack"
      group_wait: 30s
      group_interval: 1m
      repeat_interval: 12h
      continue: true


receivers:

  - name: "apialert"
    webhook_configs:
    - url: "http://127.0.0.1:5002/discord"

  - name: "apialertslack"
    webhook_configs:
    - url: "http://127.0.0.1:5002/slack"
```

### 2 - Restart alertmanager

```
sudo systemctl restart alertmanager
```


# Monitoring Blocks Heimdall and Bor

#### Server

1 - Prerequisites

Curl

```
sudo apt install curl
```

NVM

```
curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash
source ~/.profile
```

Nodejs

```
nvm install node
```

PM2

```
npm install -g pm2
```

Git

```
sudo apt install git
```

2 - Download code

```
git clone git@github.com:Michel-Leidson/blocks-monitoring.git
```

3 - Enter in project directory

```
cd blocks-monitoring
```

4 - Install dependencies of project

```
npm install
```

5 - Running as a service in PM2

```
pm2 start server.js --name <choice-a-name-of-your-service>
pm2 save
pm2 startup
```

6 - Configure job in prometheus config

a - Add job in prometheus config file.

```
 - job_name: 'monitor_polygon_blocks'
    static_configs:
      - targets: ['localhost:9992']
```

b - Restart prometheus service

```
sudo systemctl restart prometheus
```

7 - Configuring reverse proxy nginx

a - Open your nginx configuration file

```
nano /etc/nginx/sites-enabled/default
```

b - Paste the content in the file

```
       location /blockshb/ {
                gzip_types *;
                proxy_pass         http://127.0.0.1:9992/;
                proxy_set_header   Host              $host;
                proxy_set_header   X-Real-IP         $remote_addr;
                proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
                proxy_set_header   X-Forwarded-Host   $host:443;
                proxy_set_header   X-Forwarded-Server $host;
                proxy_set_header   X-Forwarded-Port 443;
                proxy_set_header   X-Forwarded-Proto $scheme;
        }
```

c - Save the file

d - Restart Nginx service

```
sudo systemctl restart nginx
```

#### Client

1 - Prerequisites

Curl

```
sudo apt install curl
```

NVM

```
curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash
source ~/.profile
```

Nodejs

```
nvm install node
```

PM2

```
npm install -g pm2
```

Git

```
sudo apt install git
```

2 - Download the code

```
git clone git@github.com:Michel-Leidson/blocks-monitoring-polygon-client.git
```

3 - Enter the project directory

```
cd blocks-monitoring-polygon-client
```

4 - Install dependencies of project, and install dotenv with dev dependency flag

```
npm install
npm install dotenv --save-dev
```

5 - Configure .env file based on .env.example

a - Copy .env.example to .env file

```
cp .env.example .env

nano .env
```

b - Set your variabales

```
MATIC_VALIDATOR_ID=<node_name>
BACKEND_API=https://your-domain.com/blockshb
```

6 - Running as a service in PM2

```
pm2 start index.js --name <choice-a-name-of-your-service>
pm2 save
pm2 startup
```

### Create Heimdall and Bor Grafana Dashboard

1 - In "Dashboards" menu click in "Browse"

<figure><img src="/files/00ae8nRLtxi5ctpMDxi6" alt=""><figcaption></figcaption></figure>

2 - Click in "New Dashboard" button

<figure><img src="/files/RZSlF4JYrLyQ5ZT3r7yJ" alt=""><figcaption></figcaption></figure>

3 - Click in "Add new panel" button

<figure><img src="/files/MyPUIP8T4s7BkPnVeGd8" alt=""><figcaption></figcaption></figure>

4 - Define missed\_blocks\_type="heimdall" and define missed\_blocks\_matic\_id in query

<figure><img src="/files/G7HIQhzyM3cmM6vocNzy" alt=""><figcaption></figcaption></figure>

5 - Define title and description of panel and click in save button

<figure><img src="/files/91cm0rpy2eY0f4s3pna9" alt=""><figcaption></figcaption></figure>

6 - Define name of Dashboard select folder and click in "Save" button

<figure><img src="/files/pEFpBj2gr7Coh8SHFnd6" alt=""><figcaption></figcaption></figure>

7 - Duplicate heimdall panel and click in "Edit" button

<figure><img src="/files/wpxN6CKE3UfA5jDvq6JO" alt=""><figcaption></figcaption></figure>

8 - Define missed\_blocks\_type="bor" and define missed\_blocks\_matic\_id in query

<figure><img src="/files/nLKkKRT3NHJciEXMJx1I" alt=""><figcaption></figcaption></figure>

9 - Define title and description of panel and click in save button

<figure><img src="/files/4qno2gjyJeLN2FJ74guz" alt=""><figcaption></figcaption></figure>


# Monitoring logs

## Centralized Logs with Loki and Promtail

Loki install

#### 1 - Download loki

```
wget https://github.com/grafana/loki/releases/download/v2.8.0/loki-linux-amd64.zip
```

#### 2 - Download Loki example configuration file

```
wget https://raw.githubusercontent.com/grafana/loki/master/cmd/loki/loki-local-config.yaml
```

#### 3 - Create directory for Loki application

```
sudo mkdir /usr/local/bin/loki
```

#### 4 - Unzip loki

```
unzip loki-linux-amd64.zip
```

#### 5 - Copy files to Loki application directory

```
sudo cp loki-linux-amd64 /usr/local/bin/loki
sudo cp loki-local-config.yaml /usr/local/bin/loki
```

#### 6 - Create service file

```
sudo vi /etc/systemd/system/loki.service
```

#### Put this content in the file and save

```
[Unit]
Description=Loki Service
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/loki/loki-linux-amd64 -config.file=/usr/local/bin/loki/loki-local-config.yaml

[Install]
WantedBy=multi-user.target
```

#### 7 - Reload daemons

```
sudo systemctl daemon-reload
```

#### 8 - Execute the loki

```
sudo systemctl start loki
```

#### 9 - Checking if loki is really running

```
sudo systemctl status loki
```

#### 10 - Enabling service in boot

```
sudo systemctl enable loki
```

Promtail install

#### 1 - Download Promtail

```
wget https://github.com/grafana/loki/releases/download/v2.4.2/promtail-linux-amd64.zip
```

#### 2 - Download Promtail example configuration file

```
wget https://raw.githubusercontent.com/grafana/loki/main/clients/cmd/promtail/promtail-local-config.yaml
```

#### 3 - Create directory for Promtail application

```
sudo mkdir /usr/local/bin/promtail
```

#### 4 - Unzip promtail

```
unzip promtail-linux-amd64.zip
```

#### 5 - Copy files to Promtail application directory

```
sudo cp promtail-linux-amd64 /usr/local/bin/promtail
sudo cp promtail-local-config.yaml /usr/local/bin/promtail
```

#### 6 - Configuring the Promtail configuration file

Open file

```
sudo nano /usr/local/bin/promtail/promtail-local-config.yaml
```

Set the address of the Loki Server that will receive the Log stream, and the path of the files that will be sent.

```
server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://localhost:3100/loki/api/v1/push

scrape_configs:
- job_name: system
  static_configs:
  - targets:
      - localhost
    labels:
      job: <LABEL NAME OF YOUR JOB>
      __path__: <PATH OF YOUR LOG FILE>
```

#### 7 - Create service file

```
sudo vi /etc/systemd/system/promtail.service
```

Put this content in the file and save

```
[Unit]
Description=Promtail Service
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/promtail/promtail-linux-amd64 -config.file=/usr/local/bin/promtail/promtail-local-config.yaml

[Install]
WantedBy=multi-user.target
```

#### 8 - Reload daemons

```
sudo systemctl daemon-reload
```

#### 9 - Execute the promtail

```
sudo systemctl start promtail
```

#### 10 - Checking if promtail is really running

```
sudo systemctl status promtail
```

#### 11 - Enabling service in boot

```
sudo systemctl enable promtail
```

### Dashboards Loki query based

1 - In "Dashboards" menu click in "Browse"

<figure><img src="/files/00ae8nRLtxi5ctpMDxi6" alt=""><figcaption></figcaption></figure>

2 - Click in "New Dashboard" button or click on an existing dashboard

<figure><img src="/files/RZSlF4JYrLyQ5ZT3r7yJ" alt=""><figcaption></figcaption></figure>

3 - Click in "Add new panel" button

<figure><img src="/files/MyPUIP8T4s7BkPnVeGd8" alt=""><figcaption></figcaption></figure>

4 - Select Loki datasource

<figure><img src="/files/stEReZf81YPEd0Aj4g70" alt=""><figcaption></figcaption></figure>

5 - Define the content filter in Loki query

<figure><img src="/files/feq0E2J5jdAZ1RZwQ05r" alt=""><figcaption></figcaption></figure>

6 - Define title and description of panel and click in save button

<figure><img src="/files/YIADeeP3btppKfnpSRVU" alt=""><figcaption></figcaption></figure>


# Kubernetes

[*https://kubernetes.io/*](https://kubernetes.io/)

*When you choose to use a public cloud-based Kubernetes service, such as Amazon EKS, Google Kubernetes Engine (GKE), or Azure Kubernetes Service (AKS), configuration and control plane management are automatically managed by the cloud provider. This includes ensuring high availability, security updates, and other essential operational tasks related to the functioning of Kubernetes. In this guide, we will cover all the necessary steps, from creating the control plane to managing the worker nodes. However, if you are using a cloud-managed Kubernetes service, you can skip directly to* [*cluster workes*](/polygon/kubernetes/worker-cluster) *as the control plane is already configured and maintained by the cloud provider. This allows you to focus on configuring and managing specific components of your cluster, such as applications and services.*


# Technical documentation

Here are the relevant details:

**Polygon Node Deployment Configuration**

This Kubernetes Deployment is designed to deploy Polygon node, a critical component of our blockchain ecosystem.

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: polygon-sentry-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: Polygon-Sentry
  template:
    metadata:
      labels:
        app: Polygon-Sentry
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: environment
                operator: In
                values:
                - sentry
      containers:
        - name: heimdall
          image: 0xpolygon/heimdall:1.0.0-beta
          command:
            - heimdalld
            - start
          args:
            - --chain=mumbai
            - --rest-server
            - --home=/var/lib/heimdall
          ports:
            - containerPort: 26656
          volumeMounts:
            - name: heimdall-config-volume
              mountPath: /var/lib/heimdall
        - name: bor
          image: 0xpolygon/bor:1.0.0-beta
          command:
            - bor
            - server
            - --config=/var/lib/bor/config.toml
          ports:
            - containerPort: 30303
          volumeMounts:
            - name: bor-config-volume
              mountPath: /var/lib/bor
        - name: rabbitmq
          image: rabbitmq:3.8
          ports:
            - containerPort: 5672
            - containerPort: 15672
      volumes:
        - name: heimdall-config-volume
          persistentVolumeClaim:
            claimName: heimdall-pvc-sentry
        - name: bor-config-volume
          persistentVolumeClaim:
            claimName: bor-pvc-sentry
```

Polygon node consists of three main containers:

**heimdall Container**

The heimdall container is responsible for running the Heimdall node on the Polygon network. Its specific configurations include:

* Docker Image: `0xpolygon/heimdall:1.0.0-beta`
* Command: Initiates the Heimdall process.
* Arguments: Specifies various configuration options for Heimdall, such as the chain to connect to (--chain=mumbai), enabling the REST server (--rest-server), and the Heimdall data directory (--home=/var/lib/heimdall).
* Ports: Exposes port 26656 to establish connections with other nodes for peer-to-peer communication.
* Volume: Mounts a volume named `heimdall-config-volume` to store persistent Heimdall data.

Heimdall is critical for transaction validation on the Polygon network, ensuring the security and integrity of the blockchain.

**bor Container**

The bor container is responsible for running the Bor node on the Polygon network. Its specific configurations include:

* Docker Image: `0xpolygon/bor:1.0.0-beta`
* Command: Starts the Bor server.
* Arguments: Specifies the path to the Bor configuration file (--config=/var/lib/bor/config.toml).
* Volume: Mounts a volume named `bor-config-volume` to store persistent Bor configuration data.

Bor plays a crucial role in the Polygon network, handling the consensus layer and transaction validation.

**rabbitmq Container**

The rabbitmq container uses the official RabbitMQ image and is responsible for providing a messaging infrastructure for internal system communication. Its specific configurations include:

* Docker Image: `rabbitmq:3.8`
* Ports: Exposes ports `5672` and `15672` for communication with services that require messaging.

RabbitMQ is a fundamental part of our system, enabling asynchronous communication between components for coordination and information exchange.

This Deployment is part of a broader infrastructure that supports the secure and efficient operation of our Polygon network.

**Persistent Volumes (PVs) e Persistent Volume Claims (PVCs)**

These PVs and PVCs play a crucial role in ensuring data persistence and storage management for these components within the Kubernetes cluster.

```yaml
# PV e PVC para Heimdall
apiVersion: v1
kind: PersistentVolume
metadata:
  name: heimdall-pv-sentry
  labels:
    app: Polygon-Sentry
spec:
  capacity:
    storage: 150Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: standard
  hostPath:
    path: /var/lib/heimdall

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: heimdall-pvc-sentry
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 150Gi
  storageClassName: standard
  selector:
    matchLabels:
      app: Polygon-Sentry

---
# PV e PVC para Bor
apiVersion: v1
kind: PersistentVolume
metadata:
  name: bor-pv-sentry
  labels:
    app: Polygon-Sentry
spec:
  capacity:
    storage: 600Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: standard
  hostPath:
    path: /var/lib/bor

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: bor-pvc-sentry
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 600Gi
  storageClassName: standard
  selector:
    matchLabels:
      app: Polygon-Sentry
```

Let's break down their purposes and significance:

**PV and PVC for Heimdall**

**`heimdall-pv-sentry` Persistent Volume (PV):**

* **Purpose:** This PV is intended to provide a persistent storage solution for the Heimdall component of Polygon node.
* **Configuration Highlights:**
  * Capacity: 150 gigabytes of storage (`storage: 150Gi`).
  * Access Mode: ReadWriteOnce, allowing read and write access by a single node at a time.
  * Reclaim Policy: Retain, which means that even after the PVC is deleted, the data in the PV will be retained.
  * Storage Class: Uses the `standard` storage class.
  * Host Path: Maps to the host machine's `/var/lib/heimdall` directory, ensuring data persistence.

**`heimdall-pvc-sentry` Persistent Volume Claim (PVC):**

* **Purpose:** The PVC is a request for storage resources that will be bound to the `heimdall-pv-sentry` PV. It allows Heimdall to access and use the specified storage capacity.
* **Configuration Highlights:**
  * Access Mode: ReadWriteOnce, ensuring that only one node can use the storage at a time.
  * Requested Storage: Requests 150 gigabytes of storage (`storage: 150Gi`).
  * Storage Class: Uses the `standard` storage class.
  * Selector: Matches labels with the `app: Polygon-Sentry` label to associate this PVC with the Polygon node application.

**PV and PVC for Bor**

**`bor-pv-sentry` Persistent Volume (PV):**

* **Purpose:** This PV is designed to offer persistent storage for the Bor component of Polygon node.
* **Configuration Highlights:**
  * Capacity: 600 gigabytes of storage (`storage: 600Gi`).
  * Access Mode: ReadWriteOnce, allowing a single node to read and write data.
  * Reclaim Policy: Retain, ensuring data retention even after PVC deletion.
  * Storage Class: Utilizes the `standard` storage class.
  * Host Path: Mapped to the host machine's `/var/lib/bor` directory to maintain data persistence.

**`bor-pvc-sentry` Persistent Volume Claim (PVC):**

* **Purpose:** The PVC represents a request for storage resources bound to the `bor-pv-sentry` PV. It facilitates Bor's access to the specified storage capacity.
* **Configuration Highlights:**
  * Access Mode: ReadWriteOnce, allowing exclusive access by one node.
  * Requested Storage: Requests 600 gigabytes of storage (`storage: 600Gi`).
  * Storage Class: Utilizes the `standard` storage class.
  * Selector: Matches labels with the `app: Polygon-Sentry` label to associate this PVC with the Polygon node application.

In summary, these PVs and PVCs are essential for Polygon node's data storage needs. They ensure that Heimdall and Bor have access to the required storage capacity while maintaining data integrity and persistence, which are crucial for the reliable operation of our blockchain ecosystem.


# control pane cluster

**Step 1: Prepare the System**

* Configure kernel modules by creating a configuration file:

```yaml
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

```

Load the required kernel modules:

```
sudo modprobe overlay
sudo modprobe br_netfilter
sudo sysctl --system

```

**Step 2: Install Container Runtimes**

* Add Docker's official GPG key:

```
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

```

Add the Docker repository to Apt sources:

```
echo \
  "deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  \$(. /etc/os-release && echo "\$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

```

Install containerd.io:

```
sudo apt-get install containerd.io

```

**Step 3: Install Container Networking Plugins**

* Download and install CNI plugins:

```
wget https://github.com/containernetworking/plugins/releases/download/v1.3.0/cni-plugins-linux-amd64-v1.3.0.tgz
mkdir -p /opt/cni/bin
tar -C /opt/cni/bin -xzvf cni-plugins-linux-amd64-v1.3.0.tgz

```

Edit the containerd configuration:

```
nano /etc/containerd/config.toml

```

Add the following configuration under

```
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
```

> You need CRI support enabled to use containerd with Kubernetes. Make sure that `cri` is not included in the`disabled_plugins` list within `/etc/containerd/config.toml`;

Restart containerd:

```
sudo systemctl restart containerd

```

**Step 4: Install Kubernetes Tools**

* Install necessary tools and set up the Kubernetes repository:

```
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl

sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg

echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update

```

List available versions of kubeadm:

```
sudo apt list -a kubeadm
```

Install kubeadm, kubelet, and kubectl (replace 1.28.0 with your desired version):

```
sudo apt-get install kubeadm=1.28.0-00
sudo apt-get install kubelet=1.28.0-00
sudo apt-get install kubectl=1.28.0-00

```

**Step 5: Initialize Kubernetes Control Plane**

* Pull the required container images:

```
sudo kubeadm config images pull --kubernetes-version=1.28.0

```

Initialize the Kubernetes control plane (replace `1.28.0` with your desired version):

```
sudo kubeadm init --kubernetes-version=1.28.0

```

Follow these steps carefully to set up your Kubernetes control plane successfully. Make sure to adjust the versions and configurations as needed for your specific requirements. At the end of the steps, your instance must be configured.

<figure><img src="/files/lIG6T6iUO8aNEBqM3iv0" alt=""><figcaption></figcaption></figure>

To start the cluster you need the following steps:

> Run the mentioned commands with `root` user

```
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf SHOME/.kube/config
sudo chown $(id -u):$ (id -g) $HOME/.kube/config
```

Now we need to install the network plugin in Kubernetes.

Can be installed onto your CNI-enabled Kubernetes cluster with a single command:

```
sudo kubectl apply -f https://github.com/weaveworks/weave/releases/download/v2.8.1/weave-daemonset-k8s.yaml
```

After completing this process, you now have your control plane cluster installed and configured.

To test whether it is working as expected, you can check the node status.

```
sudo kubectl get nodes -o wide
```

<figure><img src="/files/nuVNU4QCZWYPyYqMxNxZ" alt=""><figcaption></figcaption></figure>


# worker cluster

The worker cluster configuration steps are very similar to the control-panel cluster.

**Step 1: Prepare the System**

* Configure kernel modules by creating a configuration file:

```yaml
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

```

Load the required kernel modules:

```
sudo modprobe overlay
sudo modprobe br_netfilter
sudo sysctl --system

```

**Step 2: Install Container Runtimes**

* Add Docker's official GPG key:

```
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

```

Add the Docker repository to Apt sources:

```
echo \
  "deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  \$(. /etc/os-release && echo "\$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

```

Install containerd.io:

```
sudo apt-get install containerd.io

```

**Step 3: Install Container Networking Plugins**

* Download and install CNI plugins:

```
wget https://github.com/containernetworking/plugins/releases/download/v1.3.0/cni-plugins-linux-amd64-v1.3.0.tgz
mkdir -p /opt/cni/bin
tar -C /opt/cni/bin -xzvf cni-plugins-linux-amd64-v1.3.0.tgz

```

Edit the containerd configuration:

```
nano /etc/containerd/config.toml

```

Add the following configuration under

```
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
```

> You need CRI support enabled to use containerd with Kubernetes. Make sure that `cri` is not included in the`disabled_plugins` list within `/etc/containerd/config.toml`;

Restart containerd:

```
sudo systemctl restart containerd

```

**Step 4: Install Kubernetes Tools**

* Install necessary tools and set up the Kubernetes repository:

```
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl

sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg

echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update

```

List available versions of kubeadm:

```
sudo apt list -a kubeadm
```

Install kubeadm, kubelet, and kubectl (replace 1.28.0 with your desired version):

```
sudo apt-get install kubeadm=1.28.0-00
sudo apt-get install kubelet=1.28.0-00
```

After completing this process, you now have your worker cluster installed.

**Connecting cluster worker with cluster control-plane:**

* Access your control pane cluster and create the token to connect your worker cluster.

```
sudo kubeadm token create --print-join-command
```

> **A token will be generated see below for an example**
>
> kubeadm join 111.231.252.43:6443 --token 17htao.wo5w0fn1rpg21e9m --discovery-token-ca-cert-hash sha256:ea9a3611d926cf9a7cfadb089a0d47500f89bab1g5ef45310b9a8d82a65c48b

Now just copy the generated token into your cluster worker and the connection will be established. Once this is done, when consulting your node you will see the connection successfully.

<figure><img src="/files/slt1jlNDz1islPihroqP" alt=""><figcaption></figcaption></figure>


# Run a node

In this step, we will install the Polygon node. However, before proceeding with the installation, please ensure that you are using the Kubernetes architecture.

**Before starting the installation, make sure to verify that the worker cluster is active. You can do this by checking the status of your node.**

```
sudo kubectl get nodes
```

Now that the entire environment is up and running, all that's left is to save the [files](/polygon/kubernetes/files) with a .yaml extension and execute them.\\

```
sudo kuebctl apply -f deploy-storage.yaml
sudo kubectl apply -f deploy.yaml
```

**To check whether the polygon node is running, you can check the status of the pod.**

```
sudo kubectl get deploy -o wide
```

<figure><img src="/files/3ywZTY5EnHeAf1S7lMyl" alt=""><figcaption></figcaption></figure>

To check the logs you can pass the name of the pod plus the name of the container you want to view, example:

```
sudo kubectl logs -f polygon-sentry-deployment-cc9449857-9f95k -c heimdall
```

<figure><img src="/files/g4d5bGshuspQLJL70p9O" alt=""><figcaption></figcaption></figure>

<pre><code><strong>sudo kubectl logs -f polygon-sentry-deployment-cc9449857-9f95k -c bor
</strong></code></pre>

<figure><img src="/files/2W3SxbRvpqQxrLBECNJ9" alt=""><figcaption></figcaption></figure>


# Files

deploy-sentry.yaml

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: polygon-sentry-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: Polygon-Sentry
  template:
    metadata:
      labels:
        app: Polygon-Sentry
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: environment
                operator: In
                values:
                - sentry
      tolerations:
      - key: "node-role.kubernetes.io/control-plane"
        operator: "Exists"
        effect: "NoSchedule"
      containers:
        - name: heimdall
          image: 0xpolygon/heimdall:1.0.4-beta
          command:
            - heimdalld
            - start
          args:
            - --chain=mumbai
            - --rest-server
            - --home=/var/lib/heimdall
          ports:
            - containerPort: 26656
          volumeMounts:
            - name: heimdall-config-volume
              mountPath: /var/lib/heimdall
        - name: bor
          image: 0xpolygon/bor:1.2.6-beta
          command:
            - bor
            - server
            - --config=/var/lib/bor/config.toml
          ports:
            - containerPort: 30303
          volumeMounts:
            - name: bor-config-volume
              mountPath: /var/lib/bor
        - name: rabbitmq
          image: rabbitmq:3.8
          ports:
            - containerPort: 5672
            - containerPort: 15672
      volumes:
        - name: heimdall-config-volume
          persistentVolumeClaim:
            claimName: heimdall-pvc-sentry
        - name: bor-config-volume
          persistentVolumeClaim:
            claimName: bor-pvc-sentry

```

deploy-storage.yaml

```yaml
# PV e PVC para Heimdall
apiVersion: v1
kind: PersistentVolume
metadata:
  name: heimdall-pv-sentry
  labels:
    app: Polygon-Sentry
spec:
  capacity:
    storage: 150Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: standard
  hostPath:
    path: /var/lib/heimdall

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: heimdall-pvc-sentry
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 150Gi
  storageClassName: standard
  selector:
    matchLabels:
      app: Polygon-Sentry

---
# PV e PVC para Bor
apiVersion: v1
kind: PersistentVolume
metadata:
  name: bor-pv-sentry
  labels:
    app: Polygon-Sentry
spec:
  capacity:
    storage: 600Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: standard
  hostPath:
    path: /var/lib/bor

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: bor-pvc-sentry
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 600Gi
  storageClassName: standard
  selector:
    matchLabels:
      app: Polygon-Sentry

```

deploy-validator.yaml

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: polygon-validator-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: Polygon-validator
  template:
    metadata:
      labels:
        app: Polygon-validator
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: environment
                operator: In
                values:
                - validator
      containers:
        - name: heimdall
          image: 0xpolygon/heimdall:1.0.4-beta
          command:
            - heimdalld
            - start
          args:
            - --chain=mumbai
            - --rest-server
            - --home=/var/lib/heimdall
            - --bridge
            - --all 
          ports:
            - containerPort: 26656
          volumeMounts:
            - name: heimdall-config-volume
              mountPath: /var/lib/heimdall
        - name: bor
          image: 0xpolygon/bor:1.2.6-beta
          command:
            - bor
            - server
            - --config=/var/lib/bor/config.toml
          volumeMounts:
            - name: bor-config-volume
              mountPath: /var/lib/bor
        - name: rabbitmq
          image: rabbitmq:3.8
          ports:
            - containerPort: 5672
            - containerPort: 15672
      volumes:
        - name: heimdall-config-volume
          persistentVolumeClaim:
            claimName: heimdall-pvc-validator
        - name: bor-config-volume
          persistentVolumeClaim:
            claimName: bor-pvc-validator

```

storage-validator.yaml

```yaml
# PV e PVC para Heimdall
apiVersion: v1
kind: PersistentVolume
metadata:
  name: heimdall-pv-validator
  labels:
    app: Polygon-validator
spec:
  capacity:
    storage: 150Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: standard
  hostPath:
    path: /var/lib/heimdall

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: heimdall-pvc-validator
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 150Gi
  storageClassName: standard
  selector:
    matchLabels:
      app: Polygon-validator

---
# PV e PVC para Bor
apiVersion: v1
kind: PersistentVolume
metadata:
  name: bor-pv-validator
  labels:
    app: Polygon-validator
spec:
  capacity:
    storage: 600Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: standard
  hostPath:
    path: /var/lib/bor

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: bor-pvc-validator
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 600Gi
  storageClassName: standard
  selector:
    matchLabels:
      app: Polygon-validator

```


# Access the Kubernetes Dashboard

## Deploy and Access the Kubernetes Dashboard

Dashboard is a web-based Kubernetes user interface. Provides information on the state of Kubernetes resources in your cluster and on any errors that may have occurred.

<figure><img src="/files/0Zad6qKAwEFyAHShGe5v" alt=""><figcaption></figcaption></figure>

### Deploying the Dashboard UI <a href="#deploying-the-dashboard-ui" id="deploying-the-dashboard-ui"></a>

The Dashboard UI is not deployed by default. To deploy it, run the following command:

```
sudo kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml
```

After completing the installation, your pod must be running successfully.\\

<figure><img src="/files/gH9mhquZ2QtsvsWP0pd3" alt=""><figcaption></figcaption></figure>

Now, to be able to expose the dashboard for external access, we need to change the service's ClusterIP type to NodePort.

<figure><img src="/files/oCkjtNKLiWkYIdkCBVdQ" alt=""><figcaption></figcaption></figure>

We can make this change simply by editing the service.

```
sudo kubectl edit service kubernetes
```

<figure><img src="/files/SkBMyGnQ3qfWTVzkeNsl" alt=""><figcaption></figcaption></figure>

After making the change to `NodePort`, save and close the file.

When performing a new query, notice that the type has changed and now a new port has been added.

<figure><img src="/files/zDKByEkeuRQenctII3KV" alt=""><figcaption></figcaption></figure>

Once this is done, you should have access to the dashboard through the public IP of your instance plus the port that was released.

<figure><img src="/files/WVEMW3F9tijJU5UcG8g3" alt=""><figcaption></figcaption></figure>

As we send an http request and expect https then we receive an alert. We can simply move forward by adding https\://\
\\

<figure><img src="/files/Gm7lsSnVsxQUQb3Y91IW" alt=""><figcaption></figcaption></figure>

### Accessing the Dashboard UI <a href="#accessing-the-dashboard-ui" id="accessing-the-dashboard-ui"></a>

Creating sample user:\
Grant this user admin permissions and login to Dashboard using a bearer token tied to this user.

### Creating a Service Account

```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kubernetes-dashboard
```

### Creating a ClusterRoleBinding

We can use it and create only a `ClusterRoleBinding` for our `ServiceAccount`

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kubernetes-dashboard
```

### Getting a Bearer Token for ServiceAccount

Execute the following command:

```
sudo kubectl -n kubernetes-dashboard create token admin-user
```

### Accessing Dashboard

<figure><img src="/files/m67zKh6hETUnDLnkvaax" alt=""><figcaption></figcaption></figure>

Now we can navigate the dashboard and monitor all clusters.

<figure><img src="/files/2TRgWdSWhAIEZcFJ8YuY" alt=""><figcaption></figcaption></figure>


# Access the Kubernetes Dashboard Grafana

### Prerequisites

* Kubernetes 1.19+
* Helm 3+

### Get Helm Repository Info

```
sudo helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
sudo helm repo update
```

### Install Helm Chart

<pre><code><strong>sudo helm install [RELEASE_NAME] prometheus-community/kube-prometheus-stack
</strong></code></pre>

<figure><img src="/files/rHnbu5t39pSZnib4jd0p" alt=""><figcaption></figcaption></figure>

Once this is done, you can verify if the deployment is running.

<figure><img src="/files/bEmDkgWiC8CkiAT7iAYc" alt=""><figcaption></figcaption></figure>

Now, to expose the dashboard for external access, you need to change the service's ClusterIP type to NodePort. You can do this with an ingress or by editing the file:

```
sudo kubectl edit service grafana
```

<figure><img src="/files/RyMPIKI8hOjniUj4bkk9" alt=""><figcaption></figcaption></figure>

After making the change to `NodePort`, save and close the file. When performing a new query, you'll notice that the type has changed, and a new port has been added.

<figure><img src="/files/Of1AOTwqPHXycUq6KZsE" alt=""><figcaption></figcaption></figure>

Once this is done, you should have access to the dashboard through the public IP of your instance, along with the released port.

<figure><img src="/files/kXzUdA3TjObWBfWVnj9X" alt=""><figcaption></figcaption></figure>

Default credential:\
user: `admin`\
pass: `prom-operator`

When you access the dashboard, you will find it ready for monitoring.

<figure><img src="/files/WQ2G8nJB2TbTGa6SaYKE" alt=""><figcaption></figcaption></figure>

When installing Grafana, several default dashboards are included. You can customize them or create your own dashboard as needed.


# Monitoring logs

## Centralized Logs with Loki and Promtail

Loki install

#### 1 - Download loki

```
wget https://github.com/grafana/loki/releases/download/v2.8.0/loki-linux-amd64.zip
```

#### 2 - Download Loki example configuration file

```
wget https://raw.githubusercontent.com/grafana/loki/master/cmd/loki/loki-local-config.yaml
```

#### 3 - Create directory for Loki application

```
sudo mkdir /usr/local/bin/loki
```

#### 4 - Unzip loki

```
unzip loki-linux-amd64.zip
```

#### 5 - Copy files to Loki application directory

```
sudo cp loki-linux-amd64 /usr/local/bin/loki
sudo cp loki-local-config.yaml /usr/local/bin/loki
```

#### 6 - Create service file

```
sudo vi /etc/systemd/system/loki.service
```

#### Put this content in the file and save

```
[Unit]
Description=Loki Service
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/loki/loki-linux-amd64 -config.file=/usr/local/bin/loki/loki-local-config.yaml

[Install]
WantedBy=multi-user.target
```

#### 7 - Reload daemons

```
sudo systemctl daemon-reload
```

#### 8 - Execute the loki

```
sudo systemctl start loki
```

#### 9 - Checking if loki is really running

```
sudo systemctl status loki
```

#### 10 - Enabling service in boot

```
sudo systemctl enable loki
```

Promtail install

#### 1 - Download Promtail

```
wget https://github.com/grafana/loki/releases/download/v2.4.2/promtail-linux-amd64.zip
```

#### 2 - Download Promtail example configuration file

```
wget https://raw.githubusercontent.com/grafana/loki/main/clients/cmd/promtail/promtail-local-config.yaml
```

#### 3 - Create directory for Promtail application

```
sudo mkdir /usr/local/bin/promtail
```

#### 4 - Unzip promtail

```
unzip promtail-linux-amd64.zip
```

#### 5 - Copy files to Promtail application directory

```
sudo cp promtail-linux-amd64 /usr/local/bin/promtail
sudo cp promtail-local-config.yaml /usr/local/bin/promtail
```

#### 6 - Configuring the Promtail configuration file

Open file

```
sudo nano /usr/local/bin/promtail/promtail-local-config.yaml
```

Set the address of the Loki Server that will receive the Log stream, and the path of the files that will be sent.

```
server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://localhost:3100/loki/api/v1/push

scrape_configs:
- job_name: system
  static_configs:
  - targets:
      - localhost
    labels:
      job: <LABEL NAME OF YOUR JOB>
      __path__: <PATH OF YOUR LOG FILE>
```

#### 7 - Create service file

```
sudo vi /etc/systemd/system/promtail.service
```

Put this content in the file and save

```
[Unit]
Description=Promtail Service
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/promtail/promtail-linux-amd64 -config.file=/usr/local/bin/promtail/promtail-local-config.yaml

[Install]
WantedBy=multi-user.target
```

#### 8 - Reload daemons

```
sudo systemctl daemon-reload
```

#### 9 - Execute the promtail

```
sudo systemctl start promtail
```

#### 10 - Checking if promtail is really running

```
sudo systemctl status promtail
```

#### 11 - Enabling service in boot

```
sudo systemctl enable promtail
```

### Dashboards Loki query based

1 - In "Dashboards" menu click in "Browse"

<figure><img src="/files/00ae8nRLtxi5ctpMDxi6" alt=""><figcaption></figcaption></figure>

2 - Click in "New Dashboard" button or click on an existing dashboard

<figure><img src="/files/RZSlF4JYrLyQ5ZT3r7yJ" alt=""><figcaption></figcaption></figure>

3 - Click in "Add new panel" button

<figure><img src="/files/MyPUIP8T4s7BkPnVeGd8" alt=""><figcaption></figcaption></figure>

4 - Select Loki datasource

<figure><img src="/files/stEReZf81YPEd0Aj4g70" alt=""><figcaption></figcaption></figure>

5 - Define the content filter in Loki query

<figure><img src="/files/feq0E2J5jdAZ1RZwQ05r" alt=""><figcaption></figcaption></figure>

6 - Define title and description of panel and click in save button

<figure><img src="/files/YIADeeP3btppKfnpSRVU" alt=""><figcaption></figcaption></figure>


# Erigon


# Run a Node

#### Setting Up Your Node:[​](https://docs.celestia.org/nodes/bridge-validator-node#setting-up-your-bridge-node) <a href="#setting-up-your-bridge-node" id="setting-up-your-bridge-node"></a>

The following tutorial is done on an Ubuntu Linux 20.04 (LTS) x64 instance machine.

#### Node Installation:

This guide shows how to install and run an Polygon full node.

#### How to install Polygon from source:

* Before you start, you might want to ensure your system is updated. You can also install a utility named `jq` to read and navigate in JSON files and outputs (other utilities could be installed too).

  ```
  sudo apt update && sudo apt upgrade -y && sudo apt install -y build-essential curl wget jq
  ```

To install **Heimdall**, run the below commands:

```
curl -L https://raw.githubusercontent.com/maticnetwork/install/main/heimdall.sh | bash -s -- <heimdall_version> <network_type> <node_type>
```

That will install the `heimdalld` and `heimdallcli` binaries. Verify the installation by checking the Heimdall version on your machine:

```
heimdalld version --long
```

**Open the P2P port (26656 by default)**

```
sudo ufw allow 26656
```

### To install **Erigon**, run the below commands:

```
git clone https://github.com/0xPolygon/erigon.git
cd erigon
git checkout <latest-stable>
make erigon
```

That will install the `Erigon` binary. Verify the installation by checking the Erigon version on your machine:

```
erigon version
```

**Open the port (30303 by default)**

```
sudo ufw allow 30303
```

### Create a new erigon service file

```
sudo nano /lib/systemd/system/erigon.service
```

```
[Unit]
Description=erigon
StartLimitIntervalSec=500
StartLimitBurst=5
[Service]
Restart=on-failure
RestartSec=5s
#WorkingDirectory=$HOME
ExecStart=/bin/bash /home/node/erigon-start.sh
Type=simple
User=node
KillSignal=SIGINT
TimeoutStopSec=120
[Install]
WantedBy=multi-user.target

```

### The configuration below assumes that you are running it on amoy

```
sudo nano config.yaml
```

```
chain: "amoy"
bor.heimdall: "http://127.0.0.1:1317"
datadir: "/var/lib/erigon/data"
#ethstats: ""
http.api: [ "eth,net,web3,erigon,txpool,bor" ]
http.port: 8545
bodies.cache: 21474836480
db.pagesize: "16kb"
db.size.limit: "32TB"
#mine: true
miner.gaslimit: 30000000
#miner.etherbase: ""
#miner.sigfile: ""
#miner.recommit: "125s"
txpool.accountslots: 16
txpool.globalslots: 32768
txpool.globalqueue: 32768
maxpeers: 50
port: 30303
#bootnodes: [ ""]
#trustedpeers: [ "" ]

```

### Run erigon using configs provided in the config.toml

```
sudo nano erigon-start.sh
```

```
#!/usr/bin/env sh
set -x #echo on
/home/node/erigon/build/bin/erigon --config=/home/node/config.yaml

```

### Once the has been configured, we are ready to start the node and sync the data:

**Start Heimdall Service.**

```
sudo service heimdalld start
```

* Check logs in real tim&#x65;**.**

```
journalctl -u heimdalld.service -f
```

* To verify that the heimdall node is synced before proceeding.

```
curl -s localhost:26657/status | jq .result | jq .sync_info
```

Make sure that you have <mark style="color:orange;">`"catching_up": false`</mark>, otherwise leave it running until it is in sync.

Now once Heimdall is synced, run.

#### Start Erigon.

```
sudo service erigon start
```

* You can check Erigon logs here:

```
journalctl -u erigon.service -f
```


# Snapshots


# Polygon

S*napshots are taken automatically every 1 week.*

<table><thead><tr><th width="164">Node</th><th width="183">Type</th><th>Type</th><th width="205.3828125">Mode</th><th width="231">Age</th><th data-hidden></th><th data-hidden></th></tr></thead><tbody><tr><td>h<strong>eimdall</strong></td><td>mainnet</td><td>amoy</td><td>full / pruned</td><td>26-08-09</td><td></td><td></td></tr><tr><td>bor</td><td>mainnet</td><td>amoy</td><td>archive / full / stateless</td><td>26-08-09</td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr></tbody></table>

Install zstd if needed

```bash
sudo apt install zstd
```

Download the file and extract

```bash
curl -L https://raw.githubusercontent.com/Stakepool-node/snapshot/refs/heads/main/snapshot.sh | bash -s -- <node> <type> <mode>
```

Download manual:

If you prefer to download files directly using the raw URLs.\
\
List all available snapshots:

```bash
curl -s https://snap.stakepool.work/snapshots-stakepool/list_snapshots.txt | \
awk '{print "https://snap.stakepool.work/snapshots-stakepool/" $4}'
```

Example direct download:

```bash
aria2c -x8 -s8 -c "URL"
```

or:

```bash
wget -c  "URL"
```


# Scripts

Scripts can be written in various programming languages, and the choice of language often depends on the task you want to accomplish. Some common examples of scripting languages include Python, JavaScript, Bash, PowerShell, Ruby, among others.

\\


# Block Hash

This Bash script is designed to compare the hash of a specific block from the Heimdall blockchain on your local instance with the hash of the same block obtained from an external API.

```bash
#!/bin/bash


latest_block_height=$(curl -s localhost:26657/status | jq -r '.result.sync_info.latest_block_height')


hash_value_local=$(curl -s "localhost:26657/block?height=$latest_block_height" | jq -r '.result.block_meta.block_id.hash')


hash_value_api=$(curl -s "https://heimdall-api.polygon.technology/blocks/$latest_block_height" | jq -r '.block_meta.block_id.hash')


echo "Hash of the block (Local): $hash_value_local"
echo "Hash of the block (API): $hash_value_api"

if [ "$hash_value_local" = "$hash_value_api" ]; then
    echo "The hashes are equal."
else
    echo "The hashes are different."
fi

```

Requests Installation:

Run the script in an environment with `curl` and `jq` installed.

```bash
sudo apt-get install jq 
```

Script Execution:

Make the script executable using `chmod +x script.sh`

Run the Script:

In the terminal or command prompt, navigate to the directory where the script is located. Execute the script using the following command:

```bash
./script.sh
```


# Bor Status

This script is a Node.js application that monitors the synchronization status of a local Bor node.

```bash
var http = require('http');
var url = require('url');
const fs = require('fs');

let jsonToReturn = {};
let lastBlock = 0;
let syncedBlocks = 0;
let blocksToLive = 0;
let status = { "logs": [] };

function getLocalData() {
    var post_data = '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params": ["latest",false],"id":1}';
    var post_options = {
        host: 'localhost',
        port: '8545',
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        }
    };

    var post_req = http.request(post_options, function (res) {
        var body = '';

        res.setEncoding('utf8');

        
        res.on('readable', function () {
            var chunk = this.read() || '';

            body += chunk;
        });

        res.on('end', function () {
            let latestBlock = JSON.parse(body);
            let latestBlockNumber = parseInt(latestBlock.result.number);
            let latestBlockTimestamp = parseInt(latestBlock.result.timestamp);
            let currentTimestamp = Date.now() / 1000;
            let difference = currentTimestamp - latestBlockTimestamp;
            let humanCurrentTimestamp = new Date(currentTimestamp * 1000).toLocaleString();
            let humanLatestBlockTimestamp = new Date(latestBlockTimestamp * 1000).toLocaleString();
            let synced = false;

            
            let newLog = {};

            if (difference > 120) {
                synced = false;
                newLog = {
                    "synced": false,
                    "timestamp": currentTimestamp,
                    "dateTime": humanCurrentTimestamp,
                    "message": "Block #" + latestBlockNumber + " is " + (difference / 60) + " minutes late (" + humanCurrentTimestamp + ") compared to its original timestamp (" + humanLatestBlockTimestamp + ")"
                };
            } else {
                synced = true;
                newLog = {
                    "synced": true,
                    "timestamp": currentTimestamp,
                    "dateTime": humanCurrentTimestamp,
                    "message": "Block #" + latestBlockNumber + " is on time. Block original timestamp is " + humanLatestBlockTimestamp + " (" + (difference / 60) + " minutes from now)"
                };
            }

            
            status.logs.push(newLog);
            status = {
                "synced": synced,
                "timestamp": currentTimestamp,
                "humanCurrentTimstamp": humanCurrentTimestamp,
                "lastBlock": latestBlockNumber,
                "syncedBlocks": latestBlockNumber - lastBlock,
                "logs": status.logs
            };
            lastBlock = latestBlockNumber;

            
            if (status.logs.length > 10) {
                status.logs.shift();
            }

            console.log("Latest block number : " + latestBlockNumber);
            console.log("Latest block timestamp : " + latestBlockTimestamp);
            console.log("Current timestamp : " + currentTimestamp);
            console.log("Current difference : " + difference);
            console.log(humanCurrentTimestamp);
            console.log(humanLatestBlockTimestamp);
            console.log(status);

            fs.appendFile('message.txt', JSON.stringify(status.logs) + "\r\n", function (err) {
                if (err) throw err;
            });
        });

        post_req.on('error', function (e) {
            console.log("error" + e.message);
        });
    });
    
    post_req.write(post_data);
    post_req.end();
    setTimeout(function () {
        getLocalData();
    }, 60000)
}

getLocalData();

var port = 9615;
http.createServer(function (request, response) {
    try {
        let logsLine = []
        let fileContentsTable = ""
        const allFileContents = fs.readFileSync('message.txt', 'utf-8');
        allFileContents.split(/\r?\n/).forEach(line => {
            try {
                let liness = JSON.parse(line);
                fileContentsTable += '<tr style="background-color: ' + (liness.synced ? '#93f5b0' : '#f59393') + ';"><td style="background-color: ' + (liness.synced ? '#93f5b0' : '#f59393') + ';">Synced : ' + liness.synced + '</td><td style="background-color: ' + (liness.synced ? '#93f5b0' : '#f59393') + ';">' + liness.dateTime + " - " + liness.message + "</td></tr>"
            } catch (e) {
                console.log("Error");
            }
        });

        var requestUrl = url.parse(request.url);
        response.writeHead(200);
        let html = `<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Status Page</title>
    <style>
        body {
            font-family: Arial, Helvetica, sans-serif;
            margin: 0;
            padding: 0;
            background-color: 
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }

        .header {
            text-align: center;
            margin-bottom: 20px;
        }

        /* Restante do estilo CSS... */

    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>Status Dashboard</h1>
        </div>
        <div class="card">
            <h2>Status</h2>
            <p class="synced-${status.synced ? 'true' : 'false'}">${status.synced ? 'Synced' : 'Not Synced'}</p>
        </div>
        <div class="card">
            <h2>Block Information</h2>
            <p>Last Block: ${status.lastBlock}</p>
            <p>Timestamp: ${status.humanCurrentTimstamp}</p>
            <p>Synced Blocks: ${status.syncedBlocks}</p>
        </div>
        <div class="card">
            <h2>Logs</h2>
            ${status.logs.map(log => `
            <div class="synced-${log.synced ? 'true' : 'false'}">
                <p>${log.synced ? 'Synced' : 'Not Synced'} - ${log.dateTime}</p>
                <p>${log.message}</p>
            </div>
            `).join('')}
        </div>
    </div>
</body>
</html>`;

        jsonToReturn.syncedBlocks = status.syncedBlocks;
        jsonToReturn.humanCurrentTimstamp = status.humanCurrentTimstamp;
        jsonToReturn.fileContents = logsLine;
        jsonToReturn.timestamp = status.timestamp;

        response.end(html);
    } catch (e) {
        response.writeHead(500);
        response.end(); 
        console.log(e.stack);
    }
}).listen(port);

console.log("listening on port " + port);

```

Make sure you have Node.js installed on your system. Open the script in a text editor and adjust the initial settings if necessary, such as the server port, polling interval, and the location of the Bor node.

Execute the following command to install the necessary dependencies:

```bash
npm install
```

In the same terminal, execute the following command to start the script:

```bash
node <name-script>.js
```


# Checkpoint Notifier

This script is useful for checking validator activity, notifying you whenever the validator misses a checkpoint.

```bash
import requests
import time


api_url = "https://monitor.vn.stakepool.dev.br/missing_signers"

my_validator_ids = input("Enter the validator IDs to monitor (comma-separated): ").split(",")

my_validator_ids = [validator_id.strip() for validator_id in my_validator_ids]

while True:
    try:
        response = requests.get(api_url)

        
        if response.status_code == 200:
            data = response.json()
  
            missed_checkpoints = {validator_id: False for validator_id in my_validator_ids}

            
            for item in data:
                validator_id = str(item.get("ID"))  
                validator_signer = item.get("signer")  

               
                if validator_id in my_validator_ids:
                    print(f"Validator {validator_id} ({validator_signer}) missed a checkpoint!")
                    missed_checkpoints[validator_id] = True

            
            for validator_id, missed in missed_checkpoints.items():
                if not missed:
                    print(f"Validator {validator_id} has not missed checkpoints.")

        else:
            print("Request error:", response.status_code)

    except Exception as e:
        print("Error:", e)

    
    time.sleep(60)

```

Python and Requests Installation:

Make sure you have Python installed on your system. Open the terminal or command prompt. Execute the following command to install the `requests` library:

```bash
pip install requests
```

Script Execution:

Save the Python script (`Your_script.py`) in the directory of your choice. Place a file named "addresses.txt" in the same directory as the script, containing the addresses of the validators.

Run the Script:

In the terminal or command prompt, navigate to the directory where the script is located. Execute the script using the following command:

```bash
python3 your_script.py
```

Results:

The script will begin monitoring the validator's activity and display messages whenever the validator signs a new checkpoint. You can stop the script at any time by pressing Ctrl + C in the terminal.


# Contract Notifier

This script is useful for monitoring the creation of contracts on the Polygon network and promptly notifying users through Discord when a new contract is detected.

```yaml
from web3 import Web3
from web3.middleware import geth_poa_middleware
import time
import discord

# Set the URL of the Polygon (or Matic) network node
polygon_node_url = "" # INSERT_YOUR_RPC_HERE

# Set the ERC20 contract address for token creation
erc20_contract_address = "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0"

# Set the Discord bot token
discord_bot_token = "" # INSERT_YOUR_WEBHOOK_HERE

# Set the channel ID where notifications will be sent
discord_channel_id = ""  # INSERT_YOUR_CHANNEL_ID_HERE

# Initialize the Web3 object
web3 = Web3(Web3.HTTPProvider(polygon_node_url))

web3.middleware_onion.inject(geth_poa_middleware, layer=0)

def get_contract_creation_events(contract_address):
    event_signature = web3.keccak(text="Transfer(address,address,uint256)").hex()

    checksum_address = Web3.toChecksumAddress(contract_address)
    filter_params = {
        "address": checksum_address,
        "topics": [event_signature, None, None],
    }
    contract_creation_events = web3.eth.get_logs(filter_params)
    return contract_creation_events

def bytes_to_hex_string(byte_array):
    return byte_array.hex()

def get_latest_block_number():
    # Get the latest block number
    latest_block_number = web3.eth.blockNumber
    return latest_block_number

def send_discord_notification(message):
    # Initialize the Discord 
    client = discord.Client()

    @client.event
    async def on_ready():
        channel = client.get_channel(discord_channel_id)
        await channel.send(message)
        await client.close()

    client.run(discord_bot_token)

def main():
    try:
        # Check the connection to the Polygon network node
        if not web3.isConnected():
            print("Error: Could not connect to the Polygon network node.")
            return

        while True:
            contract_creation_events = get_contract_creation_events(erc20_contract_address)

            if contract_creation_events:
               
                transaction_hashes_bytes = [event["transactionHash"] for event in contract_creation_events]
           
                transaction_hashes_strings = [bytes_to_hex_string(txn_hash) for txn_hash in transaction_hashes_bytes]

                # Send Discord notification
                for txn_hash_string in transaction_hashes_strings:
                    message = f"Contract created in transaction: {txn_hash_string}"
                    print(message)
                    send_discord_notification(message)

            # interval
            time.sleep(1)

    except Exception as e:
        print(f"Error: {str(e)}")

if __name__ == "__main__":
    main()

```

Python and Requests Installation:

Make sure you have Python installed on your system. Open the terminal or command prompt. Execute the following command to install the `requests` library:

```bash
pip install requests
```

Script Execution:

Save the Python script (`Your_script.py`) in the directory of your choice. Place a file named "addresses.txt" in the same directory as the script, containing the addresses of the validators.

Run the Script:

In the terminal or command prompt, navigate to the directory where the script is located. Execute the script using the following command:

```bash
python3 your_script.py
```

Results:

The script will begin monitoring the creation of contracts and send notifications to the configured Discord channel whenever a new contract is detected.


# Checkpoint with prometheus

## Monitoring Integration with Prometheus and Alert.

***

### Introduction

In this guide, we will set up a **script** to monitor data from an API. This script will expose metrics to **Prometheus**. We will also configure **alerts in Prometheus** to notify when a specific **signer** and **ID** are missing.

***

### 1: Installing Dependencies

You need to install the `requests` library to make the request to the API and `prometheus_client` to expose the metrics to Prometheus.

```bash
pip install requests prometheus_client
```

#### Step 1.1: Create the Monitoring Script

```bash
import requests
from prometheus_client import start_http_server, Gauge
import time

missing_signers_gauge = Gauge('missing_signers', 'Number of missing signers')

target_signer = "<YOUR-SIGNER>"
target_id = "<YOUR-IDVALIDATOR>"

def fetch_missing_signers():
    url = 'https://monitor.vn.stakepool.dev.br/missing_signers'
    try:
        response = requests.get(url)
        response.raise_for_status()  

       
        try:
            data = response.json()
            return data
        except ValueError:
            print(f"Error decoding JSON: Malformed response. Content: {response.text}")
            return []  

    except requests.exceptions.HTTPError as err:
        print(f"Error HTTP: {err}")
    except requests.exceptions.RequestException as err:
        print(f"Error request: {err}")

    return [] 

def update_metrics():
    missing_signers = fetch_missing_signers()
    
   
    if isinstance(missing_signers, list):
        target_found = any(
            signer['signer'] == target_signer and signer['ID'] == target_id
            for signer in missing_signers
        )

        if target_found:
            missing_signers_gauge.set(1)
        else:
            missing_signers_gauge.set(0)
    else:
        print("Invalid response format. Expected a list.")

if __name__ == "__main__":
    start_http_server(8000)
    while True:
        update_metrics()
        time.sleep(60)


```

### 2: Install and Configure Prometheus

#### Step 2.1: Configure Prometheus to Scrape Metrics

Edit the `prometheus.yml` file to include the scrape target for the Python script. Add the following configuration to `prometheus.yml`:

```bash
scrape_configs:
  - job_name: 'missing_signers'
    static_configs:
      - targets: ['localhost:8000']

```

### 3: Configure Prometheus Alerting Rule

Create a new file named `alert.rules` and define the alerting rule for when the specific **signer** and **ID** are missing:

```bash
groups:
  - name: missing_signer_alerts
    rules:
    - alert: MissingSignerAlert
      expr: missing_signers == 1
      for: 1m
      labels:
        severity: critical
    
```

#### Step 3.1: Update Prometheus Configuration to Include Alerts

Add the `alert.rules` file to the Prometheus. Update `prometheus.yml` to include the `rule_files` section:

```bash
rule_files:
  - "alert.rules"

```

3.2 Restart Prometheus

```bash
systemctl restart prometheus
```

### 4. Creating a New Panel

Follow these steps to create a panel in Grafana.

#### Steps:

1. On the Grafana dashboard, click the **"+"** icon in the left sidebar and select **"Dashboard"**.
2. Click on **"Add New Panel"**.

Defining the Query for the `missing_signers` Metrics

With the panel created, the next step is to define the query to display the `missing_signers` metric.

#### Steps:

In the **Query** section (just below "Metric"), select **Prometheus** as the data source.

In the query field, enter the following query:

```plaintext
missing_signers
```

<figure><img src="/files/4J9kcIvykyoHUMRbe87P" alt=""><figcaption></figcaption></figure>

1. In the Grafana panel, click the **Alert** icon (next to the panel title).
2. Configure the alert condition. For example, set it to trigger an alert when the value of `missing_signers` is equal to 1 (which means the specific signer is missing).


# Manage Bor

The script is designed for the maintenance and management of peers in a Bor network, disconnecting peers that do not meet certain version criteria, and blocking traffic from the IPs associated with those peers.

```bash
import subprocess
import re
import json
import os

def remove_peer(enode):
    
    command = f'bor attach /var/lib/bor/bor.ipc --exec "admin.removePeer(\'{enode}\')"'
    
    try:
        
        subprocess.run(command, shell=True, check=True)
        print(f"Peer removed: {enode}")
    except subprocess.CalledProcessError as e:
        print(f"Error removing peer {enode}: {e}")

def block_ip(ip_address):
    
    block_command = f'sudo iptables -A INPUT -s {ip_address} -j DROP'
    os.system(block_command)
    print(f"Traffic from IP {ip_address} blocked using iptables")

get_peers_command = 'bor attach /var/lib/bor/bor.ipc --exec "admin.peers"'

def version_compare(v1, v2):
    """
    Compare version strings v1 and v2.
    Returns True if v1 is less than v2.
    """
    return v1 < v2

try:
    result = subprocess.check_output(get_peers_command, shell=True, text=True)
    result = re.sub(r'([{,]\s*)([A-Za-z_][a-zA-Z_0-9]*)(\s*:)','\\1"\\2"\\3', result)

    peers = json.loads(result)

    for peer in peers:
        caps = peer.get('caps', [])
        name_version = peer.get('name', '').split('/')[1] if '/' in peer.get('name', '') else ''
        ip_match = re.search(r'@(\d+\.\d+\.\d+\.\d+):30303', peer['enode'])

        if "snap/1" not in caps or version_compare(name_version, 'v1'):
            remove_peer(peer['enode'])

            if ip_match:
                ip_address = ip_match.group(1)
                block_ip(ip_address)
        else:
            print(f"Healthy peer: {peer['enode']}")

except subprocess.CalledProcessError as e:
    print(f"Error getting the list of peers: {e}")

```

Python and Requests Installation:

Make sure you have Python installed on your system. Open the terminal or command prompt. Execute the following command to install the `requests` library:

```bash
pip install requests
```

Script Execution:

Save the Python script (`Your_script.py`) in the directory of your choice.

Run the Script:

In the terminal or command prompt, navigate to the directory where the script is located. Execute the script using the following command:

```bash
python3 your_script.py
```

Results:

Iterates over the list of peers obtained from the Geth console. For each peer, it checks whether it has the "snap/1" capability and if the name version is less than "v1". If the above conditions are not met, it removes the peer using `admin.removePeer` and blocks the traffic from the associated IP using iptables.\
**Note:** You can use cron to automate the periodic execution of the script.


# Peer Delivering

Gets information about peers in the Bor network using the admin.peers command in the Geth console. Reads logs from a specified file defined by log\_file\_path. Filters the logs to find lines containing the string "Peer delivering stale transactions." Extracts the IDs of peers from the filtered logs. Prints the extracted enodes and corresponding IDs. For each peer ID found in the logs, removes the corresponding peer using admin.removePeer in the Geth console.

```bash
import subprocess
import re

def get_enodes_from_peers_info(peers_info):
    
    enode_pattern = re.compile(r'enode: "([^"]+)",\s+id: "([^"]+)"')
    enodes = enode_pattern.findall(peers_info)
    return dict(enodes)

def get_peer_id_from_log(log):
    
    peer_id_pattern = re.compile(r'peer=([a-fA-F0-9]+)')
    match = peer_id_pattern.search(log)
    return match.group(1) if match else None

def print_peer_ids_from_logs(logs):
   
    unique_peer_ids = set()
    
    print("Peer IDs in logs:")
    for log in logs:
        peer_id = get_peer_id_from_log(log)
        if peer_id is not None and peer_id not in unique_peer_ids:
            unique_peer_ids.add(peer_id)
            print(f"  {peer_id}")
    return unique_peer_ids

command = "bor attach /var/lib/bor/bor.ipc --exec 'admin.peers'"

peers_info = subprocess.check_output(command, shell=True, text=True)

log_file_path = "/your-path/log"

with open(log_file_path, 'r') as log_file:
    
    filtered_logs = [line for line in log_file if "Peer delivering stale transactions" in line]

peer_ids_from_logs = print_peer_ids_from_logs(filtered_logs)

enodes_dict = get_enodes_from_peers_info(peers_info)
print("\nExtracted Enodes:")
for enode, peer_id in enodes_dict.items():
    print(f"  {enode}: {peer_id}")

for peer_id_from_log in peer_ids_from_logs:
    if peer_id_from_log in enodes_dict.values():
        enode_to_remove = [k for k, v in enodes_dict.items() if v == peer_id_from_log][0]
        print(f"\nRemoving enode: {enode_to_remove}")
        
        remove_command = f'bor attach /var/lib/bor/bor.ipc --exec \'admin.removePeer("{enode_to_remove}")\''
        subprocess.run(remove_command, shell=True, text=True)
        
        print(f"Enode removed: {enode_to_remove}")
    else:
        print(f"\nPeer ID {peer_id_from_log} not found in peers.")
```

Python and Requests Installation:

Make sure you have Python installed on your system. Open the terminal or command prompt. Execute the following command to install the `requests` library:

```bash
pip install requests
```

Script Execution:

Save the Python script (`Your_script.py`) in the directory of your choice.\
Run the Script:

In the terminal or command prompt, navigate to the directory where the script is located. Execute the script using the following command:

```bash
python3 your_script.py
```

Results:

The script is useful for identifying specific peers based on specific logs and then removing those peers from the Bor network. Make sure to adjust the log file path (log\_file\_path) to the correct location before running the script.\
**Note:** You can use cron to automate the periodic execution of the script


# Polygon Validator Checkpoint Information

This script fetches information about validators on the Polygon network and their corresponding checkpoints within a specified range. It highlights the latest checkpoints for each validator, displaying information such as checkpoint ID, timestamp, validator name, and total staked amount. To use it, you need to run the script in a Python environment and ensure that necessary libraries, such as `requests`, are installed. Also, make sure you have permission to access the APIs mentioned in the script.

```yaml
import requests
from datetime import datetime

# Desired start and end checkpoint IDs 
start_checkpoint_id = 54360
end_checkpoint_id = 54371


validator_api_url = "https://staking-api.polygon.technology/api/v2/validators?limit=0&offset=1"

last_checkpoints = {}

validator_response = requests.get(validator_api_url)

if validator_response.status_code == 200:
    validator_data = validator_response.json()
    
    proposer_info = {validator["signer"]: {"name": validator["name"], "totalStaked": validator["totalStaked"]} for validator in validator_data["result"]}

    for checkpoint_id in range(start_checkpoint_id, end_checkpoint_id + 1):
        
        api_url = f"https://heimdall-api.polygon.technology/checkpoints/{checkpoint_id}"

        response = requests.get(api_url)

        if response.status_code == 200:
            json_data = response.json()
            proposer = json_data["result"]["proposer"]

            if proposer in last_checkpoints:
                if last_checkpoints[proposer]["timestamp"] < json_data["result"]["timestamp"]:
                    last_checkpoints[proposer] = {
                        "id": json_data["result"]["id"],
                        "timestamp": json_data["result"]["timestamp"],
                        "name": proposer_info.get(proposer, {}).get("name", "Name not found"),
                        "totalStaked": proposer_info.get(proposer, {}).get("totalStaked", "TotalStaked not found"),
                    }
            else:
                last_checkpoints[proposer] = {
                    "id": json_data["result"]["id"],
                    "timestamp": json_data["result"]["timestamp"],
                    "name": proposer_info.get(proposer, {}).get("name", "Name not found"),
                    "totalStaked": proposer_info.get(proposer, {}).get("totalStaked", "TotalStaked not found"),
                }

# Print the final results
for proposer, info in last_checkpoints.items():
    checkpoint_id = info["id"]
    timestamp = info["timestamp"]
    name = info["name"]
    total_staked = int(float(info["totalStaked"]))

    
    datetime_obj = datetime.utcfromtimestamp(timestamp)
    formatted_date = datetime_obj.strftime('%Y-%m-%d %H:%M:%S UTC')

    
    formatted_total_staked = str(total_staked)[:-18]
    formatted_total_staked += " MATIC"

    
    print(f"Proposer: {proposer}")
    print(f"ID: {checkpoint_id}")
    print(f"Timestamp: {formatted_date}")
    print(f"Validator Name: {name}")
    print(f"Total Staked: {formatted_total_staked}")
    print("---")

```

Python and Requests Installation:

Make sure you have Python installed on your system. Open the terminal or command prompt. Execute the following command to install the `requests` library:

```bash
pip install requests
```

Script Execution:

Save the Python script (`Your_script.py`) in the directory of your choice. Place a file named "addresses.txt" in the same directory as the script, containing the addresses of the validators.

Run the Script:

In the terminal or command prompt, navigate to the directory where the script is located. Execute the script using the following command:

```bash
python3 your_script.py
```

Results:

The script will process the last checkpoint of each validator on a Polygon network (formerly Matic).


# Polygon Validator Participation Analyzer

The script checks the involvement of specific validator addresses on the Polygon network, identifying the blocks in which these addresses did not sign and keeping track of how many times each address acted as the proposer. The results are saved in a file named "results.txt."

```bash
import requests

def extract_validator_addresses(block, address):
    url = f"https://heimdall-api.polygon.technology/blocks/{block}"

    
    response = requests.get(url)

   
    if response.status_code == 200:
        
        data = response.json()

        
        if 'last_commit' in data['block']:
            
            proposer_address = data['block']['header'].get('proposer_address')
            precommits = data['block']['last_commit'].get('precommits', [])

            
            validator_addresses = [commit.get('validator_address') for commit in precommits if commit]

    
            if address in validator_addresses or address == proposer_address:
                return address, True, proposer_address
            else:
                return address, False, proposer_address
        else:
            print(f"No commit data in Block {block}.")
            return address, False, None
    else:
        
        print(f"Failed request for Block {block}. Response code: {response.status_code}")
        return address, False, None

initial_block = 16378652
final_block = 16379652

with open("addresses.txt", "r") as file:
    addresses_file = [line.strip() for line in file]


addresses_not_signing = []
addresses_proposer_count = {}

for block in range(initial_block, final_block + 1):
    for address in addresses_file:
        _, signed, proposer_address = extract_validator_addresses(block, address)
        if not signed:
            addresses_not_signing.append({"address": address, "block": block, "proposer_address": proposer_address})
          
            if proposer_address in addresses_proposer_count:
                addresses_proposer_count[proposer_address] += 1
            else:
                addresses_proposer_count[proposer_address] = 1


addresses_not_signing = sorted(addresses_not_signing, key=lambda x: x['block'])


with open("results.txt", "w") as result_file:
    result_file.write("Addresses not signing blocks, sorted by block:\n")
    for item in addresses_not_signing:
        result_file.write(f"Address: {item['address']}, Block: {item['block']}, Proposer Address: {item['proposer_address'] if item['proposer_address'] is not None else 'None'}\n")

    result_file.write("\nTotal times each address proposed:\n")
    for address, count in addresses_proposer_count.items():
        result_file.write(f"Address: {address}, Total Proposals: {count}\n")

print("Results saved in results.txt")
```

\
addresses.txt

Python and Requests Installation:

Make sure you have Python installed on your system. Open the terminal or command prompt. Execute the following command to install the `requests` library:

```bash
pip install requests
```

Script Execution:

Save the Python script (`Your_script.py`) in the directory of your choice. Place a file named "addresses.txt" in the same directory as the script, containing the addresses of the validators.

Run the Script:

In the terminal or command prompt, navigate to the directory where the script is located. Execute the script using the following command:

```bash
python3 your_script.py
```

Results:

The script will process the blocks and generate a file named "results.txt" in the same directory as the script. Ensure that the "addresses.txt" file is present in the same directory as the script to ensure proper script functionality.


# Rpc Update

This application automates the management of the RPC endpoint URL to ensure it is always connected to a synchronized Bor node. If the node encounters failures or is not fully synchronized, the application automatically performs an update to a new URL

```bash
import requests
import toml
import subprocess

def update_bor_rpc_url(new_url):
    config_file_path = "/path/to/your/config.toml"  

    try:
        
        with open(config_file_path, "r") as config_file:
            config = toml.load(config_file)

        config["bor_rpc_url"] = new_url

        with open(config_file_path, "w") as config_file:
            toml.dump(config, config_file)
        
        print("bor_rpc_url updated to:", new_url)

    except Exception as e:
        print("Error updating bor_rpc_url:", e)

def check_bor_sync_status():
    bor_rpc_url = "http://localhost:8545"  

    try:
        response = requests.post(bor_rpc_url, json={"jsonrpc": "2.0", "method": "eth_syncing", "params": [], "id": 1})
        if response.status_code == 200:
            data = response.json()
            if "result" in data and data["result"] is False:
                print("Bor is fully synchronized")
            else:
                new_bor_rpc_url = "https://your-bor-rpc"
                update_bor_rpc_url(new_bor_rpc_url)
                print("Bor is not fully synchronized. Updated bor_rpc_url.")
        else:
            print("Error in Bor RPC request - Status code:", response.status_code)

    except requests.ConnectionError as ce:
        print("Connection error while checking Bor sync status:", ce)
        print("Attempting to update bor_rpc_url...")
        new_bor_rpc_url = "https://mainnet.stakepool.dev.br/rpc"
        update_bor_rpc_url(new_bor_rpc_url)
    except Exception as e:
        print("Error while checking Bor sync status:", e)

if __name__ == "__main__":
    check_bor_sync_status()
```

This script was developed to provide dynamic management of the RPC URL for a Bor node. It performs periodic checks of the synchronization status of the Bor node, and if it is not fully synchronized, dynamically updates the RPC URL with a new value. During this process, it loads the existing configuration from the TOML file, updates the `bor_rpc_url` parameter with a new URL, and performs the necessary checks to carry out the update automatically.\
\
To set up an alternative URL, replace "[https://your-bor-rpc](https://your-bor-rpc/)" with your desired RPC URL.

Python and Requests Installation:

Make sure you have Python installed on your system. Open the terminal or command prompt. Execute the following command to install the `requests` library:

```bash
pip install requests
```

Script Execution:

Save the Python script (`Your_script.py`) in the directory of your choice. Place a file named "addresses.txt" in the same directory as the script, containing the addresses of the validators.

Run the Script:

In the terminal or command prompt, navigate to the directory where the script is located. Execute the script using the following command:

```bash
python3 your_script.py
```

Results:

The script will periodically check the synchronization status of the Bor node. If the node is not fully synchronized, the script will dynamically update the RPC URL with the alternative URL you configured.

**Managing with systemd unit.**

Service Installation:

```yaml
[Unit]
Description=Reload heimdall Service on Config Change
After=network.target

[Service]
ExecStart=/path/to/reload-heimdall-service.sh

[Install]
WantedBy=default.target
```

After making the changes, reload systemd to apply the modifications:

```
sudo systemctl daemon-reload
```

Start the Service:

```
sudo systemctl start <name-service>
```

Enable:

```
sudo systemctl enable <name-service>
```

This unit file configures the Heimdall service to be restarted whenever there is a change in the configuration. Make sure to replace `/path/to/reload-heimdall-service.sh` with the actual path to your Heimdall service reload script.

**Monitoring Changes:**

```yaml
#!/bin/bash

CONFIG_FILE="/path/to/your/heimdall-config.toml"
SERVICE_NAME="heimdall"  # Change this to match the actual service name

while inotifywait -e close_write "$CONFIG_FILE"; do
    systemctl restart "$SERVICE_NAME"
done
```

This script continuously monitors the TOML configuration file. If the file is modified, it automatically restarts the Heimdall service. Replace `/path/to/your/heimdall-config.toml` with the actual path to your Heimdall configuration file.

* Save the script with a meaningful name, for example, `heimdall-config.sh`.
* Make the script executable:

```
chmod +x heimdall-config.sh
```

Run the script:

```
./heimdall-config.sh
```

* The script will enter into a loop and use `inotifywait` to watch for close\_write events on the specified configuration file.
* When a change occurs in the configuration file, the script will restart the Heimdall service using `systemctl restart`.


# SignerChange Event

This JavaScript script utilizes the Web3.js library to interact with a smart contract. It searches for events called 'SignerChange' within a range of specific validator identifiers. The script retrieves and displays information about changes in signatories ('SignerChange') for each validator in the specified range, providing the addresses of the old and new signatories for each event. This type of analysis is useful for tracking and auditing changes in signatories in a smart contract.

```
const { Web3 } = require('web3');
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); 
const readline = require('readline');
const { promisify } = require('util');

const web3 = new Web3('https://your-rpc-ethereum');
const contractAddress = '0xa59c847bd5ac0172ff4fe912c5d29e5a71a7512b'
const contractABI = [
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "_registry",
        "type": "address"
      }
    ],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "fee",
        "type": "uint256"
      }
    ],
    "name": "ClaimFee",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "totalAmount",
        "type": "uint256"
      }
    ],
    "name": "ClaimRewards",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "newValidatorId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "oldValidatorId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "ConfirmAuction",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "rewards",
        "type": "uint256"
      }
    ],
    "name": "DelegatorClaimedRewards",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "totalStaked",
        "type": "uint256"
      }
    ],
    "name": "DelegatorRestaked",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "DelegatorUnstaked",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "newDynasty",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "oldDynasty",
        "type": "uint256"
      }
    ],
    "name": "DynastyValueChange",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "exitEpoch",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "signer",
        "type": "address"
      }
    ],
    "name": "Jailed",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "previousOwner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "OwnershipTransferred",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "newProposerBonus",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "oldProposerBonus",
        "type": "uint256"
      }
    ],
    "name": "ProposerBonusChange",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "total",
        "type": "uint256"
      }
    ],
    "name": "Restaked",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "newReward",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "oldReward",
        "type": "uint256"
      }
    ],
    "name": "RewardUpdate",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "tokens",
        "type": "uint256"
      }
    ],
    "name": "ShareBurned",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "tokens",
        "type": "uint256"
      }
    ],
    "name": "ShareMinted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "nonce",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "oldSigner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newSigner",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "bytes",
        "name": "signerPubkey",
        "type": "bytes"
      }
    ],
    "name": "SignerChange",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "nonce",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "Slashed",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "nonce",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "newAmount",
        "type": "uint256"
      }
    ],
    "name": "StakeUpdate",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "signer",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "nonce",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "activationEpoch",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "total",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "bytes",
        "name": "signerPubkey",
        "type": "bytes"
      }
    ],
    "name": "Staked",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "auctionAmount",
        "type": "uint256"
      }
    ],
    "name": "StartAuction",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "newThreshold",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "oldThreshold",
        "type": "uint256"
      }
    ],
    "name": "ThresholdChange",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "fee",
        "type": "uint256"
      }
    ],
    "name": "TopUpFee",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "signer",
        "type": "address"
      }
    ],
    "name": "UnJailed",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "nonce",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "deactivationEpoch",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "UnstakeInit",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "total",
        "type": "uint256"
      }
    ],
    "name": "Unstaked",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "newCommissionRate",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "oldCommissionRate",
        "type": "uint256"
      }
    ],
    "name": "UpdateCommissionRate",
    "type": "event"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "getAccountStateRoot",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "accountStateRoot",
        "type": "bytes32"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      }
    ],
    "name": "getStakerDetails",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "reward",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "activationEpoch",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "deactivationEpoch",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "signer",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "_status",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      }
    ],
    "name": "getValidatorContractAddress",
    "outputs": [
      {
        "internalType": "address",
        "name": "ValidatorContract",
        "type": "address"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "isOwner",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "fee",
        "type": "uint256"
      }
    ],
    "name": "logClaimFee",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "totalAmount",
        "type": "uint256"
      }
    ],
    "name": "logClaimRewards",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "newValidatorId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "oldValidatorId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "logConfirmAuction",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "rewards",
        "type": "uint256"
      }
    ],
    "name": "logDelegatorClaimedRewards",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "totalStaked",
        "type": "uint256"
      }
    ],
    "name": "logDelegatorRestaked",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "logDelegatorUnstaked",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "newDynasty",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "oldDynasty",
        "type": "uint256"
      }
    ],
    "name": "logDynastyValueChange",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "exitEpoch",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "signer",
        "type": "address"
      }
    ],
    "name": "logJailed",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "getAccountStateRoot",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "accountStateRoot",
        "type": "bytes32"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      }
    ],
    "name": "getStakerDetails",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "reward",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "activationEpoch",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "deactivationEpoch",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "signer",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "_status",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      }
    ],
    "name": "getValidatorContractAddress",
    "outputs": [
      {
        "internalType": "address",
        "name": "ValidatorContract",
        "type": "address"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "isOwner",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "fee",
        "type": "uint256"
      }
    ],
    "name": "logClaimFee",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "totalAmount",
        "type": "uint256"
      }
    ],
    "name": "logClaimRewards",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "newValidatorId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "oldValidatorId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "logConfirmAuction",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "rewards",
        "type": "uint256"
      }
    ],
    "name": "logDelegatorClaimedRewards",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "totalStaked",
        "type": "uint256"
      }
    ],
    "name": "logDelegatorRestaked",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "logDelegatorUnstaked",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "newDynasty",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "oldDynasty",
        "type": "uint256"
      }
    ],
    "name": "logDynastyValueChange",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "exitEpoch",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "signer",
        "type": "address"
      }
    ],
    "name": "logJailed",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "getAccountStateRoot",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "accountStateRoot",
        "type": "bytes32"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      }
    ],
    "name": "getStakerDetails",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "reward",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "activationEpoch",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "deactivationEpoch",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "signer",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "_status",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      }
    ],
    "name": "getValidatorContractAddress",
    "outputs": [
      {
        "internalType": "address",
        "name": "ValidatorContract",
        "type": "address"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "isOwner",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "fee",
        "type": "uint256"
      }
    ],
    "name": "logClaimFee",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "totalAmount",
        "type": "uint256"
      }
    ],
    "name": "logClaimRewards",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "newValidatorId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "oldValidatorId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "logConfirmAuction",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "rewards",
        "type": "uint256"
      }
    ],
    "name": "logDelegatorClaimedRewards",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "totalStaked",
        "type": "uint256"
      }
    ],
    "name": "logDelegatorRestaked",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "logDelegatorUnstaked",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "newDynasty",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "oldDynasty",
        "type": "uint256"
      }
    ],
    "name": "logDynastyValueChange",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "exitEpoch",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "signer",
        "type": "address"
      }
    ],
    "name": "logJailed",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "getAccountStateRoot",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "accountStateRoot",
        "type": "bytes32"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      }
    ],
    "name": "getStakerDetails",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "reward",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "activationEpoch",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "deactivationEpoch",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "signer",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "_status",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      }
    ],
    "name": "getValidatorContractAddress",
    "outputs": [
      {
        "internalType": "address",
        "name": "ValidatorContract",
        "type": "address"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "isOwner",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "fee",
        "type": "uint256"
      }
    ],
    "name": "logClaimFee",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "totalAmount",
        "type": "uint256"
      }
    ],
    "name": "logClaimRewards",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "newValidatorId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "oldValidatorId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "logConfirmAuction",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "rewards",
        "type": "uint256"
      }
    ],
    "name": "logDelegatorClaimedRewards",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "newDynasty",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "oldDynasty",
        "type": "uint256"
      }
    ],
    "name": "logDynastyValueChange",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "exitEpoch",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "signer",
        "type": "address"
      }
    ],
    "name": "logJailed",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "getAccountStateRoot",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "accountStateRoot",
        "type": "bytes32"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "getCommittee",
    "outputs": [
      {
        "internalType": "bytes",
        "name": "committee",
        "type": "bytes"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "getEraSize",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "eraSize",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "eraId",
        "type": "uint256"
      }
    ],
    "name": "getEraStart",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "startTimestamp",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "eraId",
        "type": "uint256"
      }
    ],
    "name": "getEraValidatorRewardAccumulated",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "rewardAccumulated",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "getCurrentEraId",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "eraId",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "eraId",
        "type": "uint256"
      }
    ],
    "name": "getValidatorEraRewardAccumulated",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "rewardAccumulated",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "validatorId",
        "type": "uint256"
      }
    ],
    "name": "getValidatorLockup",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "lockupDuration",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "lockupAmount",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "isOwner",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [],
    "name": "lockTokens",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [],
    "name": "renounceOwnership",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [],
    "name": "renounceOwnership",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [],
    "name": "unlockTokens",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "transferOwnership",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "internalType": "uint256","type": "uint256","name": "validatorId"
      },
      {
        "internalType": "address","type": "address","name": "user"
      },
      {
        "internalType": "uint256","type": "uint256","name": "rewards"
      }
    ],
    "name": "DelegatorClaimedRewards","type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "internalType": "address","type": "address","name": "user"
      },
      {
        "internalType": "uint256","type": "uint256","name": "fee"
      }
    ],
    "name": "ClaimFee","type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "internalType": "uint256","type": "uint256","name": "newDynasty"
      },
      {
        "internalType": "uint256","type": "uint256","name": "oldDynasty"
      }
    ],
    "name": "DynastyValueChange","type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "internalType": "uint256","type": "uint256","name": "validatorId"
      },
      {
        "internalType": "uint256","type": "uint256","name": "amount"
      },
      {
        "internalType": "uint256","type": "uint256","name": "totalAmount"
      }
    ],
    "name": "ClaimRewards","type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "internalType": "uint256","type": "uint256","name": "newValidatorId"
      },
      {
        "internalType": "uint256","type": "uint256","name": "oldValidatorId"
      },
      {
        "internalType": "uint256","type": "uint256","name": "amount"
      }
    ],
    "name": "ConfirmAuction","type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "internalType": "uint256","type": "uint256","name": "validatorId"
      },
      {
        "internalType": "address","type": "address","name": "user"
      },
      {
        "internalType": "uint256","type": "uint256","name": "amount"
      }
    ],
    "name": "DelegatorRestaked","type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "internalType": "uint256","type": "uint256","name": "validatorId"
      },
      {
        "internalType": "address","type": "address","name": "user"
      },
      {
        "internalType": "uint256","type": "uint256","name": "amount"
      }
    ],
    "name": "DelegatorUnstaked","type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "internalType": "uint256","type": "uint256","name": "validatorId"
      },
      {
        "internalType": "uint256","type": "uint256","name": "exitEpoch"
      },
      {
        "internalType": "address","type": "address","name": "signer"
      }
    ],
    "name": "Jailed","type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "internalType": "bytes32","type": "bytes32","name": "accountStateRoot"
      }
    ],
    "name": "NewAccountStateRoot","type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "internalType": "bytes32","type": "bytes32","name": "committeeStateRoot"
      }
    ],
    "name": "NewCommitteeStateRoot","type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "internalType": "uint256","type": "uint256","name": "eraSize"
      }
    ],
    "name": "NewEraSize","type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "internalType": "uint256","type": "uint256","name": "eraId"
      },
      {
        "internalType": "uint256","type": "uint256","name": "startTimestamp"
      }
    ],
    "name": "NewEraStart","type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "internalType": "uint256","type": "uint256","name": "eraId"
      },
      {
        "internalType": "uint256","type": "uint256","name": "rewardAccumulated"
      }
    ],
    "name": "NewEraValidatorRewardAccumulated","type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "internalType": "uint256","type": "uint256","name": "eraId"
      },
      {
        "internalType": "uint256","type": "uint256","name": "rewardAccumulated"
      }
    ],
    "name": "NewValidatorEraRewardAccumulated","type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "internalType": "uint256","type": "uint256","name": "validatorId"
      },
      {
        "internalType": "uint256","type": "uint256","name": "lockupDuration"
      },
      {
        "internalType": "uint256","type": "uint256","name": "lockupAmount"
      }
    ],
    "name": "SetValidatorLockup","type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "internalType": "address","type": "address","name": "previousOwner"
      }
    ],
    "name": "OwnershipRenounced","type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "internalType": "address","type": "address","name": "previousOwner"
      },
      {
        "internalType": "address","type": "address","name": "newOwner"
      }
    ],
    "name": "OwnershipTransferred","type": "event"
  }
];


const contract = new web3.eth.Contract(contractABI, contractAddress);

async function fetchEventDataForValidator(validatorId) {
  await sleep(5000);

  try {
    const events = await contract.getPastEvents('SignerChange', {
      filter: { validatorId },
      fromBlock: 'earliest',
      toBlock: 'latest',
    });

    events.forEach((event) => {
      const oldSigner = event.returnValues.oldSigner;
      const newSigner = event.returnValues.newSigner;
      console.log('Validator ID:', validatorId);
      console.log('Old Signer:', oldSigner);
      console.log('New Signer:', newSigner);
    });
  } catch (error) {
    console.error('Error fetching event logs for Validator ID:', validatorId);
    console.error(error);
  }
}

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

rl.question('Enter validator ID: ', (validatorId) => {
  rl.close();

  
  validatorId = parseInt(validatorId, 10);
  if (!isNaN(validatorId)) {
    fetchEventDataForValidator(validatorId);
  } else {
    console.error('Invalid input. Please enter a valid validator ID.');
  }
});



```

Dependencies Installation:

Ensure that Node.js is installed on your system. If not, you can download it from nodejs.org. Navigate to the directory where you saved the script on your system using the terminal or command prompt. Web3.js Installation:

Execute the following command to install the Web3.js library:

```bash
npm install web3
```

Script Editing:

Open the script (`your-script.js`) in a text editor. Replace '[https://your-rpc-ethereum](https://your-rpc-ethereum/)' with the RPC URL of your Ethereum node.

In the terminal or command prompt, within the directory where the script is located, execute the following command:

```bash
node your-script.js
```

Make sure to replace `your-script.js` with the actual name of your script file.\
\
**Note:** that in the script, all events defined in the contract have been included, providing the flexibility to implement logic related to any specific event, not limited solely to SignerChange.


# Span Participation

The script retrieves information about the latest span, prompts the user for a validator ID, and checks if that validator is currently participating in the span. These operations aid in monitoring the involvement of a validator.

```bash
import os
import requests

if os.path.exists("lockfile"):
    exit()

with open("lockfile", "w") as file:
    file.write("")

def get_current_span_id():
    try:
        url = "https://heimdall-api.polygon.technology/bor/latest-span"
        response = requests.get(url)
        if response.status_code == 200:
            data = response.json()
            span_id = data.get("result", {}).get("span_id")
            return span_id
        else:
            return None
    except Exception as e:
        return None

def check_validator_in_current_span(span_id, validator_id):
    try:
        url = f"https://heimdall-api.polygon.technology/bor/span/{span_id}"
        response = requests.get(url)
        if response.status_code == 200:
            data = response.json()
            selected_producers = data.get("result", {}).get("validator_set", {}).get("validators", [])
            for validator in selected_producers:
                if validator["ID"] == validator_id:
                    return "The validator is part of the current span."
            return "The validator is NOT part of the current span."
        else:
            return "Error fetching current span information."
    except Exception as e:
        return "Error checking validator in the current span."

if __name__ == "__main__":
    validator_id = int(input("Enter the validator ID: "))
    current_span_id = get_current_span_id()

    if current_span_id is not None:
        response = check_validator_in_current_span(current_span_id, validator_id)
        print(response)

    
    os.remove("lockfile")
```

Python and Requests Installation:

Make sure you have Python installed on your system. Open the terminal or command prompt. Execute the following command to install the `requests` library:

```bash
pip install requests
```

Script Execution:

Save the Python script (`Your_script.py`) in the directory of your choice. Place a file named "addresses.txt" in the same directory as the script, containing the addresses of the validators.

Run the Script:

In the terminal or command prompt, navigate to the directory where the script is located. Execute the script using the following command:

```bash
python3 your_script.py
```

Results:

The script checks if the validator with the provided ID is among the validators of the current span.


# RPC

<table><thead><tr><th width="164">Name</th><th width="401">Chain</th><th>Chain ID</th></tr></thead><tbody><tr><td><strong>Polygon</strong></td><td><strong>Ethereum</strong></td><td>137</td></tr><tr><td>Heimdall API</td><td><a href="https://mainnet.stakepool.dev.br/mainnet"><mark style="color:green;">https://mainnet.stakepool.dev.br/mainnet</mark></a></td><td></td></tr><tr><td>Heimdall Rest API</td><td><a href="https://mainnet.stakepool.dev.br/"><mark style="color:green;">https://mainnet.stakepool.dev.br/</mark></a></td><td></td></tr><tr><td>RPC Endpoint</td><td><a href="https://mainnet.stakepool.dev.br/rpc"><mark style="color:green;">https://mainnet.stakepool.dev.br/rpc</mark></a></td><td></td></tr></tbody></table>

<table><thead><tr><th width="164">Name</th><th width="401">Chain</th><th>Chain ID</th></tr></thead><tbody><tr><td><strong>Amoy</strong></td><td><strong>Sepolia</strong></td><td>80002</td></tr><tr><td>Heimdall API</td><td><a href="https://amoy.stakepool.dev.br/amoy/"><mark style="color:green;">https://amoy.stakepool.dev.br/amoy/</mark></a></td><td></td></tr><tr><td>Heimdall Rest API</td><td><a href="https://amoy.stakepool.dev.br/"><mark style="color:green;">https://amoy.stakepool.dev.br</mark></a><mark style="color:green;">/</mark></td><td></td></tr><tr><td>RPC Endpoint</td><td><a href="https://amoy.stakepool.dev.br/rpc"><mark style="color:green;">https://amoy.stakepool.dev.br/rpc</mark></a></td><td></td></tr></tbody></table>

\
\\


# Faucet

[<mark style="color:green;">https://faucet.stakepool.dev.br/amoy</mark>](https://faucet.stakepool.dev.br/amoy)

\
\\


# Live Peers

<table><thead><tr><th width="164">Name</th><th width="468">Chain</th><th>Chain ID</th></tr></thead><tbody><tr><td><strong>Polygon</strong></td><td><strong>Mainnet</strong></td><td>137</td></tr><tr><td>Heimdall</td><td>2832F17466F96F08CA4AE05E18A6F96D4D856968@46.4.95.132:26656</td><td></td></tr><tr><td>Bor</td><td>enode://429bb1c348c769218ecf2b400dc16b86045f3eec89780a8d8d73913f6c9dc184a8d3d3bfcb7197092065c2ebbad4fd177aa8d9053f5051e5db518f541144268a@46.4.95.132:30303</td><td></td></tr></tbody></table>

<table><thead><tr><th width="164">Name</th><th width="468">Chain</th><th>Chain ID</th></tr></thead><tbody><tr><td><strong>Amoy</strong></td><td><strong>Testnet</strong></td><td>80002</td></tr><tr><td>Heimdall</td><td>4CA9FF871C7AA1E7B64E1EAE110835F68D6A0BD4@194.233.65.96:26646</td><td></td></tr><tr><td>Bor</td><td>enode://3ef9bb36b0df8dff269a555e334de9bd703f42836a3d88a77804b6ffcebb9c706448b2a1088cbdb252ca70ed52c63b9c27821f1624992be89e92873eb89a67f4@194.233.65.96:30313</td><td></td></tr></tbody></table>


# Discord Webhook

## Webhooks

An easy way to send automated messages and data updates to a text channel on your server.

#### Creating a Webhook

​1 - Open Discord and join your desired server.

2 - Locate the channel for which you want to create the webhook.

3 - Click on the ⚙️ icon next to the channel name.

4 - Set a name for the webhook and choose an avatar

5 - Click the button to create a new webhook.

<figure><img src="/files/670hnY6wGNYeM1Quu5Nc" alt=""><figcaption></figcaption></figure>

Now you have your own accessible URL that you can link to a specific channel to receive messages.

​

<figure><img src="/files/YJEhhqO8Ca3EVPi5EPgk" alt=""><figcaption></figcaption></figure>


