-
-
Save Erumite/98471f62e97d1f76981b01f2985ede25 to your computer and use it in GitHub Desktop.
#! /bin/bash | |
# -------- Config -------- | |
# WALLET_NAME should be the name of the wallet (chain-maind keys list) | |
WALLET_NAME="Default" | |
# If WALLET_ADDRESS is blank, it will try to pull your address. (chain-maind keys show ${WALLET_NAME} --address) | |
# This is my address, so replace it with your own. Feel free to send me a murder of CROs. | |
# NOTE: All transactions with my address will fail since you don't have my keys, of course. | |
WALLET_ADDRESS="cro1ghlngrfrvjjf7r2h6ffpwaqcs630qxm509p2el" | |
# VALIDATOR_IGNORE_REGEX is a Regular Expression that will ignore specific validators for re-delegating but | |
# will still withdraw rewards. This is useful if you accidentally staked to a high percentage validator and don't | |
# care to unstake/redelegate the CRO being held by them. These addresses will be listed in RED on the summary page. | |
VALIDATOR_IGNORE_REGEX="(crocncl1ewgpxu7ec2kdeu9cyze0xaufnwkkqkpwq7rmst|crocncl1h6350mf68lg52ll44t7whcnffhf3qcycxrqfu7)" | |
# GAS can be left at auto to detect required gas when performing mult-withdraw or staking operations. | |
# GAS_LIMIT is used when combining withdrawal + staking since it can't be auto-detected. Excess gas fees are returned. | |
GAS="auto" | |
GAS_LIMIT=350000 | |
# FEE_CLAIM and FEE_DELEGATE are fees to offer to the network when claiming rewards or delegating respectively. | |
# Values are in basecro (CRO/100000000) | |
# If the fee is insufficient, it asks to continue with the value suggested in the network response. | |
# I typically see below 6000 basecro fees per multi-withdraw and <4000 for delegation. | |
# This is considerably cheaper than 10000-20000basecro per withdrawal and delegation. | |
# If using combined withdraw+restake, these can't be 0 since it can't auto-detect. | |
FEE_CLAIM=5800 | |
FEE_DELEGATE=3900 | |
# Combine the above for a rough idea of the combined fees. This can be set manually as well. | |
FEE_COMBINED=$((${FEE_CLAIM} + ${FEE_DELEGATE})) | |
# If YES is --yes it will skip chain-maind confirmations when collecting rewards and redelegating. | |
#YES="--yes" | |
YES="" | |
# Optional Config | |
CHAIN="--chain-id crypto-org-chain-mainnet-1" | |
NODE="" # Use local full node. | |
NODE="--node https://mainnet.crypto.org:26657" # Use CDC's mainnet node (slower) | |
# -------- Shouldn't need editing below here. -------- | |
# Some colors because it helps me parse things visually. | |
RED=$(tput setaf 1) | |
GREEN=$(tput setaf 2) | |
YELLOW=$(tput setaf 3) | |
LIME_YELLOW=$(tput setaf 190) | |
POWDER_BLUE=$(tput setaf 153) | |
BLUE=$(tput setaf 4) | |
MAGENTA=$(tput setaf 5) | |
CYAN=$(tput setaf 6) | |
WHITE=$(tput setaf 7) | |
BOLD=$(tput bold) | |
NORMAL=$(tput sgr0) | |
UNDERLINE=$(tput smul) | |
# If the wallet address isn't configured, set it based on Wallet Name | |
[[ -z $WALLET_ADDRESS ]] && WALLET_ADDRESS=$($(which chain-maind) keys show ${WALLET_NAME} --address) | |
# Convert basecro to cro. Accepts raw numbers as well as "###basecro" "###,basecro" and "### basecro" | |
function basecro_to_cro() { | |
basecro=${*//\"/} | |
basecro=${basecro%% *} | |
basecro=${basecro%%,*} | |
echo ${basecro%basecro} | awk '{printf "%8f", $1/100000000}' | |
} | |
# Read current balance (Unstaked) | |
json=$(chain-maind ${CHAIN} ${NODE} query bank balances ${WALLET_ADDRESS} --output=json) | |
WALLET_BALANCE=$(echo "${json}"|jq -r ".balances[]|[.amount,.denom]|@tsv") | |
# Find Delegations to individual validators and do some math to get a total. | |
json=$(chain-maind ${CHAIN} ${NODE} query staking delegations ${WALLET_ADDRESS} --output json) | |
DELEGATIONS=$(echo "${json}" | \ | |
jq -r '.delegation_responses[]|[.delegation.validator_address,.delegation.shares,.balance.amount,.balance.denom]|@csv') | |
DELEGATIONS=${DELEGATIONS//\"/} | |
DELEGATION_TOTAL=$(echo "${DELEGATIONS}" | awk -F, '{t+=$2};END{printf "%d",t}') | |
# Get available rewards from each of the delegators and count up the total. | |
# Also generate a new total by combining withdrawable rewards with current balance. | |
# This is used to generate NEW_STAKE, which is what will be re-staked to the network. | |
unset rewards; declare -A rewards | |
unset REWARD_TOTAL; REWARD_TOTAL=0 | |
for d in ${DELEGATIONS}; do | |
v=${d%%,*} | |
r="$(chain-maind ${CHAIN} ${NODE} query distribution rewards ${WALLET_ADDRESS} ${v} --output=json |jq -r '.rewards[]|[.amount,.denom]|@tsv')" | |
rewards[${v}]=$r | |
done | |
REWARD_TOTAL=$(echo "${rewards[@]}" | awk '{ for (i=1; i<=NF; i++) t+=$i};END{printf "%d", t}') | |
NEW_TOTAL=$((${REWARD_TOTAL} + ${WALLET_BALANCE//basecro})) | |
NEW_STAKE=$(((${REWARD_TOTAL} + ${WALLET_BALANCE//basecro}) - 1000000)) | |
# Print off some information about the current wallet. | |
echo -e "\n${BOLD}${UNDERLINE}${YELLOW}${WALLET_NAME}${NORMAL} (${UNDERLINE}${WALLET_ADDRESS}${NORMAL})"; \ | |
echo -en "${GREEN}Bal: $(basecro_to_cro ${WALLET_BALANCE}) CRO${NORMAL}\t" ;\ | |
echo -en "${LIME_YELLOW}Stk: ${BOLD}$(basecro_to_cro ${DELEGATION_TOTAL}) CRO${NORMAL}\t" ;\ | |
echo -e "${CYAN}Rew: ${BOLD}$(basecro_to_cro ${REWARD_TOTAL})${NORMAL}\n" | |
# Find the smallest non-ignored validator which we'll delegate to later. | |
DELEGATION_TARGET=$(echo "${DELEGATIONS}"|sed -r "/${VALIDATOR_IGNORE_REGEX}/d"|sort -t, -k3n|head -n1|cut -d, -f1) | |
# Print off individual delegators, amount staked, and available rewards. | |
{ echo "${BOLD}${CYAN}Validator${NORMAL}@Staked Amount@${BOLD}Rewards${NORMAL}" | |
for d in ${DELEGATIONS} ; do | |
validator=${d%%,*} | |
[[ ${validator} =~ ${VALIDATOR_IGNORE_REGEX} ]] && validator="${BOLD}${RED}${validator}${NORMAL}" || validator="${BOLD}${GREEN}${validator}${NORMAL}" | |
staked=$(basecro_to_cro $(echo "${d}" | awk -F, '{printf "%d",$2}')) | |
reward="${BOLD}$(basecro_to_cro ${rewards[${d%%,*}]})${NORMAL}" | |
[[ ${d%%,*} == ${DELEGATION_TARGET} ]] && reward="${reward} ${BOLD}${POWDER_BLUE}<+> Delegation Target${NORMAL}" | |
echo ${validator}@${staked}@${reward} | |
done | sort -k2n -t@ ; } |column -t -s@ | |
# -------- Claim & Restake 1-TX -------- | |
# Print out some helpful info about what we'll be doing. | |
echo -e "\n\n${BOLD}${UNDERLINE}${CYAN}Claim all transactions and restake immediately?${NORMAL}" | |
echo -e "${BOLD}New Total: $(basecro_to_cro ${NEW_TOTAL})" | |
echo -e "${BOLD}New Stake: $(basecro_to_cro ${NEW_STAKE})" | |
echo -e "${BOLD}Stake To : ${DELEGATION_TARGET}\n" | |
# Set up some variables and confirm. Will continue to separeate withdraw/stake prompts if declined. | |
done=0 fee=$((${FEE_CLAIM}+${FEE_DELEGATE})) gas=${GAS_LIMIT} | |
read -p "Continue to withdraw and redelegate? [Y/n]: " | |
[[ ! $REPLY =~ ^[Yy] ]] && done=1 | |
# This is hacky since there's no real supported method of combining these that I know of. | |
# Essentially generate the message for staking based on the NEW_STAKE value and extract the relevant message. | |
# Then, generate a message for withdrawing all rewards, then append the message from the redelegation. | |
# Also, manually set the fee/gas limits in the json and save the transaction to a file. | |
# Finally, sign the transaction (password prompt) and broadcast the transaction to the network. | |
while [[ ${done} == 0 ]] ; do | |
json=$(chain-maind ${CHAIN} ${NODE} ${YES} tx staking delegate ${DELEGATION_TARGET} ${NEW_STAKE}basecro --from ${WALLET_ADDRESS} --generate-only) | |
delegation=$(echo "${json}" | jq '.body.messages[0]') | |
json=$(chain-maind ${CHAIN} ${NODE} ${YES} tx distribution withdraw-all-rewards --from ${WALLET_ADDRESS} --generate-only) | |
append_slot=$(echo "${json}"| jq ".body.messages|length") | |
tx=$(echo "${json}" | jq ".body.messages[${append_slot}]|=${delegation}|.auth_info.fee.amount[0]|={\"denom\":\"basecro\",\"amount\":\"${fee}\"}|.auth_info.fee.gas_limit|=${gas}") | |
tmpfile='/tmp/cro_restake.tx' | |
echo "${tx}" > ${tmpfile} | |
tx=$(chain-maind ${CHAIN} ${NODE} tx sign ${tmpfile} --from ${WALLET_ADDRESS}) | |
echo "${tx}" > ${tmpfile}.signed | |
chain-maind ${CHAIN} ${NODE} tx broadcast ${tmpfile}.signed | |
rm ${tmpfile} ${tmpfile}.signed | |
echo -e "${BOLD}${GREEN}Done broadcasting transaction. If you have a failure message, increase gas limit or fees.${NORMAL}" | |
exit 1 | |
done | |
# (If the combined withdraw/restake action wasn't taken, then offer to do them individually) | |
# ------- Withdraw all delegations -------- | |
# Withdraw all delegations in one transaction. | |
# If fee is empty, it will keep retrying with the value suggested in the response. | |
# This can take several attempts, so I tend to just set the fee to something slightly | |
# above the usual value I land on when doing the manual method. | |
# Gas can generally be left as auto in the config and it will auto-detect required. | |
echo | |
done=0 fee=${FEE_CLAIM} gas=${GAS} | |
read -p "Withdraw all delegations now? [Y/n]: " | |
[[ ! $REPLY =~ ^[Yy] ]] && done=1 | |
while [[ ${done} == 0 ]] ; do | |
response=$(chain-maind ${CHAIN} ${NODE} ${YES} tx distribution withdraw-all-rewards --from ${WALLET_ADDRESS} --gas ${gas} --fees ${fee}basecro) | |
if [[ ${response} =~ "insufficient fees" ]] ; then | |
fee=$(echo "${response}" | jq -r '.raw_log' | grep -oP "(?<=required: )[0-9]*(?=basecro)") | |
read -p "[Failed] : Retry with ${fee} fee? : " | |
[[ $REPLY =~ ^[Yy] ]] && done=0 || done=1 | |
echo | |
else | |
done=1 | |
fi | |
done | |
# ---- Redelegate balance of Wallet, keeping .01 CRO for later ---- | |
# Print off some info about the new balance and stake to be delegated. | |
json=$(chain-maind ${CHAIN} ${NODE} query bank balances ${WALLET_ADDRESS} --output=json) | |
WALLET_BALANCE=$(echo "${json}"|jq -r ".balances[]|[.amount,.denom]|@tsv") | |
NEW_STAKE=$((${WALLET_BALANCE//basecro/} - 1000000)) | |
echo -e "\n${BOLD}${UNDERLINE}${YELLOW}${WALLET_NAME}${NORMAL} [${UNDERLINE}${WALLET_ADDRESS}${NORMAL}]"; \ | |
echo -en "${GREEN}Bal: ${BOLD}$(basecro_to_cro ${WALLET_BALANCE}) CRO${NORMAL}\t" ;\ | |
echo -en "${LIME_YELLOW}Stk: ${BOLD}$(basecro_to_cro ${DELEGATION_TOTAL}) CRO${NORMAL}\t" ; \ | |
echo -e "${RED}NewStk: ${BOLD}$(basecro_to_cro ${NEW_STAKE}) CRO${NORMAL}\n" | |
# Confirm if we want to continue or not. | |
read -p "Redelegate ${BOLD}$(basecro_to_cro ${NEW_STAKE})${NORMAL} to ${UNDERLINE}${DELEGATION_TARGET}${NORMAL}? [Y/n]: " | |
[[ ! $REPLY =~ ^[Yy] ]] && exit 0 | |
# Pretty much the same as the claim function, though fees tend to be lower. | |
done=0 fee=${FEE_DELEGATE} gas=${GAS} | |
while [[ ${done} == 0 ]] ; do | |
response=$(chain-maind ${CHAIN} ${NODE} ${YES} tx staking delegate ${DELEGATION_TARGET} ${NEW_STAKE}basecro --from ${WALLET_ADDRESS} --gas ${gas} --fees ${fee}basecro) | |
if [[ ${response} =~ "insufficient fees" ]] ; then | |
fee=$(echo "${response}" | jq -r '.raw_log' | grep -oP "(?<=required: )[0-9]*(?=basecro)") | |
read -p "[Failed] : Retry with ${fee} fee? : " | |
[[ $REPLY =~ ^[Yy] ]] && done=0 || done=1 | |
echo | |
else | |
done=1 | |
fi | |
done | |
exit 0 |
Greate script, thank you!!
Trying to run as a schedule but it keeps asking me for the passphrase. Is there a way to pass the passphrase to chain-maind or not to have to type it ever?
It's possible with the --keyring-backend
flag shown here: https://crypto.org/docs/wallets/cli.html#the-keyring-keyring-backend-option
I usually run this on the linux subsystem for windows and it asks for a password every time, though if I try on Mac, it seems to store my password in the keyring and doesn't require a password.
It's supposed to work the same on Linux, but I couldn't get libsecret
to work. :/ The other option is to use --keyring-backend test
but I don't like the fact that it stores the keys in an unencrypted file, though if it's a risk you're willing to take, that's an option.
Yes, I also use WSL and the test backend was the only way I found for it to work.
Older version for reference.