Cover photo

self-hosted mean.finance Alerts with Telegram and Python

In this post, I want to share with you a project that I recently completed: a Telegram bot that alerts me when a position on mean.finance runs out of funds.

For those who may not be familiar with it, mean.finance is a decentralized finance (DeFi) platform that allows users to dollar-cost average (DCA) into cryptocurrency by automating purchases using smart contracts. In addition to DCA, mean.finance also allows users to earn yield on their idle cryptocurrency. However, these positions can sometimes run out of funds, which might be annoying since there is no direct notification system that informs you of that.

To solve this problem, I decided to write a Telegram bot that monitors the funds in my mean.finance positions and sends me a notification as soon as one of them becomes depleted. In this post, I'll explain how I built the bot and how you can set up a similar one for your own mean.finance positions.

I hope you find this project as interesting and useful as I do, and I look forward to hearing your thoughts and feedback in the comments section. Let's get started!

What you need:

  • Raspberry Pi or Server; something to run the python script on

  • a telegram bot instance; just follow step 2 of this tutorial. You should now have the token for the http API

  • your own telegram chat id: follow this tutorial to get it

The Code

always use the up to date version of the code: github

I will walk you through the code piece by piece, but don’t worry in the end there will be all to copy paste.

First we will import all necessary libraries, you can install them via pip install.

import web3
import json
import requests
from datetime import datetime as Datetime
import time

Than we will import all individual inputs

lastMessageTime = 0

#load telegram bot token
token = 0
with open("token.txt") as file:
    token = file.read()

#load the ABI from a JSON file
contract_abi = 0
with open("abi.json") as f:
    contract_abi = json.load(f)

#load position ID
positionID = 0
with open("positionID.txt") as file:
    positionID = int(file.read())

#load chat ID
chatID = 0
with open("chatID.txt") as file:
    chatID = file.read()
  • the bot token is the one obtained during the setup of your telegram bot instance

  • the ABI, to get it:

    • look up the smart contract address of the DCA Hub. You can find it in the docs of mean finance, it should be 0xA5AdC5484f9997fBF7D405b9AA62A7d88883C345 for all chains that mean.finance is deployed on

    • paste the address in the block explorer of the chain your position is on, for example polygonscan

    • click contract and scroll all the way down until you find the field “Contract ABI”

    • copy paste the content into a file called abi.json and make sure the file extension is .json

  • the position ID of the position you want to have alerts for

    • look it up using a block explorer or on mean.finance directly (go to your position and click view NFT).

  • the chat id we obtained earlier.

Then we will create our web3 instance and initiate the smart contract we will work with

# Set the HTTP endpoint for your Polygon node
w3 = web3.Web3(web3.Web3.HTTPProvider("https://polygon-rpc.com"))

# Set the contract address and ABI (Application Binary Interface)
contract_address = "0xA5AdC5484f9997fBF7D405b9AA62A7d88883C345"


# Instantiate the contract
contract = w3.eth.contract(address=contract_address, abi=contract_abi)

Now everything is set up and we can get to the loop that will run constantly

#loop

while(True):

    #get the current time
    currentTime = int(time.time())

    #check wether 4 hours have passed
    if currentTime > (lastMessageTime+(3600*4)): 
        lastMessageTime = currentTime
        # Call the "userPosition" function of the contract with the input "positionID"
        result = contract.functions.userPosition(positionID).call()

        #calculate how much time is left until last swap 
        swapsLeft = result[-3]
        secondsBetweenSwaps = result[2]
        roughlyTimeLeftSeconds = swapsLeft*secondsBetweenSwaps
        roughlyTimeLeftHours = swapsLeft*secondsBetweenSwaps/3600
        # Print the result
        print(swapsLeft)
        print(roughlyTimeLeftHours)



        outOfFundsTimestamp = int(time.time()) + roughlyTimeLeftSeconds
        datetime_obj = Datetime.utcfromtimestamp(outOfFundsTimestamp)

        print(str(datetime_obj.strftime("%d.%m.%y %H:%M:%S")))

        #check wether the last swap is less than 72 hours away
        if roughlyTimeLeftHours < 72:
            
            #text user on telegram
            messageString = str("you have only funds left for " + str(swapsLeft) + " swaps! You will run out of funds around " + str(datetime_obj.strftime("%d.%m.%y %H:%M:%S")))
            params = {"chat_id":chatID, "text":messageString}
            url = f"https://api.telegram.org/bot{token}/sendMessage"
            message = requests.post(url, params=params)
    #wait 60s before checking if 4 hours have passed
    time.sleep(60)

The code is currently configured to check every 60s if the refresh time of 4 hours is reached. If that is the case and your position will run out of funds in less than 72h it will send you a message every 4 hours!

You can run the code on whatever device you want. I use a Raspberry Pi, which works perfect for the job and draws very litle power.

Pls comment or reach out if you have any questions about the code or on how to get it running :)

As promised here is all the code to copy paste:

import web3
import json
import requests
from datetime import datetime as Datetime
import time

lastMessageTime = 0

#load telegram bot token
token = 0
with open("token.txt") as file:
    token = file.read()

#load the ABI from a JSON file
contract_abi = 0
with open("abi.json") as f:
    contract_abi = json.load(f)

#load position ID
positionID = 0
with open("positionID.txt") as file:
    positionID = int(file.read())

#load chat ID
chatID = 0
with open("chatID.txt") as file:
    chatID = file.read()


# Set the HTTP endpoint for your Polygon node
w3 = web3.Web3(web3.Web3.HTTPProvider("https://polygon-rpc.com"))

# Set the contract address and ABI (Application Binary Interface)
contract_address = "0xA5AdC5484f9997fBF7D405b9AA62A7d88883C345"


# Instantiate the contract
contract = w3.eth.contract(address=contract_address, abi=contract_abi)


#loop
while(True):

    #get the current time
    currentTime = int(time.time())

    #check wether 4 hours have passed
    if currentTime > (lastMessageTime+(3600*4)): 
        lastMessageTime = currentTime
        # Call the "userPosition" function of the contract with the input "positionID"
        result = contract.functions.userPosition(positionID).call()

        #calculate how much time is left until last swap 
        swapsLeft = result[-3]
        secondsBetweenSwaps = result[2]
        roughlyTimeLeftSeconds = swapsLeft*secondsBetweenSwaps
        roughlyTimeLeftHours = swapsLeft*secondsBetweenSwaps/3600
        # Print the result
        print(swapsLeft)
        print(roughlyTimeLeftHours)



        outOfFundsTimestamp = int(time.time()) + roughlyTimeLeftSeconds
        datetime_obj = Datetime.utcfromtimestamp(outOfFundsTimestamp)

        print(str(datetime_obj.strftime("%d.%m.%y %H:%M:%S")))

        #check wether the last swap is less than 72 hours away
        if roughlyTimeLeftHours < 72:
            
            #text user on telegram
            messageString = str("you have only funds left for " + str(swapsLeft) + " swaps! You will run out of funds around " + str(datetime_obj.strftime("%d.%m.%y %H:%M:%S")))
            params = {"chat_id":chatID, "text":messageString}
            url = f"https://api.telegram.org/bot{token}/sendMessage"
            message = requests.post(url, params=params)
    #wait 60s before checking if 4 hours have passed
    time.sleep(60)