Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incentives bot #5

Merged
merged 3 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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_REWARDS=
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_REWARDS,
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 .rewards import RewardsBot


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)
rewards_bot = RewardsBot(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),
rewards_bot.start(DISCORD_TOKEN_REWARDS),
)


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/rewards.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 RewardsBot(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 & {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_REWARDS = os.environ["DISCORD_TOKEN_REWARDS"]

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_rewards():
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