-
Notifications
You must be signed in to change notification settings - Fork 73
Implement sugar-sdk pool snapshot data pull #1393
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
Draft
dioptx
wants to merge
16
commits into
main
Choose a base branch
from
sugar_sdk_pull
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
e9bcda4
sugar pull poc
dioptx 724cbcf
Merge branch 'main' of github.com:ethereum-optimism/op-analytics into…
dioptx 87b93cc
Merge branch 'main' of github.com:ethereum-optimism/op-analytics into…
dioptx 5c514ae
implement changes for the first batch of comments
dioptx 9833369
Support the new sugar-sdk version
dioptx 3178240
update to support the new version
dioptx 21d3e76
rename the sugar class and tables
dioptx 0827f02
update sugarwrapper
dioptx 1c677ea
done goofed up
dioptx 4dfe114
Rename and refactor
dioptx 04895d1
Merge branch 'main' of github.com:ethereum-optimism/op-analytics into…
dioptx 46fe02c
Merge branch 'main' of github.com:ethereum-optimism/op-analytics into…
dioptx f8e3a67
Merge branch 'main' of github.com:ethereum-optimism/op-analytics into…
dioptx 3a87bb5
Merge branch 'main' of github.com:ethereum-optimism/op-analytics into…
dioptx 6ff6dd0
finalize implementation
dioptx 0f039c6
Merge branch 'main' of github.com:ethereum-optimism/op-analytics into…
dioptx File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
from op_analytics.coreutils.partitioned.dailydata import DailyDataset | ||
|
||
|
||
class Velodrome(DailyDataset): | ||
""" | ||
The Sugar dataset tracks tokens, pools, and prices from the Velodrome sugar-sdk. | ||
See also: | ||
- https://github.com/velodrome-finance/sugar | ||
- https://github.com/velodrome-finance/sugar-sdk | ||
|
||
Tables: | ||
- tokens_v1 | ||
- pools_v1 | ||
- prices_v1 | ||
""" | ||
|
||
TOKENS = "tokens_v1" | ||
POOLS = "pools_v1" | ||
PRICES = "prices_v1" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from sugar.chains import AsyncOPChain, AsyncBaseChain | ||
|
||
|
||
def chain_cls_to_str(chain_cls: type) -> str: | ||
""" | ||
Convert a chain class like OPChain → 'op' or BaseChain → 'base'. | ||
Extend this if you support more chains in future. | ||
""" | ||
name = chain_cls.__name__.lower() | ||
if "opchain" in name: | ||
return "op" | ||
elif "basechain" in name: | ||
return "base" | ||
# Fallback or raise an error for unhandled cases | ||
raise ValueError(f"Unrecognized chain class: {chain_cls.__name__}") | ||
|
||
|
||
# Map our chain names to the sugar sdk Chain class. | ||
SUGAR_CHAINS = { | ||
"op": AsyncOPChain(), | ||
"base": AsyncBaseChain(), | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from op_analytics.coreutils.partitioned.dailydata import DailyDataset | ||
|
||
|
||
class Velodrome(DailyDataset): | ||
TOKENS = "tokens_v1" | ||
POOLS = "liquidity_pools_v1" | ||
PRICES = "prices_v1" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import polars as pl | ||
|
||
from op_analytics.coreutils.logger import structlog | ||
from op_analytics.coreutils.partitioned.dailydatautils import dt_summary | ||
from op_analytics.datasources.velodrome.chain_list import SUGAR_CHAINS | ||
from op_analytics.datasources.velodrome.pools import fetch_pools_for_chain | ||
from op_analytics.datasources.velodrome.prices import fetch_prices_for_chain | ||
from op_analytics.datasources.velodrome.tokens import fetch_tokens_for_chain | ||
from .dataaccess import Velodrome | ||
|
||
log = structlog.get_logger() | ||
|
||
|
||
def _collect_data() -> dict[str, list[pl.DataFrame]]: | ||
""" | ||
Gather tokens, pools, and prices DataFrames for each chain in SUGAR_CHAINS. | ||
Returns a dict mapping "tokens", "pools", and "prices" to lists of DataFrames. | ||
""" | ||
all_data = {"tokens": [], "pools": [], "prices": []} | ||
|
||
for chain_name, chain_instance in SUGAR_CHAINS.items(): | ||
log.info("Fetching Velodrome data through sugar-sdk", chain=chain_name) | ||
|
||
chain_cls = type(chain_instance) | ||
chain_tokens_df = fetch_tokens_for_chain(chain_cls) | ||
chain_pools_df = fetch_pools_for_chain(chain_cls) | ||
chain_prices_df = fetch_prices_for_chain(chain_cls) | ||
|
||
all_data["tokens"].append(chain_tokens_df) | ||
all_data["pools"].append(chain_pools_df) | ||
all_data["prices"].append(chain_prices_df) | ||
|
||
return all_data | ||
|
||
|
||
def execute_pull() -> dict[str, dict]: | ||
""" | ||
Main entry point for Velodrome data ingestion logic. Fetches tokens, pools, and prices for each chain, | ||
|
||
Returns: | ||
dict: Summary of the data ingestion process. | ||
""" | ||
log.info("Starting Velodrome data ingestion") | ||
|
||
collected = _collect_data() | ||
summary = {} | ||
|
||
Velodrome.write_dataset(collected["tokens"], Velodrome.TOKENS) | ||
summary["tokens_df"] = dt_summary(collected["tokens"]) | ||
log.info("Tokens data written successfully", count=len(collected["tokens"])) | ||
|
||
Velodrome.write_dataset(collected["pools"], Velodrome.POOLS) | ||
summary["pools_df"] = dt_summary(collected["pools"]) | ||
log.info("Pools data written successfully", count=len(collected["pools"])) | ||
|
||
Velodrome.write_dataset(collected["prices"], Velodrome.PRICES) | ||
summary["prices_df"] = dt_summary(collected["prices"]) | ||
log.info("Prices data written successfully", count=len(collected["prices"])) | ||
|
||
log.info("Velodrome ingestion completed", summary=summary) | ||
return summary |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
from op_analytics.coreutils.logger import structlog | ||
import polars as pl | ||
from sugar.pool import LiquidityPool | ||
|
||
from op_analytics.datasources.velodrome.sugarwrapper import fetch_pools | ||
from op_analytics.datasources.velodrome.chain_list import chain_cls_to_str | ||
|
||
log = structlog.get_logger() | ||
|
||
POOLS_SCHEMA = { | ||
"chain": pl.Utf8, | ||
"lp": pl.Utf8, | ||
"factory": pl.Utf8, | ||
"symbol": pl.Utf8, | ||
"is_stable": pl.Boolean, | ||
"total_supply": pl.Float64, | ||
"decimals": pl.Int64, | ||
"token0": pl.Utf8, | ||
"token1": pl.Utf8, | ||
"pool_fee": pl.Float64, | ||
"gauge_total_supply": pl.Float64, | ||
"emissions_token": pl.Utf8, | ||
"nfpm": pl.Utf8, | ||
"alm": pl.Utf8, | ||
} | ||
|
||
|
||
def fetch_pools_for_chain(chain_cls: type) -> pl.DataFrame: | ||
""" | ||
Fetch pool data from sugarwrapper.py. We then map the returned LiquidityPool objects | ||
into a Polars DataFrame defined by POOLS_SCHEMA. | ||
""" | ||
chain_str = chain_cls_to_str(chain_cls) | ||
log.info(f"Fetching pools for {chain_str}") | ||
|
||
velodrome_pools = fetch_pools(chain_str) | ||
raw_pools = velodrome_pools.pools # list[LiquidityPool] | ||
|
||
pool_records = [] | ||
for lp in raw_pools: | ||
if not isinstance(lp, LiquidityPool): | ||
continue | ||
pool_records.append( | ||
{ | ||
"chain": chain_str, | ||
"lp": lp.lp, | ||
"symbol": lp.symbol, | ||
"factory": lp.factory, | ||
"is_stable": lp.is_stable, | ||
"total_supply": float(lp.total_supply), | ||
"decimals": int(lp.decimals), | ||
"token0": lp.token0.token_address if lp.token0 else "unknown", | ||
"token1": lp.token1.token_address if lp.token1 else "unknown", | ||
"pool_fee": float(lp.pool_fee), | ||
"gauge_total_supply": float(lp.gauge_total_supply), | ||
"emissions_token": lp.emissions_token.token_address | ||
if lp.emissions_token | ||
else "unknown", | ||
"nfpm": lp.nfpm, | ||
"alm": lp.alm, | ||
} | ||
) | ||
|
||
df = pl.DataFrame(pool_records, schema=POOLS_SCHEMA) | ||
log.info(f"{chain_str} returned {df.height} pools.") | ||
return df |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
from op_analytics.coreutils.logger import structlog | ||
import polars as pl | ||
|
||
from sugar.price import Price | ||
|
||
from op_analytics.datasources.velodrome.sugarwrapper import fetch_pools | ||
from op_analytics.datasources.velodrome.chain_list import chain_cls_to_str | ||
|
||
log = structlog.get_logger() | ||
|
||
PRICES_SCHEMA = { | ||
"chain": pl.Utf8, | ||
"token_address": pl.Utf8, | ||
"price": pl.Float64, | ||
} | ||
|
||
|
||
def fetch_prices_for_chain(chain_cls: type) -> pl.DataFrame: | ||
""" | ||
Fetch token prices for each chain by leveraging sugarwrapper.py's fetch_pools(). | ||
We then build a DataFrame from the returned Price objects. | ||
""" | ||
chain_str = chain_cls_to_str(chain_cls) | ||
log.info(f"Fetching prices for {chain_str} via sugarwrapper fetch_pools()") | ||
|
||
velodrome_pools = fetch_pools(chain_str) | ||
prices = velodrome_pools.prices # list[Price] | ||
|
||
price_records = [] | ||
for cp in prices: | ||
if not isinstance(cp, Price): | ||
continue | ||
price_records.append( | ||
{ | ||
"chain": chain_str, | ||
"token_address": cp.token.token_address, | ||
"price": cp.price, | ||
} | ||
) | ||
|
||
df = pl.DataFrame(price_records, schema=PRICES_SCHEMA) | ||
log.info(f"{chain_str} returned {df.height} prices.") | ||
return df |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
from op_analytics.coreutils.logger import structlog | ||
import polars as pl | ||
|
||
from op_analytics.datasources.velodrome.sugarwrapper import fetch_pools | ||
from op_analytics.datasources.velodrome.chain_list import chain_cls_to_str | ||
|
||
log = structlog.get_logger() | ||
|
||
TOKEN_SCHEMA = { | ||
"chain": pl.Utf8, | ||
"token_address": pl.Utf8, | ||
"symbol": pl.Utf8, | ||
"decimals": pl.Int64, | ||
"listed": pl.Boolean, | ||
} | ||
|
||
|
||
def fetch_tokens_for_chain(chain_cls: type) -> pl.DataFrame: | ||
""" | ||
Fetch token metadata for a single chain by leveraging sugarwrapper.py's fetch_pools(). | ||
Returns a Polars DataFrame matching TOKEN_SCHEMA. | ||
""" | ||
chain_str = chain_cls_to_str(chain_cls) | ||
log.info(f"Fetching tokens for {chain_str} via sugarwrapper fetch_pools()") | ||
|
||
# fetch_pools() returns a VelodromePools dataclass; we only need tokens here | ||
velodrome_pools = fetch_pools(chain_str) | ||
tokens = velodrome_pools.tokens # a list of sugar.token.Token objects | ||
|
||
token_records = [] | ||
for t in tokens: | ||
token_records.append( | ||
{ | ||
"chain": chain_str, | ||
"token_address": t.token_address, | ||
"symbol": t.symbol, | ||
"decimals": t.decimals, | ||
"listed": t.listed, # if the Token object has a 'listed' attribute | ||
} | ||
) | ||
|
||
df = pl.DataFrame(token_records, schema=TOKEN_SCHEMA) | ||
log.info(f"{chain_str} returned {df.height} tokens.") | ||
return df |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lithium323
We have a bunch of missing Token information for native tokens mostly. Not sure how to fix this yet.
Sample data:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I brought this up with the velodrome team on telegram. At first setting listed_only=False made all pools with missing tokens go away. We should check again with them if that isn't working anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tried with both "listed_only" options, and this issue persists. I think this requires patching on the sugar-sdk side.