Skip to content

Instantly share code, notes, and snippets.

@banteg

banteg/ethena.py Secret

Created April 10, 2024 23:06
Show Gist options
  • Save banteg/7c75fa2d4ebc371e5a3969cbcb6ddc24 to your computer and use it in GitHub Desktop.
Save banteg/7c75fa2d4ebc371e5a3969cbcb6ddc24 to your computer and use it in GitHub Desktop.
from pathlib import Path
import cryo
import hvplot
import polars as pl
from ape import Contract, chain, networks
from eth_utils import encode_hex, keccak
from rich.console import Console
names = {
"DAI": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"USDT": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"USDC": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"FRAX": "0x853d955aCEf822Db058eb8505911ED77F175b99e",
"USDe": "0x4c9EDD5852cd905f086C759E8383e09bff1E68B3",
"FDUSD": "0xc5f0f7b66764F6ec8C8Dff7BA683102295E16409",
}
console = Console()
print = console.log
def get_deploy_block(address):
resp = chain.provider._make_request("ots_getContractCreator", [address])
return chain.provider.web3.eth.get_transaction(resp["hash"]).blockNumber
def decode_amount(data: pl.Series):
return data.map_elements(lambda x: float(int.from_bytes(x, "big")), pl.Float64)
def obtain_data():
for name, address in names.items():
print(name, address)
contract = Contract(address)
deploy = get_deploy_block(address)
match name:
case "USDT":
issue = keccak(text=contract.Issue.abi.selector)
redeem = keccak(text=contract.Redeem.abi.selector)
topics = {
"mint": [issue],
"burn": [redeem],
}
case _:
transfer = keccak(text=contract.Transfer.abi.selector)
topics = {
"mint": [transfer, bytes(32)],
"burn": [transfer, None, bytes(32)],
}
for topic_name, topic_data in topics.items():
print(topic_name)
topic_data = topic_data + [None for _ in range(4 - len(topic_data))]
topic_data = {
f"topic{n}": [encode_hex(topic_data[n])]
for n in range(4)
if topic_data[n]
}
print(topic_data)
out_dir = Path(f"data/{name.lower()}/{topic_name}")
out_dir.mkdir(parents=True, exist_ok=True)
cryo.freeze(
["logs"],
blocks=[f"{deploy//10_000*10_000}:"],
align=True,
compression=["zstd", "3"],
contract=[address],
chunk_size=10_000,
output_dir=str(out_dir),
rpc=chain.provider.uri,
**topic_data,
)
def parse_data():
dfs = []
for name, address in names.items():
print(f"parsing {name}")
contract = Contract(address)
scale = 10 ** contract.decimals()
inner_dfs = []
for side in ["mint", "burn"]:
files = Path("data").glob(f"{name.lower()}/{side}/*.parquet")
sign = 1 if side == "mint" else -1
inner_dfs.append(
pl.scan_parquet(files)
.with_columns(
delta=(
pl.col("data").map_batches(decode_amount) / float(scale) * sign
),
name=pl.lit(name),
)
.select(["block_number", "delta", "name"])
)
df = (
pl.concat(inner_dfs)
.sort("block_number")
.with_columns(supply=pl.cum_sum("delta"))
)
dfs.append(df)
blocks = pl.scan_parquet("data/blocks/*.parquet")
supplies = pl.concat(dfs)
base = (
supplies.join(blocks, left_on="block_number", right_on="number", how="left")
.select(["block_number", "timestamp", "delta", "supply", "name"])
.with_columns(pl.from_epoch("timestamp"))
.collect()
)
uptos = []
for name in names:
wen = (
base.filter((pl.col("name") == name) & (pl.col("supply") >= 2e9))
.sort("block_number")
.item(0, "block_number")
)
upto = (
base.filter((pl.col("name") == name) & (pl.col("block_number") <= wen))
.sort("timestamp")
.group_by_dynamic("timestamp", every="1d")
.agg(pl.sum("delta"), pl.last("supply"), pl.last("name"))
.with_columns(
lifespan=(
pl.col("timestamp") - pl.col("timestamp").first()
).dt.total_days()
)
)
uptos.append(upto)
return pl.concat(uptos)
def main():
obtain_data()
df = parse_data()
print(df)
plot_a = df.plot.step(x="timestamp", y="supply", by="name")
plot_b = df.plot.step(x="lifespan", y="supply", by="name")
hvplot.save(plot_a, "ethena_timestamp.html")
hvplot.save(plot_b, "ethena_lifespan.html")
if __name__ == "__main__":
with networks.parse_network_choice("ethereum:mainnet:geth"):
main()
console.rule()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment