Last active
February 6, 2023 18:47
-
-
Save Philogy/b02212a14da0891be057f942e9a21ebe to your computer and use it in GitHub Desktop.
LLama Lend History Gettooor
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
| import sys | |
| import os | |
| import dotenv | |
| import requests | |
| from collections import defaultdict | |
| from decimal import Decimal as D | |
| dotenv.load_dotenv() | |
| # Instructions: | |
| # Create `.env` file with `ALCHEMY_KEY` property set to your alchemy key | |
| # Run by exeuction `python llama-lend-get-history.py <pool address>` | |
| JSON_HEADER = { | |
| 'accept': 'application/json', | |
| 'content-type': 'application/json' | |
| } | |
| def get_alchmey_asset_transfers(key, **params): | |
| return requests.post( | |
| f'https://eth-mainnet.g.alchemy.com/v2/{key}', | |
| headers=JSON_HEADER, | |
| json={ | |
| 'id': 1, | |
| 'jsonrpc': '2.0', | |
| 'method': 'alchemy_getAssetTransfers', | |
| 'params': [ | |
| { | |
| **params | |
| } | |
| ] | |
| } | |
| ).json() | |
| def hex_to_int(h): | |
| return int(h[2:], 16) | |
| def get_asset_transfers(pool): | |
| alchemy_key = os.environ.get('ALCHEMY_KEY') | |
| transfers_out = get_alchmey_asset_transfers( | |
| alchemy_key, | |
| category=['external', 'internal', 'erc721'], | |
| fromAddress=pool | |
| ) | |
| transfers_in = get_alchmey_asset_transfers( | |
| alchemy_key, | |
| category=['external', 'internal', 'erc721'], | |
| toAddress=pool | |
| ) | |
| cleaned_transfers = [ | |
| { | |
| 'from': transfer['from'], | |
| 'to': transfer['to'], | |
| 'hash': transfer['hash'], | |
| 'asset': (transfer['asset'], transfer['rawContract']['address']), | |
| 'denom': hex_to_int(transfer['erc721TokenId']) | |
| if transfer['value'] is None | |
| else hex_to_int(transfer['rawContract']['value']), | |
| 'blockNum': hex_to_int(transfer['blockNum']) | |
| } | |
| for transfer in | |
| transfers_out['result']['transfers'] + | |
| transfers_in['result']['transfers'] | |
| ] | |
| grouped_transfers = defaultdict(list) | |
| for transfer in cleaned_transfers: | |
| grouped_transfers[transfer['hash']].append(transfer) | |
| sorted_transfers = sorted( | |
| grouped_transfers.items(), | |
| key=lambda p: p[1][0]['blockNum'] | |
| ) | |
| return sorted_transfers | |
| def is_eth(asset): | |
| return asset[0] == 'ETH' and asset[1] is None | |
| def sign(x): | |
| zero = D(0) if isinstance(x, D) else 0 | |
| if x > zero: | |
| return '+' | |
| return '' | |
| def etherscan(tx_hash): | |
| return f'https://etherscan.io/tx/{tx_hash}' | |
| def main(): | |
| pool = sys.argv[1] | |
| txs = get_asset_transfers(pool) | |
| pool = pool.lower() | |
| borrows = {} | |
| for tx_hash, tx_transfers in txs: | |
| prints = [ | |
| None, | |
| f' tx hash: {tx_hash}', | |
| f' link: {etherscan(tx_hash)}' | |
| ] | |
| tx_type = None | |
| if len(tx_transfers) == 1 and is_eth((transfer := tx_transfers[0])['asset']): | |
| if transfer['from'] == pool: | |
| tx_type = 'WITHDRAWAL' | |
| elif transfer['to'] == pool: | |
| tx_type = 'DEPOSIT' | |
| else: | |
| raise ValueError(f'Unrecognized: {transfer}') | |
| amount = transfer['denom'] | |
| prints.append(f' amount: {amount / 1e18 :,.6f} ETH') | |
| elif len(tx_transfers) >= 2: | |
| pool_eth_gain = 0 | |
| tokens_out = [] | |
| tokens_in = [] | |
| for transfer in tx_transfers: | |
| if transfer['to'] == pool: | |
| if is_eth(transfer['asset']): | |
| pool_eth_gain += transfer['denom'] | |
| else: | |
| tokens_in.append(transfer['denom']) | |
| elif transfer['from'] == pool: | |
| if is_eth(transfer['asset']): | |
| pool_eth_gain -= transfer['denom'] | |
| else: | |
| tokens_out.append(transfer['denom']) | |
| else: | |
| raise ValueError(f'Unrecognized: {transfer}') | |
| prints.append( | |
| f' change: {sign(pool_eth_gain)}{pool_eth_gain/ 1e18:,.6f} ETH' | |
| ) | |
| if pool_eth_gain > 0 and not tokens_in: | |
| tx_type = 'REPAY' | |
| total_principal = 0 | |
| borrow_txs = [] | |
| for token_id in tokens_out: | |
| borrow_tx, amount = borrows[token_id] | |
| borrow_txs.append(borrow_tx) | |
| total_principal += amount | |
| del borrows[token_id] | |
| prints.append( | |
| f' Interest earned: +{D(pool_eth_gain - total_principal)/ D(10**18)} ETH' | |
| ) | |
| if borrow_txs: | |
| prints.append(f' Borrow txs:') | |
| for borrow_tx in borrow_txs: | |
| prints.append(f' - {etherscan(borrow_tx)}') | |
| elif pool_eth_gain < 0 and not tokens_out: | |
| tx_type = 'BORROW' | |
| for token_id in tokens_in: | |
| borrows[token_id] = ( | |
| tx_hash, -pool_eth_gain // len(tokens_in)) | |
| else: | |
| raise ValueError(f'Unrecognized: {tx_transfers}') | |
| elif all(not is_eth(transfer['asset']) for transfer in tx_transfers): | |
| tx_type = 'LIQUIDATION' | |
| lost_principal = 0 | |
| assumed_tokens = [] | |
| for transfer in tx_transfers: | |
| token_id = transfer['denom'] | |
| asset_name, _ = transfer['asset'] | |
| borrow_tx, amount = borrows[token_id] | |
| lost_principal += amount | |
| assumed_tokens.append((f'{asset_name}#{token_id}', borrow_tx)) | |
| del borrows[token_id] | |
| prints.append( | |
| f' Forfeited principal: {D(lost_principal) / D(10**18)} ETH') | |
| prints.append(f' Assumed tokens:') | |
| for token, borrow_tx in assumed_tokens: | |
| prints.append( | |
| f' - {token} (tx: {etherscan(borrow_tx)})' | |
| ) | |
| else: | |
| raise ValueError(f'Unrecognized: {tx_transfers}') | |
| prints[0] = f'tx: {tx_hash[:8]}..{tx_hash[-6:]} [{tx_type}]' | |
| for p in prints: | |
| print(p) | |
| print() | |
| if __name__ == '__main__': | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment