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