Validator Transaction Manifests

There’s been a few requests for transaction manifests relating to validator management that aren’t very well documented by RDX Works. I thought it would be useful to collate them all in one thread…

In all cases, the transaction manifest needs to begin with a proof of the owner badge. The manifest for this is shown below:

CALL_METHOD
    Address("<ACCOUNT ADDRESS WITH VALIDATOR BADGE>")
    "create_proof_of_non_fungibles"
    Address("resource_rdx1nfxxxxxxxxxxvdrwnrxxxxxxxxx004365253834xxxxxxxxxvdrwnr")
    Array<NonFungibleLocalId>(
        NonFungibleLocalId("<BADGE ID STARTING AND ENDING WITH []>")
    )
;

The manifest above can then be appended with any of the following manifests to perform specific tasks. You can do these individually, or club them together in sequence to perform as one atomic transaction.

Unregister/Register Validator
Removes your validator from the active set at the start of the next epoch. Use this to prevent any downtime or maintenance from causing your node to miss proposals and slow down the network

CALL_METHOD
    Address("<VALIDATOR_ADDRESS>")
    "unregister";

Change Key
Updates the public key associated with your validator component. Use this to failover to a backup node that is sync’d to the network but not currently validating. Note: your validator address must also be present in the backup nodes config

CALL_METHOD
    Address("<VALIDATOR ADDRESS>")
    "update_key"
Bytes("<PUBLIC ADDRESS>");

Accept Delegated Stake
Toggle whether your node will accept stake from anyone but the owner

CALL_METHOD
    Address("<VALIDATOR_ADDRESS>")
    "update_accept_delegated_stake"
    true;

Update Validator Fee
Update the fee percentage of emission rewards collected by your validator. This should be in decimal format as shown below, with the example given equalling 5%

CALL_METHOD
    Address("<VALIDATOR_ADDRESS>")
    "update_fee"
    Decimal("0.05");

Update Validator Metadata
Updates the validator name, description, URL of icon, URL of info website

SET_METADATA
    Address("<VALIDATOR_ADDRESS>")
    "name"
    Enum<Metadata::String>("<METADATA_VALIDATOR_NAME>");

SET_METADATA
    Address("<VALIDATOR_ADDRESS>")
    "description"
    Enum<Metadata::String>("<METADATA_VALIDATOR_DESCRIPTION>");

SET_METADATA
    Address("<VALIDATOR_ADDRESS>")
    "icon_url"
    Enum<Metadata::Url>("<METADATA_VALIDATOR_ICON_URL>");

SET_METADATA
    Address("<VALIDATOR_ADDRESS>")
    "info_url"
    Enum<Metadata::Url>("<METADATA_VALIDATOR_INFO_URL>");

In addition to the fundamental manifests above, it is also possible to lock and unlock owner stake. This demonstrates to the community that a validator has “skin in the game” and any LSUs locked to the owner vault are subject to a 4 week unstake delay.

Lock Owner Stake
Takes LSUs and deposits them into the locked owner stake vault of the validator component

CALL_METHOD
    Address("<ACCOUNT ADDRESS TO LOCK LSUs FROM>")
    "withdraw"
    Address("<VALIDATOR LSU RESOURCE ADDRESS>")
    Decimal("<NUMBER OF LSUS TO LOCK>");

TAKE_FROM_WORKTOP
    Address("<VALIDATOR LSU RESOURCE ADDRESS>")
    Decimal("<NUMBER OF LSUS TO LOCK>")
    Bucket("stake_units_to_lock");

CALL_METHOD
    Address("<VALIDATOR_ADDRESS>")
    "lock_owner_stake_units"
    Bucket("stake_units_to_lock");

If you want to unlock stake from a validator, this has to be done in 2 parts. Initially a request is made to start the unlock process. Once the “unlock epoch” has passed, a second request to finish the process is made:

Start Unlock Owner Stake
Begins the process of unlocking owner stake

CALL_METHOD
    Address("<VALIDATOR ADDRESS>")
    "start_unlock_owner_stake_units"
    Decimal("<AMOUNT OF LSUs TO UNLOCK>");

Once the unlock process has started, it is possible to check the status of this using the core API of your node. Below is an example request to confirm the unlock epoch:

curl -k -X POST 'http://localhost:3333/core/state/validator' \
--header 'Content-Type: application/json' \
--data-raw '{
"network": "mainnet",
"validator_address": "<VALIDATOR ADDRESS>"
}' | jq

In the response, you will find the following entries, which will show the unlock epoch and amount.

"pending_owner_stake_unit_withdrawals": [],
      "already_unlocked_owner_stake_unit_amount": "0"

Once the unlock epoch has elapsed, the final step is to finish the process:

Finish Owner Stake Unlock
Deposits unlocked owner stake LSUs back to an account

CALL_METHOD
    Address("<VALIDATOR ADDRESS>")
    "finish_unlock_owner_stake_units";

CALL_METHOD
  Address("<ACCOUNT TO DEPOSIT LSUs TO>")
  "deposit_batch"
  Expression("ENTIRE_WORKTOP");

Note that the unlocked LSUs are still staked, and will require unstaking if the desire is to redeem them back to XRD.

I hope that’s useful! If there are any problems, or requests for other manifests, please comment in the thread below :slightly_smiling_face:

EDIT - 25/01/2024
Protocol Update Readiness Signal

In readiness for the first coordinated protocol update on the Radix Babylon network, all validators are required to signal readiness. Unlike during Olympia where this signal was made through an API call to the node, at Babylon this is performed on-ledger using the following transaction manifest:

CALL_METHOD
    Address("<ACCOUNT ADDRESS WITH VALIDATOR BADGE>")
    "create_proof_of_non_fungibles"
    Address("resource_rdx1nfxxxxxxxxxxvdrwnrxxxxxxxxx004365253834xxxxxxxxxvdrwnr")
    Array<NonFungibleLocalId>(
        NonFungibleLocalId("<BADGE ID STARTING AND ENDING WITH []>")
    )
;

CALL_METHOD
    Address("<VALIDATOR ADDRESS>")
    "signal_protocol_update_readiness"
    "220e2a4a4e86e3e6000000000anemone"
;

EDIT - 17/05/2024
Protocol Update Readiness Signal

In readiness for the second coordinated protocol update (Bottlenose) on the Radix Babylon network, all validators are required to signal readiness. As per the coordinated update above, please use the following manifest to signal your readiness once your node has been updated:

CALL_METHOD
    Address("<ACCOUNT ADDRESS WITH VALIDATOR BADGE>")
    "create_proof_of_non_fungibles"
    Address("resource_rdx1nfxxxxxxxxxxvdrwnrxxxxxxxxx004365253834xxxxxxxxxvdrwnr")
    Array<NonFungibleLocalId>(
        NonFungibleLocalId("<BADGE ID STARTING AND ENDING WITH []>")
    )
;

CALL_METHOD
    Address("<VALIDATOR ADDRESS>")
    "signal_protocol_update_readiness"
    "86894b9104afb73a000000bottlenose"
;
15 Likes

This is awesome, thanks alot!

1 Like

This is great!
Thank you Faraz

1 Like

This is a awesome good Idea. Top :slight_smile:

1 Like

We are excited to introduce The Validator Manager !

1 Like