File size: 6,944 Bytes
a4a226b
 
 
 
 
 
91cdfbe
1fd1024
a4a226b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5337889
a4a226b
 
5337889
a4a226b
5337889
 
 
 
 
a4a226b
 
 
 
1fd1024
 
 
 
a59dc6c
 
4a72f7f
a4a226b
 
 
 
a59dc6c
a4a226b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5337889
5d6129a
faffd71
5d6129a
 
a4a226b
 
 
 
 
5d6129a
 
 
a4a226b
 
 
 
 
 
 
 
 
1fd1024
5d6129a
a4a226b
 
 
 
2775398
a4a226b
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import requests
import asyncio
import aiohttp
import pandas as pd
import urllib
import gradio as gr
import os
from pymongo import MongoClient

addresses = [
    "0x46dae11e3ab74C417d370FB0d467364397178f5d",
    "0x2B3cb7D697Dd5224f3AcfC1b7EbDe77F468Dd539",
    "0x7B04395AF49caf8C4Fd5FF716698D97f394d0d41",
    "0x4dAFdbdB45947AB8666411AFc07F95b39Ab3841B",
    "0xa2ebb53A922E8fe9E6949316b0018F95f0956Ce7",
    "0x0FCc5f7845F3B2188918495f60F6294a15dc772b",
    "0xefa59c4aE5BaBA2c1017Ce2Fc9c1E0E080e5F448",
    "0x1C2A2D1b1A95a252AD4f9E8c9055885a1721c252",
    "0xfCb81D6a2678D9f0798124BD3E46bA0E1Ff0C32c",
    "0xdF04384C3AE43bD08E04692139C6B6C6Eb308B76",
    "0xA734C747E53171261568DbBB1BCd699EDF01925c",
    "0xC6347a0924aAbBA5E655397fbD18cb702066304b",
    "0xc6300201821CB7026E0e73cC10dC9Ca5C623Ff15",
    "0x2777698A53410C344e98267dB672a432c77C6Ed9",
    "0xCCc07e973c04c607381b96eCBe3e426cc7c17B88",
    "0xE813553077d0bfdd9615465D8A974F63bbf33aD4",
    "0x0a045118cDb3fB0FDb50D2f91f04c56B28F5fD07",
    "0x0bC67eE9A36f5bc8e05282E538FDe119C372a222",
    "0x1ca7bCe8A7c4F62a3575279688D9E9ad337f6b3d",
    "0x940665c733acBcA198306F8881c1d268E2F92DFA",
    "0x6393613ca5a87Cc2BEe2eC3610EcAcD279FcBcD6",
    "0xAB851cA6F8e5329Dcf9B164d312f7d5C9D41eE01",
    "0xe2Db92758567D1D67A861F0645F850ddb6e216E8",
    "0x4a7F42021C71Be21D3c21717743449620Abb3D25",
    "0x2Bd99Ac1dCa390A543BD06F7712ece7B787752a5",
    "0x8c27aa11FF3562c807C5A40e0512bDC043B39Bb0",
    "0xB4e6dEa305a1A84FA0B2185F36de31bcE2c22005",
    "0xA89f812f26ef2f6D89c81371A89787240c63D524",
    "0xF7a54082215D4508CD674b270ECc37Bd281FD92d",
    "0xc419410F5B29cdd4343013210ac194B872cF5a4B"
]

# Chunk into groups of 3
def chunk_addresses(addresses, size):
    return [addresses[i:i + size] for i in range(0, len(addresses), size)]

API_URL = "https://api.g.alchemy.com/data/v1/yhe6L3PXmiENzS1sP9Fu4_T5E3l0QyeB/assets/tokens/by-address"

async def fetch_batch(session, batch):
    json_payload = {
        "addresses": [{"address": addr, "networks": ["arb-mainnet"]} for addr in batch],
        "withMetadata": True,
        "withPrices": True,
        "includeNativeTokens": True
    }
    async with session.post(API_URL, json=json_payload) as response:
        response = await response.json()
        return response['data']['tokens']

