├── requirements.txt ├── crontab ├── docker-compose.yml ├── .env.example ├── dockerfile ├── README.md └── src └── send_minimum_tx.py /requirements.txt: -------------------------------------------------------------------------------- 1 | py-algorand-sdk==1.4.1 2 | -------------------------------------------------------------------------------- /crontab: -------------------------------------------------------------------------------- 1 | # 0 */12 * * * Every 12 hours 2 | 0 */6 * * * python3 /src/send_minimum_tx.py >> /var/log/send_minimum_tx.log 2>&1 3 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | 3 | services: 4 | algorand-compound: 5 | build: ./ 6 | env_file: ./.env 7 | image: algorand-compound 8 | restart: "always" -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | ALGOD_PURESTAKE_TOKEN=<> 2 | ALGO_FROM_SEED="<< Seed from your temporal Algo wallet>>" 3 | ALGO_TO_ADDRESS=<> 4 | -------------------------------------------------------------------------------- /dockerfile: -------------------------------------------------------------------------------- 1 | # set base image (host OS) 2 | FROM python:3.8-alpine 3 | LABEL maintainer="andres.salazar.spinetti@gmail.com" 4 | 5 | RUN apk --no-cache --update add build-base libffi-dev musl-dev 6 | 7 | # set the working directory in the container 8 | WORKDIR /src 9 | 10 | # copy the dependencies file to the working directory 11 | COPY requirements.txt . 12 | COPY ./crontab /etc/crontabs/root 13 | 14 | # install dependencies 15 | RUN pip install -r requirements.txt 16 | 17 | # copy the content of the local src directory to the working directory 18 | COPY src/ . 19 | 20 | # command to run on container start 21 | CMD ["crond", "-f", "-d", "8"] 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Algorand automate claim rewards 2 | A docker-compose project that will help you automate claiming your Algorand stake rewards. For this repo, I used the basic examples provided by the Algorand developer team. 3 | 4 | By default, a cron job will run every 6 hours when you start the docker container transferring a minimum amount of Algo to the address you want to claim your stake rewards. 5 | ## Requirements 6 | - You will need to install [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/install/) if you don't have them already. 7 | - Open an account under [PureStake](https://developer.purestake.io/) and retrieve an API token. 8 | 9 | ## Installation 10 | 1. Clone the project. 11 | 2. Copy the `.env.example` file and call it `.env`. 12 | 3. Create a temporal Algorand Wallet, save the seed, and transfer just a few Algo. We will use the seed for the next step. 13 | 4. Fill the env variables inside `.env` with the required info. (**Warning:** Do not use the seed of the account you want to claim the rewards, use the temporal one we created above) 14 | 15 | ## Usage 16 | 17 | ```python 18 | docker-compose up -d 19 | ``` 20 | Once you run it, by default the docker container will start every time you start the system and it will run in the background. 21 | 22 | ## Optional 23 | - If you need to change the interval of the transactions, you can do it under the crontab file. Just make sure to rebuild your docker image with `docker-compose build --no-cache` and then use `docker-compose up -d` again. 24 | -------------------------------------------------------------------------------- /src/send_minimum_tx.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from algosdk import encoding 4 | from algosdk import transaction 5 | from algosdk import kmd 6 | from algosdk.v2client import algod 7 | from algosdk import account 8 | from algosdk import mnemonic 9 | 10 | # Setup HTTP client w/guest key provided by PureStake 11 | algod_purestake_address = "https://mainnet-algorand.api.purestake.io/ps2" 12 | algod_purestake_token = os.environ['ALGOD_PURESTAKE_TOKEN'] 13 | purestake_token_header = {"X-API-Key": algod_purestake_token} 14 | 15 | # Warning: Only use a seed with just a few algo, since it's going to be used as a way to generate a txn. Never use the seed where your hold your Algo. 16 | mnemonic_phrase = os.environ['ALGO_FROM_SEED'] 17 | account_private_key = mnemonic.to_private_key(mnemonic_phrase) 18 | account_public_key = mnemonic.to_public_key(mnemonic_phrase) 19 | 20 | algodclient = algod.AlgodClient(algod_purestake_token, algod_purestake_address, headers=purestake_token_header) 21 | 22 | # get suggested parameters from Algod 23 | 24 | params = algodclient.suggested_params() 25 | gh = params.gh 26 | first_valid_round = params.first 27 | last_valid_round = params.last 28 | fee = params.min_fee 29 | send_amount = 0 30 | 31 | existing_account = account_public_key 32 | 33 | # The address that needs to claim the stake rewards 34 | send_to_address = os.environ['ALGO_TO_ADDRESS'] 35 | 36 | # Create and sign transaction 37 | tx = transaction.PaymentTxn( 38 | existing_account, 39 | fee, 40 | first_valid_round, 41 | last_valid_round, 42 | gh, 43 | send_to_address, 44 | send_amount, 45 | flat_fee=True, 46 | ) 47 | signed_tx = tx.sign(account_private_key) 48 | 49 | 50 | # Function from Algorand Inc. 51 | def wait_for_confirmation(client, txid): 52 | """ 53 | Utility function to wait until the transaction is 54 | confirmed before proceeding. 55 | """ 56 | last_round = client.status().get("last-round") 57 | txinfo = client.pending_transaction_info(txid) 58 | while not (txinfo.get("confirmed-round") and txinfo.get("confirmed-round") > 0): 59 | print("Waiting for confirmation") 60 | last_round += 1 61 | client.status_after_block(last_round) 62 | txinfo = client.pending_transaction_info(txid) 63 | print( 64 | "Transaction {} confirmed in round {}.".format( 65 | txid, txinfo.get("confirmed-round") 66 | ) 67 | ) 68 | return txinfo 69 | 70 | 71 | try: 72 | tx_confirm = algodclient.send_transaction(signed_tx) 73 | wait_for_confirmation(algodclient, txid=signed_tx.transaction.get_txid()) 74 | except Exception as e: 75 | print(e) 76 | --------------------------------------------------------------------------------