Run a Validator Node

Validator parameters

  • Minimum stake: 3,175,000 FTM

  • Maximum validator size: 15x the self-stake amount

  • Minimum hardware requirements: AWS EC2 m5.xlarge with 4 vCPUs (3.1 GHz) and at least 500GB of Amazon EBS General Purpose SSD (gp2) storage (or equivalent).

  • Rewards: currently ~13% APY (Normal APY on self-stake + 15% of delegators' rewards). APY varies based on staked %. For up-to-date APY, check https://fantom.foundation/ftm-staking/

What are we going to do?

Launch Cloud Instance

You can either run a node on your own hardware or use a cloud provider. We would recommend choosing one of the big cloud providers, e.g. Amazon AWS.

Node Specifications

We recommend the following or better: m5.xlarge General Purpose Instance with 4 vCPUs (3.1 GHz), 16GB of memory, up to 10 Gbps network bandwidth.

We would recommend going with Ubuntu Server 20.04 LTS (64-bit) at least 500GB of disk space for now to ensure enough storage.

Network Settings

Open up port 22 for SSH, as well as port 5050 for both TCP and UDP traffic.

Set up Non-Root User

If there is already a non-root user available, you can skip this step.

# SSH into your machine
(local)$ ssh [email protected]{VALIDATOR_IP_ADDRESS}
# Update the system
(validator)$ sudo apt-get update && sudo apt-get upgrade -y
# Create a non-root user
(validator)$ USER={USERNAME}
(validator)$ sudo mkdir -p /home/$USER/.ssh
(validator)$ sudo touch /home/$USER/.ssh/authorized_keys
(validator)$ sudo useradd -d /home/$USER $USER
(validator)$ sudo usermod -aG sudo $USER
(validator)$ sudo chown -R $USER:$USER /home/$USER/
(validator)$ sudo chmod 700 /home/$USER/.ssh
(validator)$ sudo chmod 644 /home/$USER/.ssh/authorized_keys

Make sure to paste your public SSH key into the authorized_keys file of the newly created user in order to be able to log in via SSH.

# Enable sudo without password for the user
(validator)$ sudo vi /etc/sudoers

Add the following line to the end of the file:

{USERNAME} ALL=NOPASSWD: ALL
(This step is Optional)
# Install zsh
(validator)$ sudo apt-get install -y zsh
# Set zsh as the default shell for the user
(validator)$ sudo vi /etc/passwd

Go to the line with your username (most probably the last one, as you just created the user) and change the default shell at the end of the line from:

(This step is Optional)
/bin/bash --> /bin/zsh

Now close the root SSH connection to the machine and log in as your newly created user:

# Close the root SSH connection
(validator)$ exit
# Log in as new user
(local)$ ssh {USERNAME}@{VALIDATOR_IP_ADDRESS}

Now let’s make the zsh shell a little bit prettier:

(This step is Optional)
# Install oh-my-zsh
(validator)$ git clone https://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh
(validator)$ cp ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zshrc

Exit the shell and log in again for the changes be activated:

# Exit the shell
(validator)$ exit
# Re-Login
(local)$ ssh {USERNAME}@{VALIDATOR_IP_ADDRESS}

Install Required Tools

You are still logged in as the new user via SSH. Now we are going to install Go and Lachesis.

First, install the required build tools:

# Install build-essential
(validator)$ sudo apt-get install -y build-essential

Install Go

# Install go
(validator)$ wget https://dl.google.com/go/go1.13.3.linux-amd64.tar.gz
(validator)$ sudo tar -xvf go1.13.3.linux-amd64.tar.gz
(validator)$ sudo mv go /usr/local

Then open up your .zshrc file and export the required Go paths:

# Export go paths
(validator)$ vi ~/.zshrc
# Append at the bottom of .zshrc
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$GOPATH/bin:$GOROOT/bin:$PATH

Reload your .zshrc config:

# Reload .zshrc config
(validator)$ source ~/.zshrc

Validate your Go installation:

Install Lachesis

# Install Lachesis
(validator)$ mkdir -p $HOME/go/src/github.com/Fantom-foundation
(validator)$ cd $HOME/go/src/github.com/Fantom-foundation/
(validator)$ git clone https://github.com/Fantom-foundation/go-lachesis.git
(validator)$ cd go-lachesis
(validator)$ git checkout tags/v0.7.0-rc.1 -b lachesis-v7rc1
(validator)$ make build

Validate your Lachesis installation:

$./build/lachesis help
VERSION:
0.7.0-rc.1

Add Lachesis to the path so that you can access it from anywhere:

# Export lachesis path
(validator)$ vi ~/.zshrc
# Append at the bottom of .zshrc
export PATH=$PATH:/home/{USERNAME}/go/src/github.com/Fantom-foundation/go-lachesis/build

Reload your .zshrc config:

# Reload .zshrc config
(validator)$ source ~/.zshrc

You can now access Lachesis from every directory 👌:

# Reload .zshrc config
(validator)$ cd ~
(validator)$ lachesis version

Register Fantom Validator

After installing everything we need, you can continue by registering your Fantom validator node on-chain. To do this, you need to create a validator wallet. The wallet is the validator’s identity in the network which it uses to authenticate, sign messages, etc. First, start the Lachesis read-only node to interact with it and to create a validator wallet:

# Start lachesis node
(validator)$ lachesis --nousb

Starting up your node will look something like this:

The node should start to sync the network data right away:

Create a validator wallet

The node running and syncing the network in your current console, so you need to open up a new console window, connect via SSH to the server and enter the following commands to create a wallet:

# Create validator wallet
(validator)$ lachesis account new

After entering the command, you will get prompted to enter a password for the account (= wallet) — use a strong one! You can e.g. use a password manager to generate a 20+ digit password to secure your wallet. It will look something like this:

NEVER share your private key or keystore with anyone!

By the way: The wallet above is not a real wallet we use, it’s just for demonstration purposes only.

Fund your validator wallet

The next step is to fund your validator wallet with enough FTM to become a validator. That means you need to have at least 3,175,000 FTM in the wallet you just created (send a little more to cover transaction fees).

If you don’t have your FTM on the Opera mainnet already, you will need to swap your FTM using the Fantom Bridge:

How to use the Fantom Bridge

After successfully swapping FTM to your newly created Opera wallet, you can register your validator via the SFC Smart Contract.

Make sure you wait for your node to be fully synced, otherwise your FTM will not show up in your wallet!

Create your validator via the SFC

Open up the console where you entered the commands to create the validator wallet previously and attach to the Lachesis node console:

# Attach to lachesis console
(validator)$ lachesis attach

By doing so, you will get a JavaScript console where you can directly interact with the Lachesis node and e.g. send transactions (which you will do in a moment):

Now initialize the SFC contract ABI variable:

abi = JSON.parse('[{"constant":true,"inputs":[],"name":"minDelegation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"bondedRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"stakersNum","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"slashedStakeTotalAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"short","type":"uint256"},{"internalType":"uint256","name":"long","type":"uint256"}],"name":"updateGasPowerAllocationRate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"withdrawDelegation","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"minStakeDecrease","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"updateBaseRewardPerSec","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"prepareToWithdrawDelegation","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"delegationLockPeriodEpochs","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"epochSnapshots","outputs":[{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"epochFee","type":"uint256"},{"internalType":"uint256","name":"totalBaseRewardWeight","type":"uint256"},{"internalType":"uint256","name":"totalTxRewardWeight","type":"uint256"},{"internalType":"uint256","name":"baseRewardPerSecond","type":"uint256"},{"internalType":"uint256","name":"stakeTotalAmount","type":"uint256"},{"internalType":"uint256","name":"delegationsTotalAmount","type":"uint256"},{"internalType":"uint256","name":"totalSupply","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"maxDelegatedRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"wrID","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"prepareToWithdrawStakePartial","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"contractCommission","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"stakerID","type":"uint256"}],"name":"_upgradeStakerStorage","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"maxEpochs","type":"uint256"}],"name":"claimValidatorRewards","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"delegationsTotalAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes","name":"metadata","type":"bytes"}],"name":"updateStakerMetadata","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"minStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"unbondingUnlockPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[],"name":"increaseDelegation","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"stakeTotalAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"stakeLockPeriodTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"delegator","type":"address"}],"name":"_syncDelegator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"delegationsNum","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"withdrawalRequests","outputs":[{"internalType":"uint256","name":"stakerID","type":"uint256"},{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"delegation","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"unbondingStartDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"stakerID","type":"uint256"},{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"commission","type":"uint256"}],"name":"_calcValidatorEpochReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"stakeLockPeriodEpochs","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"minDelegationIncrease","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getStakerID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"stakerID","type":"uint256"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"_calcRawValidatorEpochReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bondedTargetRewardUnlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"stakerID","type":"uint256"},{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"delegationAmount","type":"uint256"},{"internalType":"uint256","name":"commission","type":"uint256"}],"name":"_calcDelegationEpochReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardsStash","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"currentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"maxEpochs","type":"uint256"}],"name":"claimDelegationRewards","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"bondedTargetPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"currentSealedEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"stakersLastID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"rewardsAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"unstashRewards","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","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":"dagAdrress","type":"address"},{"internalType":"address","name":"sfcAddress","type":"address"},{"internalType":"bytes","name":"metadata","type":"bytes"}],"name":"createStakeWithAddresses","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"stakerID","type":"uint256"},{"internalType":"uint256","name":"_fromEpoch","type":"uint256"},{"internalType":"uint256","name":"maxEpochs","type":"uint256"}],"name":"calcValidatorRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakerMetadata","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"slashedDelegationsTotalAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"validatorCommission","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"maxStakerMetadataSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"internalType":"bool","name":"isDelegation","type":"bool"},{"internalType":"uint256","name":"stakerID","type":"uint256"}],"name":"_rewardsBurnableOnDeactivation","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"sfcAddress","type":"address"}],"name":"_sfcAddressToStakerID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"uint256","name":"_fromEpoch","type":"uint256"},{"internalType":"uint256","name":"maxEpochs","type":"uint256"}],"name":"calcDelegationRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"e","type":"uint256"},{"internalType":"uint256","name":"v","type":"uint256"}],"name":"epochValidator","outputs":[{"internalType":"uint256","name":"stakeAmount","type":"uint256"},{"internalType":"uint256","name":"delegatedMe","type":"uint256"},{"internalType":"uint256","name":"baseRewardWeight","type":"uint256"},{"internalType":"uint256","name":"txRewardWeight","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"withdrawStake","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"delegations","outputs":[{"internalType":"uint256","name":"createdEpoch","type":"uint256"},{"internalType":"uint256","name":"createdTime","type":"uint256"},{"internalType":"uint256","name":"deactivatedEpoch","type":"uint256"},{"internalType":"uint256","name":"deactivatedTime","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"paidUntilEpoch","type":"uint256"},{"internalType":"uint256","name":"toStakerID","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"to","type":"uint256"}],"name":"createDelegation","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newSfcAddress","type":"address"}],"name":"updateStakerSfcAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"prepareToWithdrawStake","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"minStakeIncrease","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"minDelegationDecrease","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes","name":"metadata","type":"bytes"}],"name":"createStake","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"bondedTargetStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[],"name":"increaseStake","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"wrID","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"prepareToWithdrawDelegationPartial","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"stakerID","type":"uint256"}],"name":"_syncStaker","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"delegationLockPeriodTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"wrID","type":"uint256"}],"name":"partialWithdrawByRequest","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakers","outputs":[{"internalType":"uint256","name":"status","type":"uint256"},{"internalType":"uint256","name":"createdEpoch","type":"uint256"},{"internalType":"uint256","name":"createdTime","type":"uint256"},{"internalType":"uint256","name":"deactivatedEpoch","type":"uint256"},{"internalType":"uint256","name":"deactivatedTime","type":"uint256"},{"internalType":"uint256","name":"stakeAmount","type":"uint256"},{"internalType":"uint256","name":"paidUntilEpoch","type":"uint256"},{"internalType":"uint256","name":"delegatedMe","type":"uint256"},{"internalType":"address","name":"dagAddress","type":"address"},{"internalType":"address","name":"sfcAddress","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"stakerID","type":"uint256"},{"indexed":true,"internalType":"address","name":"dagSfcAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CreatedStake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"stakerID","type":"uint256"},{"indexed":true,"internalType":"address","name":"oldSfcAddress","type":"address"},{"indexed":true,"internalType":"address","name":"newSfcAddress","type":"address"}],"name":"UpdatedStakerSfcAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"stakerID","type":"uint256"}],"name":"UpdatedStakerMetadata","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"stakerID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"diff","type":"uint256"}],"name":"IncreasedStake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"toStakerID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CreatedDelegation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"stakerID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"diff","type":"uint256"}],"name":"IncreasedDelegation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"uint256","name":"stakerID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fromEpoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"untilEpoch","type":"uint256"}],"name":"ClaimedDelegationReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"stakerID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fromEpoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"untilEpoch","type":"uint256"}],"name":"ClaimedValidatorReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"auth","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"rewards","type":"uint256"}],"name":"UnstashedRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"addr","type":"address"},{"indexed":true,"internalType":"uint256","name":"stakerID","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isDelegation","type":"bool"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BurntRewardStash","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"stakerID","type":"uint256"}],"name":"PreparedToWithdrawStake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"stakerID","type":"uint256"}],"name":"DeactivatedStake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"auth","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"uint256","name":"stakerID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"wrID","type":"uint256"},{"indexed":false,"internalType":"bool","name":"delegation","type":"bool"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CreatedWithdrawRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"stakerID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"penalty","type":"uint256"}],"name":"WithdrawnStake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"stakerID","type":"uint256"}],"name":"PreparedToWithdrawDelegation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"stakerID","type":"uint256"}],"name":"DeactivatedDelegation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"stakerID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"penalty","type":"uint256"}],"name":"WithdrawnDelegation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"auth","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"uint256","name":"stakerID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"wrID","type":"uint256"},{"indexed":false,"internalType":"bool","name":"delegation","type":"bool"},{"indexed":false,"internalType":"uint256","name":"penalty","type":"uint256"}],"name":"PartialWithdrawnByRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"oldStakerID","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"newStakerID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UpdatedDelegation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"stakerID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delegatedMe","type":"uint256"}],"name":"UpdatedStake","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"UpdatedBaseRewardPerSec","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"short","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"long","type":"uint256"}],"name":"UpdatedGasPowerAllocationRate","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"}]')

… as well as the SFC contract object itself:

sfc = web3.ftm.contract(abi).at("0xfc00face00000000000000000000000000000000")

After intializing both variables, you can now interact with the network’s SFC. Enter the following command to check that everything works as expected:

# Show info about validator #1
sfc.stakers(1)

If it looks like this, everything is OK (you should not get an error here):

Next, try to get your stakerId (= validator id) from the SFC using your previously generated validator wallet address:

# Get your validator id
sfc.getStakerID("{VALIDATOR_WALLET_ADDRESS}")

This should return 0, as you are not registered as a validator yet:

Next, unlock your validator wallet to be able to execute the registration transaction (make sure to use the password you set before):

# Unlock validator wallet
personal.unlockAccount("{VALIDATOR_WALLET_ADDRESS}", "{PASSWORD}", 60)

This will return “true” if unlocking the wallet was successful:

Next, send the createStake transaction to register your validator (the value is the representation of the smallest FTM unit, so dividing it be 1e18 will result in 3,175,000 FTM):

# Register your validator
tx = sfc.createStake([], {from: "{VALIDATOR_WALLET_ADDRESS}", value: "3175000000000000000000000"})

Make sure to check your registration transaction (could take a few moments to be confirmed):

# Check your registration transaction
ftm.getTransactionReceipt(tx)

Look for the status: “0x1” at the bottom, which means the transaction was successful:

You can also copy the transactionHash and go the Fantom Explorer and check your transaction there:

https://explorer.fantom.network/transactions/[YOURTX]

This would look something like the below:

Finally, execute the following command again to check your stakerId (= validator id):

# Get your validator id
sfc.getStakerID("{VALIDATOR_WALLET_ADDRESS}")

It should now return something other than “0”:

Congrats, you are now a Fantom validator!

Run your Fantom Validator Node

Before you run off celebrating, you need to restart your node in validator mode! 😉 Close the Lachesis console window by typing “exit”. Then head back to the console window where you started your node with the following command:

(validator)$ lachesis --nousb --validator {VALIDATOR_WALLET_ADDRESS} --unlock {VALIDATOR_WALLET_ADDRESS} --password $HOME/password

Run validator node as pm2 process (Optional)

Before continuing this optional step, you have to stop the validator node by pressing CTRL+C. Then install nodejs, npm and the process manager pm2:

(validator)$ sudo apt-get install -y nodejs npm
(validator)$ sudo npm i -g pm2

Next, create a file called “password” in your home directory and input the wallet password, so the validator node has access to it and can unlock the wallet:

# Create password file in home directory
(validator)$ cd ~
(validator)$ touch password
# Put in the password you created earlier
(validator)$ vi password

Next, create a shell script where you put in the start command for the Lachesis node:

# Create script to run the lachesis node
(validator)$ cd ~
(validator)$ touch runNode.sh
(validator)$ chmod +x runNode.sh

Then put the following content into the newly created runNode.sh file:

#!/bin/sh
$HOME/go/src/github.com/Fantom-foundation/go-lachesis/build/lachesis --nousb --validator {VALIDATOR_WALLET_ADDRESS} --unlock {VALIDATOR_WALLET_ADDRESS} --password $HOME/password

Next, let’s create the pm2 config file, where you tell pm2 what application it should run:

# Create pm2 config
(validator)$ touch ecosystem.config.js

Paste the following content into the ecosystem.config.js file:

module.exports = { apps : [ { name: "fantom", script: "/home/{USERNAME}/runNode.sh", exec_mode: "fork", exec_interpreter: "bash"} ] }

Then start the Lachesis node using pm2:

# Start lachesis node with pm2
(validator)$ pm2 start ./ecosystem.config.js

This will start up the Lachesis node as a pm2 controlled process. You can check the status via:

(validator)$ pm2 status

You can also attach to the process to see the logs:

(validator)$ pm2 logs

Finally, tell pm2 to autostart the Lachesis node upon system restart:

(validator)$ pm2 startup
(validator)$ pm2 save

Now your node is running as a pm2 process and will restart in case the system reboots.

This guide was originally written by [email protected] (https://www.block42.tech/) and has been published with the author’s consent. Check out the original guide.