Skip to content

Commit

Permalink
add incentives bot
Browse files Browse the repository at this point in the history
  • Loading branch information
callmephilip committed Nov 8, 2023
1 parent a86cb36 commit 49aad14
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 4 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
DISCORD_TOKEN_PRICING=
DISCORD_TOKEN_TVL=
DISCORD_TOKEN_FEES=
DISCORD_TOKEN_INCENTIVES=
WEB3_PROVIDER_URI=https://mainnet.optimism.io
PROTOCOL_NAME=Velodrome
LP_SUGAR_ADDRESS=0xa1F09427fa89b92e9B4e4c7003508C8614F19791
Expand Down
4 changes: 4 additions & 0 deletions bots/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
DISCORD_TOKEN_PRICING,
DISCORD_TOKEN_TVL,
DISCORD_TOKEN_FEES,
DISCORD_TOKEN_INCENTIVES,
TOKEN_ADDRESS,
STABLE_TOKEN_ADDRESS,
PROTOCOL_NAME,
Expand All @@ -14,6 +15,7 @@
from .price import PriceBot
from .tvl import TVLBot
from .fees import FeesBot
from .incentives import IncentivesBot


async def main():
Expand All @@ -30,11 +32,13 @@ async def main():
price_bot = PriceBot(source_token=token, target_token=stable)
tvl_bot = TVLBot(protocol_name=PROTOCOL_NAME)
fees_bot = FeesBot(protocol_name=PROTOCOL_NAME)
incentives_bot = IncentivesBot(protocol_name=PROTOCOL_NAME)

await asyncio.gather(
price_bot.start(DISCORD_TOKEN_PRICING),
fees_bot.start(DISCORD_TOKEN_FEES),
tvl_bot.start(DISCORD_TOKEN_TVL),
incentives_bot.start(DISCORD_TOKEN_INCENTIVES),
)


Expand Down
57 changes: 57 additions & 0 deletions bots/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,3 +270,60 @@ def total_fees(self) -> float:
result += self.token1_fees.amount_in_stable

return result


@dataclass(frozen=True)
class LiquidityPoolEpoch:
"""Data class for Liquidity Pool
based on: https://github.com/velodrome-finance/sugar/blob/v2/contracts/LpSugar.vy#L69
"""

pool_address: str
bribes: List[Amount]
fees: List[Amount]

@classmethod
async def fetch_latest(cls):
tokens = await Token.get_all_listed_tokens()
prices = await Price.get_prices(tokens)

prices = {price.token.token_address: price for price in prices}
tokens = {t.token_address: t for t in tokens}

sugar = w3.eth.contract(address=LP_SUGAR_ADDRESS, abi=LP_SUGAR_ABI)
pool_epochs = await sugar.functions.epochsLatest(
GOOD_ENOUGH_PAGINATION_LIMIT, 0
).call()

result = []

for pe in pool_epochs:
pool_address, bribes, fees = pe[1], pe[4], pe[5]

bribes = list(
filter(
lambda b: b is not None,
map(lambda b: Amount.build(b[0], b[1], tokens, prices), bribes),
)
)
fees = list(
filter(
lambda f: f is not None,
map(lambda f: Amount.build(f[0], f[1], tokens, prices), fees),
)
)

result.append(
LiquidityPoolEpoch(pool_address=pool_address, bribes=bribes, fees=fees)
)

return result

@property
def total_fees(self) -> float:
return sum(map(lambda fee: fee.amount_in_stable, self.fees))

@property
def total_bribes(self) -> float:
return sum(map(lambda bribe: bribe.amount_in_stable, self.bribes))
5 changes: 5 additions & 0 deletions bots/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ def chunk(list_to_chunk: List, n: int):
yield list_to_chunk[i : i + n]


def amount_to_k_string(amount: float) -> str:
"""Turns 2000 to "2K" """
return f"{round(amount/1000, 2)}K"


# logging
LOGGING_LEVEL = os.getenv("LOGGING_LEVEL", "DEBUG")
LOGGING_HANDLER = logging.StreamHandler(sys.stdout)
Expand Down
33 changes: 33 additions & 0 deletions bots/incentives.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from discord.ext import tasks

from .settings import BOT_TICKER_INTERVAL_MINUTES
from .data import LiquidityPoolEpoch
from .helpers import LOGGER, amount_to_k_string
from .ticker import TickerBot


class IncentivesBot(TickerBot):
def __init__(self, *args, protocol_name: str, **kwargs):
super().__init__(*args, **kwargs)
self.protocol_name = protocol_name

async def on_ready(self):
LOGGER.debug(f"Logged in as {self.user} (ID: {self.user.id})")
LOGGER.debug("------")
await self.update_presence(f"incentives for {self.protocol_name}")

@tasks.loop(seconds=BOT_TICKER_INTERVAL_MINUTES * 60)
async def ticker(self):
try:
lpes = await LiquidityPoolEpoch.fetch_latest()
fees = sum(map(lambda lpe: lpe.total_fees, lpes))
bribes = sum(map(lambda lpe: lpe.total_bribes, lpes))

await self.update_nick_for_all_servers(
f"Rewards: {amount_to_k_string(fees + bribes)}"
)
await self.update_presence(
f"{amount_to_k_string(fees)} fees and {amount_to_k_string(bribes)} incentives"
)
except Exception as ex:
LOGGER.error(f"Ticker failed with {ex}")
2 changes: 2 additions & 0 deletions bots/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
DISCORD_TOKEN_PRICING = os.environ["DISCORD_TOKEN_PRICING"]
DISCORD_TOKEN_TVL = os.environ["DISCORD_TOKEN_TVL"]
DISCORD_TOKEN_FEES = os.environ["DISCORD_TOKEN_FEES"]
DISCORD_TOKEN_INCENTIVES = os.environ["DISCORD_TOKEN_INCENTIVES"]

WEB3_PROVIDER_URI = os.environ["WEB3_PROVIDER_URI"]
LP_SUGAR_ADDRESS = os.environ["LP_SUGAR_ADDRESS"]
PRICE_ORACLE_ADDRESS = os.environ["PRICE_ORACLE_ADDRESS"]
Expand Down
5 changes: 2 additions & 3 deletions bots/ticker.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@ async def update_nick_for_all_servers(self, nick: str):
async def update_presence(self, presence_text: str):
# https://discordpy.readthedocs.io/en/latest/api.html#discord.ActivityType
await self.change_presence(
activity=discord.Activity(
name=presence_text, type=discord.ActivityType.watching
)
# XX: emoji does not work for some reason
activity=discord.CustomActivity(name=presence_text, emoji="😀")
)

@tasks.loop(seconds=BOT_TICKER_INTERVAL_MINUTES * 60)
Expand Down
12 changes: 11 additions & 1 deletion tests/test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
load_dotenv(".env.example")

from bots.settings import TOKEN_ADDRESS # noqa
from bots.data import Token, Price, LiquidityPool # noqa
from bots.data import Token, Price, LiquidityPool, LiquidityPoolEpoch # noqa


@pytest.mark.asyncio
Expand Down Expand Up @@ -35,3 +35,13 @@ async def test_fees():
pools = await LiquidityPool.get_pools()
fees = sum(map(lambda p: p.total_fees, pools))
assert fees != 0


@pytest.mark.asyncio
async def test_incentives():
lpes = await LiquidityPoolEpoch.fetch_latest()
fees = sum(map(lambda lpe: lpe.total_fees, lpes))
bribes = sum(map(lambda lpe: lpe.total_bribes, lpes))

assert fees != 0
assert bribes != 0

0 comments on commit 49aad14

Please sign in to comment.