Skip to content

Commit

Permalink
Merge branch 'main' into service_fee_log
Browse files Browse the repository at this point in the history
  • Loading branch information
harisang authored Nov 10, 2024
2 parents 7b725ee + 59cb592 commit 0a45c82
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 76 deletions.
35 changes: 15 additions & 20 deletions src/fetch/payouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
from dune_client.types import Address
from pandas import DataFrame, Series

from src.abis.load import WETH9_ADDRESS
from src.constants import COW_TOKEN_ADDRESS, COW_BONDING_POOL
from src.fetch.dune import DuneFetcher
from src.fetch.prices import eth_in_token, TokenId, token_in_eth
from src.fetch.prices import exchange_rate_atoms
from src.models.accounting_period import AccountingPeriod
from src.models.overdraft import Overdraft
from src.models.token import Token
Expand Down Expand Up @@ -269,7 +270,6 @@ class TokenConversion:
"""

eth_to_token: Callable
token_to_eth: Callable


def extend_payment_df(pdf: DataFrame, converter: TokenConversion) -> DataFrame:
Expand Down Expand Up @@ -407,19 +407,10 @@ def construct_payout_dataframe(
normalize_address_field(service_fee_df, join_column)

# 3. Merge the three dataframes (joining on solver)

reward_target_reduced_df_columns = [
x for x in list(reward_target_df.columns) if x != "solver_name"
]
reward_target_reduced_df = reward_target_df[reward_target_reduced_df_columns]
service_fee_reduced_df_columns = [
x for x in list(service_fee_df.columns) if x != "solver_name"
]
service_fee_reduced_df = service_fee_df[service_fee_reduced_df_columns]
merged_df = (
payment_df.merge(slippage_df, on=join_column, how="left")
.merge(reward_target_reduced_df, on=join_column, how="left")
.merge(service_fee_reduced_df, on=join_column, how="left")
.merge(reward_target_df, on=join_column, how="left")
.merge(service_fee_df, on=join_column, how="left")
)

# 4. Add slippage from fees to slippage
Expand Down Expand Up @@ -474,9 +465,6 @@ def construct_payouts(
"""Workflow of solver reward payout logic post-CIP27"""
# pylint: disable-msg=too-many-locals

price_day = dune.period.end - timedelta(days=1)
reward_token = TokenId.COW

quote_rewards_df = orderbook.get_quote_rewards(dune.start_block, dune.end_block)
batch_rewards_df = orderbook.get_solver_rewards(dune.start_block, dune.end_block)
partner_fees_df = batch_rewards_df[["partner_list", "partner_fee_eth"]]
Expand Down Expand Up @@ -512,15 +500,22 @@ def construct_payouts(
# TODO - After CIP-20 phased in, adapt query to return `solver` like all the others
slippage_df = slippage_df.rename(columns={"solver_address": "solver"})

reward_token = COW_TOKEN_ADDRESS
native_token = Address(WETH9_ADDRESS)
price_day = dune.period.end - timedelta(days=1)
converter = TokenConversion(
eth_to_token=lambda t: exchange_rate_atoms(
native_token, reward_token, price_day
)
* t,
)

complete_payout_df = construct_payout_dataframe(
# Fetch and extend auction data from orderbook.
payment_df=extend_payment_df(
pdf=merged_df,
# provide token conversion functions (ETH <--> COW)
converter=TokenConversion(
eth_to_token=lambda t: eth_in_token(reward_token, t, price_day),
token_to_eth=lambda t: token_in_eth(reward_token, t, price_day),
),
converter=converter,
),
# Dune: Fetch Solver Slippage & Reward Targets
slippage_df=slippage_df,
Expand Down
41 changes: 18 additions & 23 deletions src/fetch/prices.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
import logging.config
from datetime import datetime
from enum import Enum
from fractions import Fraction

from coinpaprika import client as cp
from dune_client.types import Address

from src.constants import LOG_CONFIG_FILE

Expand Down Expand Up @@ -41,32 +43,25 @@ def decimals(self) -> int:
return 18


def eth_in_token(quote_token: TokenId, amount: int, day: datetime) -> int:
"""
Compute how much of `token` is equivalent to `amount` ETH on `day`.
Use current price if day not specified.
"""
eth_amount_usd = token_in_usd(TokenId.ETH, amount, day)
quote_price_usd = token_in_usd(quote_token, 10 ** quote_token.decimals(), day)
return int(eth_amount_usd / quote_price_usd * 10 ** quote_token.decimals())


def token_in_eth(token: TokenId, amount: int, day: datetime) -> int:
"""
The inverse of eth_in_token;
how much ETH is equivalent to `amount` of `token` on `day`
"""
token_amount_usd = token_in_usd(token, amount, day)
eth_price_usd = token_in_usd(TokenId.ETH, 10 ** TokenId.ETH.decimals(), day)
TOKEN_ADDRESS_TO_ID = {
Address("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"): TokenId.ETH,
Address("0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB"): TokenId.COW,
Address("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"): TokenId.USDC,
}

return int(token_amount_usd / eth_price_usd * 10 ** TokenId.ETH.decimals())


def token_in_usd(token: TokenId, amount_wei: int, day: datetime) -> float:
"""
Converts token amount [wei] to usd amount on given day.
def exchange_rate_atoms(
token_1_address: Address, token_2_address: Address, day: datetime
) -> Fraction:
"""Exchange rate for converting tokens on a given day.
The convention for the exchange rate r is as follows:
x atoms of token 1 have the same value as x * r atoms of token 2.
"""
return float(amount_wei) * usd_price(token, day) / 10.0 ** token.decimals()
token_1 = TOKEN_ADDRESS_TO_ID[token_1_address]
token_2 = TOKEN_ADDRESS_TO_ID[token_2_address]
price_1 = Fraction(usd_price(token_1, day)) / Fraction(10 ** token_1.decimals())
price_2 = Fraction(usd_price(token_2, day)) / Fraction(10 ** token_2.decimals())
return price_1 / price_2


@functools.cache
Expand Down
56 changes: 26 additions & 30 deletions tests/e2e/test_prices.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import unittest
from datetime import datetime, timedelta

from dune_client.types import Address

from src.abis.load import WETH9_ADDRESS
from src.constants import COW_TOKEN_ADDRESS
from src.fetch.prices import (
eth_in_token,
TOKEN_ADDRESS_TO_ID,
TokenId,
token_in_eth,
token_in_usd,
exchange_rate_atoms,
usd_price,
)

Expand All @@ -19,48 +22,41 @@ def setUp(self) -> None:
self.cow_price = usd_price(TokenId.COW, self.some_date)
self.eth_price = usd_price(TokenId.ETH, self.some_date)
self.usdc_price = usd_price(TokenId.USDC, self.some_date)
self.cow_address = COW_TOKEN_ADDRESS
self.weth_address = Address(WETH9_ADDRESS)
self.usdc_address = Address("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48")

def test_usd_price(self):
self.assertEqual(self.usdc_price, 1.001622)
self.assertEqual(self.eth_price, 2481.89)
self.assertEqual(self.cow_price, 0.194899)

def test_token_in_usd(self):
def test_exchange_rate_atoms(self):
with self.assertRaises(AssertionError):
token_in_usd(TokenId.COW, ONE_ETH, datetime.today())
exchange_rate_atoms(self.cow_address, self.weth_address, datetime.today())

self.assertEqual(
token_in_usd(TokenId.ETH, ONE_ETH, self.some_date), self.eth_price
)
self.assertEqual(
token_in_usd(TokenId.COW, ONE_ETH, self.some_date), self.cow_price
exchange_rate_atoms(self.cow_address, self.cow_address, self.some_date), 1
)
self.assertEqual(
token_in_usd(TokenId.USDC, 10**6, self.some_date), self.usdc_price
exchange_rate_atoms(self.cow_address, self.weth_address, self.some_date),
1
/ exchange_rate_atoms(self.weth_address, self.cow_address, self.some_date),
)

def test_eth_in_token(self):
self.assertAlmostEqual(
eth_in_token(TokenId.COW, ONE_ETH, self.some_date) / 10**18,
self.eth_price / self.cow_price,
delta=DELTA,
)
self.assertAlmostEqual(
eth_in_token(TokenId.USDC, ONE_ETH, self.some_date) / 10**6,
self.eth_price / self.usdc_price,
delta=DELTA,
self.assertEqual(
float(
exchange_rate_atoms(self.cow_address, self.weth_address, self.some_date)
),
self.cow_price / self.eth_price,
)

def test_token_in_eth(self):
self.assertAlmostEqual(
token_in_eth(TokenId.COW, ONE_ETH, self.some_date),
10**18 * self.cow_price // self.eth_price,
delta=DELTA,
)
self.assertAlmostEqual(
token_in_eth(TokenId.USDC, 10**6, self.some_date),
10**18 * self.usdc_price // self.eth_price,
delta=DELTA,
self.assertEqual(
float(
exchange_rate_atoms(self.cow_address, self.usdc_address, self.some_date)
)
* 10**18,
self.cow_price / self.usdc_price * 10**6,
)

def test_price_cache(self):
Expand Down
4 changes: 1 addition & 3 deletions tests/unit/test_payouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,7 @@ def setUp(self) -> None:
0.0,
]
# Mocking TokenConversion!
self.mock_converter = TokenConversion(
eth_to_token=lambda t: int(t * 1000), token_to_eth=lambda t: t // 1000
)
self.mock_converter = TokenConversion(eth_to_token=lambda t: int(t * 1000))

def test_extend_payment_df(self):
base_data_dict = {
Expand Down

0 comments on commit 0a45c82

Please sign in to comment.