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

Treasury and wallets #183

Closed
wants to merge 89 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
255a46b
update tvl dashboard (#181)
DarkGhost7 Dec 17, 2021
8c22494
feat: add arbitrum start date to tvl exporter
banteg Dec 17, 2021
349e612
feat: change db schema to use relationships
banteg Dec 17, 2021
b44d474
update tvl dashboard (#181)
DarkGhost7 Dec 17, 2021
cba3a64
feat: add tx exporter
BobTheBuidler Dec 17, 2021
1149796
feat: add skeleton
BobTheBuidler Dec 17, 2021
9d503ef
fix: temporarily disable victoria
BobTheBuidler Dec 17, 2021
b2301ee
feat: add total users
BobTheBuidler Dec 17, 2021
23204af
feat: count wallets by # vaults used
BobTheBuidler Dec 17, 2021
6ec8213
feat: add total users by vault
BobTheBuidler Dec 17, 2021
f5511e4
fix: use Decimal instead of float
BobTheBuidler Dec 17, 2021
7a42fb5
feat: use pickle instead of csv for tx cache
BobTheBuidler Dec 17, 2021
91e96c1
chore: refactor
BobTheBuidler Dec 17, 2021
0206067
chore: checkpoint tx pickle thru block 10949751
BobTheBuidler Dec 17, 2021
3ac9f09
feat: parallelism
BobTheBuidler Dec 17, 2021
5dd8934
fix: accept new transfer event field labels
BobTheBuidler Dec 17, 2021
f88039d
fix: handle reverts in price magic
BobTheBuidler Dec 17, 2021
75dc857
fix: etherscan rate limit handler
BobTheBuidler Dec 17, 2021
1dcd041
chore: refactor dataframe
BobTheBuidler Dec 17, 2021
481863f
chore: increase dop
BobTheBuidler Dec 17, 2021
35daef4
chore: refactor
BobTheBuidler Dec 17, 2021
17bb37d
chore: checkpoint tx pickle thru block 13398262
BobTheBuidler Dec 17, 2021
5c25688
feat:user balances (#2)
BobTheBuidler Dec 17, 2021
b45e6c9
feat: merge tx analysis with existing exporter
BobTheBuidler Dec 17, 2021
d2e3e05
chore: wallets not users
BobTheBuidler Dec 17, 2021
3db81d2
chore: cleanup
BobTheBuidler Dec 17, 2021
ef4558d
chore: wallets not users
BobTheBuidler Dec 17, 2021
a0982a6
chore: refactor
BobTheBuidler Dec 17, 2021
255b29c
feat: env variable to skip wallet stats for speed
BobTheBuidler Dec 17, 2021
4a33773
chore: cleanup
BobTheBuidler Dec 17, 2021
b292227
fix: skip transfers
BobTheBuidler Dec 17, 2021
8cee429
fix: typo
BobTheBuidler Dec 17, 2021
af8e819
feat: treasury exporter
BobTheBuidler Dec 17, 2021
cb36be3
feat: add buckets
BobTheBuidler Dec 17, 2021
236d6f5
fix: add details to revert message when can't decode logs
BobTheBuidler Dec 17, 2021
d14ddd9
feat: silent kwarg for magic.get_price
BobTheBuidler Dec 17, 2021
90eddab
feat: postgres tx caching
BobTheBuidler Dec 17, 2021
eb859c3
chore: cleanup reverted changes
BobTheBuidler Dec 17, 2021
83ba334
feat: yearn.describe_wallets
BobTheBuidler Dec 17, 2021
ddf1687
feat: wallet exporter
BobTheBuidler Dec 17, 2021
201a744
feat: specify new agg stats
BobTheBuidler Dec 17, 2021
1ec531b
feat: skip middleware if already setup
BobTheBuidler Dec 17, 2021
bfd1703
chore: cleanup
BobTheBuidler Dec 17, 2021
d0848a7
chore: black
BobTheBuidler Dec 17, 2021
1f79a83
chore: cleanup
BobTheBuidler Dec 17, 2021
5e733c5
chore: cleanup
BobTheBuidler Dec 17, 2021
c076718
fix: setup middleware
BobTheBuidler Dec 17, 2021
1965d30
chore: revert changes to create_filter
BobTheBuidler Dec 17, 2021
7992f40
chore: revert silent kwarg on magic.get_price
BobTheBuidler Dec 17, 2021
1b935e7
chore: refactor
BobTheBuidler Dec 17, 2021
67967df
feat: sms exporter
BobTheBuidler Dec 17, 2021
46f82ae
fix: compound debt zero address bug
BobTheBuidler Dec 17, 2021
0a3660b
chore: fix logger name
BobTheBuidler Dec 17, 2021
684eb3b
feat: replace pyodbc with ponyorm
BobTheBuidler Dec 17, 2021
edbb418
feat: wallet exporter for earn, ib, special
BobTheBuidler Dec 17, 2021
4baa60d
chore: add pony to requirements
BobTheBuidler Dec 17, 2021
06f916b
fix: sms exporter
BobTheBuidler Dec 17, 2021
2fe3ff0
fix: wallet exporter
BobTheBuidler Dec 17, 2021
259994e
fix: historical treasury exporter
BobTheBuidler Dec 17, 2021
fbc9d38
fix: treasury exporter
BobTheBuidler Dec 17, 2021
65dd267
fix: sql syntax
BobTheBuidler Dec 17, 2021
360ef46
fix: registry wallet exporter
BobTheBuidler Dec 17, 2021
a8a838e
fix: vault wallet exporter
BobTheBuidler Dec 17, 2021
36dc075
fix: yearn registry
BobTheBuidler Dec 17, 2021
30c1889
fix: victoria
BobTheBuidler Dec 17, 2021
8a75624
fix: transactions exporter insufficient chunk size
BobTheBuidler Dec 17, 2021
d6cd2a8
feat: add sms address for ftm
BobTheBuidler Dec 17, 2021
9770a96
fix: typeerror in compound debt calc
BobTheBuidler Dec 17, 2021
3f7e9fc
chore: cleaner treasury constants for multichain
BobTheBuidler Dec 17, 2021
e61b6a7
chore: homogenize registry method names for active_vaults_at
BobTheBuidler Dec 17, 2021
21ccda5
chore: remove unnecessary caching
BobTheBuidler Dec 17, 2021
66b4ab0
chore: cleanup constants
BobTheBuidler Dec 17, 2021
e2b7cc0
chore: refactor
BobTheBuidler Dec 17, 2021
95aa3a4
chore: remove artifact from testing
BobTheBuidler Dec 17, 2021
92aa656
chore: better utilize ponyorm queries
BobTheBuidler Dec 17, 2021
634e052
chore: refactor
BobTheBuidler Dec 17, 2021
762ad58
fix: last_recorded_block postgres util
BobTheBuidler Dec 17, 2021
4e06e59
fix: transactions exporter broken by previous refactoring
BobTheBuidler Dec 17, 2021
e472f09
chore: remove artifact from testing
BobTheBuidler Dec 17, 2021
955b34f
chore: remove unneeded infura env vars
BobTheBuidler Dec 17, 2021
c9f42d9
chore: remove dev artifacts
BobTheBuidler Dec 17, 2021
e13b7e8
fix: remove parallelism on transactions exporter
BobTheBuidler Dec 17, 2021
6db3613
chore: extract variable
BobTheBuidler Dec 17, 2021
a233060
chore: add comment
BobTheBuidler Dec 17, 2021
861746a
chore: refactor
BobTheBuidler Dec 17, 2021
594e875
chore: remove commented out wip snippets
BobTheBuidler Dec 17, 2021
bb811b9
fix: buckets
BobTheBuidler Dec 17, 2021
648d94b
fix: unhashable type error
BobTheBuidler Dec 17, 2021
4c66e9d
chore: clarify comment
BobTheBuidler Dec 17, 2021
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ rebuild: down build up
scratch: clean-volumes build up

logs:
$(dashboards_command) logs -f -t eth-exporter historical-eth-exporter ftm-exporter historical-ftm-exporter treasury-exporter historical-treasury-exporter transactions-exporter wallet-exporter
$(dashboards_command) logs -f -t eth-exporter historical-eth-exporter ftm-exporter historical-ftm-exporter treasury-exporter historical-treasury-exporter transactions-exporter wallet-exporter sms-exporter historical-sms-exporter

test:
$(test_command) up
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ boto3==1.17.88
rich>=10.11.0
matplotlib>=3.4.3
pandas>=1.3.0
pyodbc>=4.0.32
pony>=0.6.7
sqlalchemy>=1.4.26
44 changes: 44 additions & 0 deletions scripts/historical_sms_exporter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import logging
from datetime import datetime, timezone

from yearn.historical_helper import export_historical, time_tracking
from yearn.treasury.treasury import StrategistMultisig
from yearn.utils import closest_block_after_timestamp

logger = logging.getLogger('yearn.historical_sms_exporter')

def main():
start = datetime.now(tz=timezone.utc)
# end: 2020-12-23 first sms tx
end = datetime(2020, 12, 23, tzinfo=timezone.utc)
export_historical(
start,
end,
export_chunk,
export_snapshot,
'sms_assets'
)


def export_chunk(chunk, export_snapshot_func):
sms = StrategistMultisig()
for snapshot in chunk:
ts = snapshot.timestamp()
export_snapshot_func(
{
'treasury': sms,
'snapshot': snapshot,
'ts': ts,
'exporter_name': 'historical_sms'
}
)


@time_tracking
def export_snapshot(sms, snapshot, ts, exporter_name):
block = closest_block_after_timestamp(ts)
assert block is not None, "no block after timestamp found"
sms.export(block, ts)
logger.info("exported SMS snapshot %s", snapshot)


7 changes: 4 additions & 3 deletions scripts/historical_treasury_exporter.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import logging
from datetime import datetime, timezone

from yearn.historical_helper import export_historical, time_tracking
from yearn.treasury.treasury import YearnTreasury
from yearn.utils import closest_block_after_timestamp
from yearn.treasury.treasury import Treasury

logger = logging.getLogger('yearn.historical_treasury_exporter')

def main():
start = datetime.now(tz=timezone.utc)
# end: 2020-02-12 first treasury tx
# end: 2020-07-21 first treasury tx
end = datetime(2020, 7, 21, tzinfo=timezone.utc)
export_historical(
start,
Expand All @@ -20,7 +21,7 @@ def main():


def export_chunk(chunk, export_snapshot_func):
treasury = Treasury()
treasury = YearnTreasury()
for snapshot in chunk:
ts = snapshot.timestamp()
export_snapshot_func(
Expand Down
20 changes: 20 additions & 0 deletions scripts/sms_exporter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import logging
import os
import time

from brownie import chain
from yearn.treasury.treasury import StrategistMultisig
from yearn.outputs import victoria

logger = logging.getLogger('yearn.sms_exporter')
sleep_interval = int(os.environ.get('SLEEP_SECONDS', '0'))


def main():
treasury = StrategistMultisig(watch_events_forever=True)
for block in chain.new_blocks(height_buffer=12):
start_time = time.time()
treasury.export(block.number, block.timestamp)
duration = time.time() - start_time
victoria.export_duration(duration, 1, "sms_forwards", block.timestamp)
time.sleep(sleep_interval)
129 changes: 63 additions & 66 deletions scripts/transactions_exporter.py
Original file line number Diff line number Diff line change
@@ -1,63 +1,74 @@
import logging
import time
import warnings
from decimal import Decimal
import logging

import pandas as pd
from brownie import ZERO_ADDRESS, Contract, chain, web3
from brownie.exceptions import BrownieEnvironmentWarning
from pony.orm import db_session
from joblib import Parallel, delayed
import sqlalchemy
from tqdm import tqdm
from web3._utils.abi import filter_by_name
from web3._utils.events import construct_event_topic_set
from yearn.events import create_filter, decode_logs, get_logs_asap
from yearn.outputs.postgres.postgres import postgres
from yearn.entities import UserTx # , TreasuryTx
from yearn.events import decode_logs, get_logs_asap
from yearn.outputs.postgres.utils import (cache_address, cache_token,
last_recorded_block)
from yearn.prices import magic
from yearn.utils import contract
from yearn.v1.registry import Registry as RegistryV1
from yearn.v2.registry import Registry as RegistryV2
from yearn.treasury.treasury import Treasury

treasury = Treasury()
from yearn.yearn import Yearn

warnings.simplefilter("ignore", BrownieEnvironmentWarning)

registryV1 = RegistryV1()
registryV2 = RegistryV2()
yearn = Yearn(load_strategies=False)

logger = logging.getLogger(__name__)

BATCH_SIZE = 5000

def main():
for block in chain.new_blocks(height_buffer=1):
process_and_cache_user_txs(postgres.last_recorded_block('user_txs'))


def active_vaults_at(end_block) -> set:
v1 = {vault.vault for vault in registryV1.active_vaults_at(end_block)}
v2 = {vault.vault for vault in registryV2.active_vaults_at(end_block)}
return v1.union(v2)
process_and_cache_user_txs(last_recorded_block(UserTx))


@db_session
def process_and_cache_user_txs(last_saved_block=None):
max_block_to_cache = (
chain.height - 50
) # We look 50 blocks back to avoid uncles and reorgs
# NOTE: We look 50 blocks back to avoid uncles and reorgs
max_block_to_cache = chain.height - 50
start_block = last_saved_block + 1 if last_saved_block else None
end_block = (
10650000
9480000 # NOTE block some arbitrary time after iearn's first deployment
if start_block is None
else start_block + 500
if start_block + 500 < max_block_to_cache
else start_block + BATCH_SIZE
if start_block + BATCH_SIZE < max_block_to_cache
else max_block_to_cache
)
df = pd.DataFrame()
for vault in tqdm(active_vaults_at(end_block)):
df = df.append(get_token_transfers(vault, start_block, end_block))
df = df.rename(columns={'token': 'vault'})
df.to_sql('user_txs', postgres.sqla_engine, if_exists='append', index=False)
print(f'user txs batch {start_block}-{end_block} exported to postrges')
for vault in yearn.active_vaults_at(end_block):
df = df.append(get_token_transfers(vault.vault, start_block, end_block))
if len(df):
# NOTE: We want to insert txs in the order they took place, so wallet exporter
# won't have issues in the event that transactions exporter fails mid-run.
df = df.sort_values('block')
for index, row in df.iterrows():
UserTx(
vault=cache_token(row.token),
timestamp=row.timestamp,
block=row.block,
hash=row.hash,
log_index=row.log_index,
type=row.type,
from_address=row['from'],
to_address=row['to'],
amount = row.amount,
price = row.price,
value_usd = row.value_usd,
gas_used = row.gas_used,
gas_price = row.gas_price
)
if start_block == end_block:
logger.warn(f'{len(df)} user txs exported to postrges [block {start_block}]')
else:
logger.warn(f'{len(df)} user txs exported to postrges [blocks {start_block}-{end_block}]')


# Helper functions
Expand All @@ -66,76 +77,62 @@ def get_token_transfers(token, start_block, end_block) -> pd.DataFrame:
filter_by_name('Transfer', token.abi)[0],
web3.codec,
)
postgres.cache_token(token.address)
decimals = contract(token.address).decimals()
token_entity = cache_token(token.address)
events = decode_logs(
get_logs_asap(token.address, topics, from_block=start_block, to_block=end_block)
)
return pd.DataFrame(
Parallel(1, 'threading')(
delayed(_process_transfer_event)(event, token, decimals) for event in events
)
)
return pd.DataFrame([_process_transfer_event(event, token_entity) for event in events])


def _process_transfer_event(event, vault, decimals) -> dict:
def _process_transfer_event(event, token_entity) -> dict:
sender, receiver, amount = event.values()
postgres.cache_address(sender)
postgres.cache_address(receiver)
price = _get_price(event, vault)
cache_address(sender)
cache_address(receiver)
price = _get_price(event, token_entity)
if (
vault.address == '0x7F83935EcFe4729c4Ea592Ab2bC1A32588409797'
# NOTE magic.get_price() returns erroneous price due to erroneous ppfs
token_entity.token.address == '0x7F83935EcFe4729c4Ea592Ab2bC1A32588409797'
and event.block_number == 12869164
):
# NOTE magic.get_price() returns erroneous price due to erroneous ppfs
price = 99999
if price > 100000:
logger.warn(f'token: {vault.address}')
logger.warn(f'price: {price}')
logger.warn(f'block: {event.block_number}')
txhash = event.transaction_hash.hex()
return {
'chainid': chain.id,
'block': event.block_number,
'timestamp': chain[event.block_number].timestamp,
'hash': txhash,
'log_index': event.log_index,
'token': vault.address,
'type': _event_type(sender, receiver, vault.address),
'token': token_entity.token.address,
'type': _event_type(sender, receiver, token_entity.token.address),
'from': sender,
'to': receiver,
'amount': Decimal(amount) / Decimal(10 ** decimals),
'amount': Decimal(amount) / Decimal(10 ** token_entity.decimals),
'price': price,
'value_usd': Decimal(amount) / Decimal(10 ** decimals) * Decimal(price),
'value_usd': Decimal(amount) / Decimal(10 ** token_entity.decimals) * Decimal(price),
'gas_used': web3.eth.getTransactionReceipt(txhash).gasUsed,
'gas_price': web3.eth.getTransaction(
txhash
).gasPrice, # * 1.0 # force pandas to insert this as decimal not bigint
'gas_price': web3.eth.getTransaction(txhash).gasPrice
}


def _get_price(event, vault):
def _get_price(event, token_entity):
while True:
try:
try:
return magic.get_price(vault.address, event.block_number)
return magic.get_price(token_entity.token.address, event.block_number)
except TypeError: # magic.get_price fails because all liquidity was removed for testing and `share_price` returns None
return magic.get_price(vault.token(), event.block_number)
return magic.get_price(Contract(token_entity.token.address).token(), event.block_number)
except ConnectionError as e:
# Try again
print(f'ConnectionError: {str(e)}')
logger.warn(f'ConnectionError: {str(e)}')
time.sleep(1)
except ValueError as e:
print(f'ValueError: {str(e)}')
if str(e) in [
"Failed to retrieve data from API: {'status': '0', 'message': 'NOTOK', 'result': 'Max rate limit reached'}",
"Failed to retrieve data from API: {'status': '0', 'message': 'NOTOK', 'result': 'Max rate limit reached, please use API Key for higher rate limit'}",
]:
logger.warn(f'ValueError: {str(e)}')
if 'Max rate limit reached' in str(e):
# Try again
print('trying again...')
logger.warn('trying again...')
time.sleep(5)
else:
print(f'vault: {vault.address}')
logger.warn(f'vault: {token_entity.token.address}')
raise Exception(str(e))


Expand Down
4 changes: 2 additions & 2 deletions scripts/treasury_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
import time

from brownie import chain
from yearn.treasury.treasury import Treasury
from yearn.treasury.treasury import YearnTreasury
from yearn.outputs import victoria

logger = logging.getLogger('yearn.treasury_exporter')
sleep_interval = int(os.environ.get('SLEEP_SECONDS', '0'))


def main():
treasury = Treasury(watch_events_forever=True)
treasury = YearnTreasury(watch_events_forever=True)
for block in chain.new_blocks(height_buffer=12):
start_time = time.time()
treasury.export(block.number, block.timestamp)
Expand Down
10 changes: 7 additions & 3 deletions scripts/wallet_exporter.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import logging
import os
from datetime import datetime, timezone
from itertools import count

import requests
from brownie import chain
from yearn.outputs.postgres.postgres import postgres
from yearn.entities import UserTx
from yearn.historical_helper import export_historical, has_data, time_tracking
from yearn.outputs.postgres.utils import last_recorded_block
from yearn.utils import closest_block_after_timestamp
from yearn.historical_helper import export_historical, time_tracking, has_data
from yearn.yearn import Yearn

logger = logging.getLogger('yearn.wallet_exporter')

postgres_cached_thru_block = postgres.last_recorded_block('user_txs')
postgres_cached_thru_block = last_recorded_block(UserTx)
postgres_cached_thru_ts = chain[postgres_cached_thru_block].timestamp


def main():
start = datetime.now(tz=timezone.utc)
# end: 2020-02-12 first iearn deployment
Expand Down
Loading