async def get_wallet_balances(addresses):
    async with aiohttp.ClientSession() as session:
        batches = chunk_addresses(addresses, 3)
        tasks = [fetch_batch(session, batch) for batch in batches]
        responses = await asyncio.gather(*tasks)
        first_parts = []
        rest_parts = []

        for res in responses:
            first_parts.extend(res[:3])
            rest_parts.extend(res[3:])

        responses = first_parts + rest_parts
        df = pd.DataFrame(responses)
        for i in range(len(addresses)):
            df.at[i,'tokenMetadata'] = {}
    
        df['tokenBalance'] = df['tokenBalance'].apply(lambda x : int(x,16))
        df['decimals'] = df['tokenMetadata'].apply(lambda x : x.get('decimals',0))
        df['tokenBalance'] = df['tokenBalance']/(10**df['decimals'])
        for i in range(len(addresses)):
            df.at[i, 'tokenBalance'] = df.at[i, 'tokenBalance'] / (10**18)

        df['symbol'] = df['tokenMetadata'].apply(lambda x : x.get('symbol',''))
        for i in range(len(addresses)):
            df.at[i,'symbol'] = 'ETH'
        
        df['currentPrice'] = df['tokenPrices'].apply(lambda x : x[0].get('value',None) if len(x) > 0 else None)
        df.drop(columns=['tokenPrices','tokenMetadata','network','tokenAddress'], inplace=True)
        df = df.dropna()
        df['tokenBalance'] = pd.to_numeric(df['tokenBalance'], errors='coerce')
        df = df[df['tokenBalance'] != 0]
        df['tokenBalance'] = pd.to_numeric(df['tokenBalance'], errors='coerce')
        df['currentPrice'] = pd.to_numeric(df['currentPrice'], errors='coerce')
        df['value'] = df['tokenBalance'] * df['currentPrice']
        df.drop(columns=['decimals'],inplace=True)

        df = df[df['symbol'].isin(['BLUP', 'ETH', 'USDT', 'USDC'])]
        df= df[['address','symbol','tokenBalance','value']]
        df = df.rename(columns={'tokenBalance':'amount','value':'usd_value'})

        df = df.sort_values(['address', 'symbol'])
        df['address'] = df['address'].mask(df['address'].duplicated()).fillna('')

        #df = df.groupby("symbol", as_index=False).sum()

        return df, df['usd_value'].sum()

async def get_lp_balances():
    client = MongoClient(os.getenv('MONGO_DB_URI'))
    db = client["debank_tokens"]
    token_collection = db["debank"]
    token_list = token_collection.find_one(sort=[("timestamp", -1)])
    del token_list["_id"]
    del token_list["timestamp"]
    
    url = "https://api.debank.com/user?id=0x275c7f2781db0b78228029f85735059c9bdd4f35"

    SCRAPE_DO_TOKEN = os.getenv('SCRAPE_DO_KEY')

    headers = token_list

    encoded_url = urllib.parse.quote(url)
    proxy_url = f"http://api.scrape.do/?token={SCRAPE_DO_TOKEN}&url={encoded_url}&forwardHeaders=True&super=True"
    response = requests.get(proxy_url, headers=headers)

    response = response.json()['data']['user']['stats']
    top_coins = response['top_coins']
    top_tokens = response['top_tokens']
    coins = pd.DataFrame(top_coins)
    tokens = pd.DataFrame(top_tokens)
    balances = pd.concat([coins, tokens], axis=0)
    balances.drop(columns=['id','logo_url','percent','price','chain_id'],inplace=True)
    balances = balances[['symbol','amount','usd_value']]

    return balances, balances['usd_value'].sum()

async def get_all_dfs():
    wallet_df, wallet_total = await get_wallet_balances(addresses)
    lp_df, lp_total = await get_lp_balances()
    final_df = pd.concat([wallet_df,lp_df],axis=0)
    final_df.drop(columns=['address'],inplace=True)
    final_df = final_df.groupby("symbol", as_index=False).sum()
    starting_df = pd.DataFrame([{"symbol":"ETH","amount":24,"usd_value":1613*24}])
    pnl = final_df['usd_value'].sum() - starting_df['usd_value'].sum()
    return wallet_df, wallet_total, lp_df, lp_total, final_df, final_df['usd_value'].sum(),starting_df,starting_df['usd_value'].sum(),pnl


with gr.Blocks() as demo:
    gr.Markdown("## BLUP Balances and PnL")

    df3_out = gr.Dataframe(label="Starting Balances")
    txt3_out = gr.Textbox(label="Total Value")

    df0_out = gr.Dataframe(label="Wallet Balances")
    txt0_out = gr.Textbox(label="Total Value")

    df1_out = gr.Dataframe(label="LP Balances")
    txt1_out = gr.Textbox(label="Total Value")

    df2_out = gr.Dataframe(label="Total Balances")
    txt2_out = gr.Textbox(label="Total Value")

    pnl_out = gr.Textbox(label="PNL (AUM Model)")

    # Load from MongoDB on app load (sync function)
    demo.load(
    fn=  get_all_dfs, 
    inputs = [],
    outputs=[df0_out, txt0_out, df1_out, txt1_out, df2_out, txt2_out,df3_out,txt3_out,pnl_out]
    )

demo.launch(debug=True, share=True)