์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿ”— ํŒŒ์ด์ฌ์œผ๋กœ ๋ธ”๋ก์ฒด์ธ ๊ฑฐ๋ž˜๋‚ด์—ญ ์ž๋™ ๋ถ„์„ ํ”„๋กœ๊ทธ๋žจ ๊ฐœ๋ฐœ

๐Ÿ”— ํŒŒ์ด์ฌ์œผ๋กœ ๋ธ”๋ก์ฒด์ธ ๊ฑฐ๋ž˜๋‚ด์—ญ ์ž๋™ ๋ถ„์„ ํ”„๋กœ๊ทธ๋žจ ๊ฐœ๋ฐœ

๋ธ”๋ก์ฒด์ธ ๋ฐ์ดํ„ฐ๋ฅผ ํŒŒ์ด์ฌ์œผ๋กœ ๋˜‘๋˜‘ํ•˜๊ฒŒ ๋ถ„์„ํ•˜๋Š” ์™„๋ฒฝ ๊ฐ€์ด๋“œ ๐Ÿš€

Block Hash: 0x3a4f Tx: 247 Block Hash: 0x7b2e Tx: 189 Block Hash: 0x9c1d Tx: 312 ๐Ÿ Python Analysis ๐Ÿ“Š Data Insights

๐ŸŽฏ ๋ธ”๋ก์ฒด์ธ ๊ฑฐ๋ž˜๋‚ด์—ญ ๋ถ„์„, ์™œ ํ•„์š”ํ• ๊นŒ?

์š”์ฆ˜ ๋ธ”๋ก์ฒด์ธ ๊ธฐ์ˆ ์ด ๋Œ€์„ธ์ž–์•„? ๋น„ํŠธ์ฝ”์ธ, ์ด๋”๋ฆฌ์›€๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ ์ˆ˜๋งŽ์€ ์•”ํ˜ธํ™”ํ์™€ NFT, DeFi ํ”„๋กœ์ ํŠธ๋“ค์ด ์Ÿ์•„์ง€๊ณ  ์žˆ์–ด. ๊ทธ๋Ÿฐ๋ฐ ์ด ๋ชจ๋“  ๊ฑฐ๋ž˜๋“ค์ด ๋ธ”๋ก์ฒด์ธ์— ํˆฌ๋ช…ํ•˜๊ฒŒ ๊ธฐ๋ก๋˜๋Š” ๊ฑด ์•Œ์ง€? ๐Ÿ˜Š

๋ฌธ์ œ๋Š” ์ด ๋ฐ์ดํ„ฐ๊ฐ€ ์—„์ฒญ๋‚˜๊ฒŒ ๋ฐฉ๋Œ€ํ•˜๋‹ค๋Š” ๊ฑฐ์•ผ. ๋งค์ผ ์ˆ˜๋ฐฑ๋งŒ ๊ฑด์˜ ๊ฑฐ๋ž˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ , ๊ฐ ๊ฑฐ๋ž˜๋งˆ๋‹ค ๋ณด๋‚ด๋Š” ์‚ฌ๋žŒ, ๋ฐ›๋Š” ์‚ฌ๋žŒ, ๊ธˆ์•ก, ์ˆ˜์ˆ˜๋ฃŒ, ํƒ€์ž„์Šคํƒฌํ”„ ๋“ฑ ๋‹ค์–‘ํ•œ ์ •๋ณด๊ฐ€ ๋‹ด๊ฒจ์žˆ์–ด. ์ด๊ฑธ ์‚ฌ๋žŒ์ด ์ผ์ผ์ด ํ™•์ธํ•œ๋‹ค? ๋ถˆ๊ฐ€๋Šฅ์— ๊ฐ€๊น์ง€! ๐Ÿคฏ

๊ทธ๋ž˜์„œ ๋“ฑ์žฅํ•œ ๊ฒŒ ๋ฐ”๋กœ ์ž๋™ ๋ถ„์„ ํ”„๋กœ๊ทธ๋žจ์ด์•ผ. ํŒŒ์ด์ฌ์„ ํ™œ์šฉํ•˜๋ฉด ๋ธ”๋ก์ฒด์ธ ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ˆ˜์ง‘ํ•˜๊ณ , ํŒจํ„ด์„ ๋ถ„์„ํ•˜๊ณ , ์˜๋ฏธ์žˆ๋Š” ์ธ์‚ฌ์ดํŠธ๋ฅผ ๋ฝ‘์•„๋‚ผ ์ˆ˜ ์žˆ์–ด. ํˆฌ์ž ์ „๋žต ์ˆ˜๋ฆฝ, ์ด์ƒ๊ฑฐ๋ž˜ ํƒ์ง€, ์‹œ์žฅ ํŠธ๋ Œ๋“œ ๋ถ„์„ ๋“ฑ ํ™œ์šฉ ๋ฒ”์œ„๊ฐ€ ๋ฌด๊ถ๋ฌด์ง„ํ•˜์ง€!
๐Ÿ’ก ์‹ค์ œ ํ™œ์šฉ ์‚ฌ๋ก€

โ€ข ์•”ํ˜ธํ™”ํ ๊ฑฐ๋ž˜์†Œ: ์ด์ƒ๊ฑฐ๋ž˜ ํŒจํ„ด ๊ฐ์ง€ ๋ฐ ๋ณด์•ˆ ๊ฐ•ํ™”
โ€ข ํˆฌ์ž์ž: ๊ณ ๋ž˜(๋Œ€๋Ÿ‰ ๋ณด์œ ์ž) ์›€์ง์ž„ ์ถ”์  ๋ฐ ํˆฌ์ž ์ „๋žต ์ˆ˜๋ฆฝ
โ€ข ์„ธ๋ฌด ๋‹น๊ตญ: ์•”ํ˜ธํ™”ํ ๊ฑฐ๋ž˜ ์ถ”์  ๋ฐ ๊ณผ์„ธ ์ž๋ฃŒ ํ™•๋ณด
โ€ข ์—ฐ๊ตฌ๊ธฐ๊ด€: ๋ธ”๋ก์ฒด์ธ ๋„คํŠธ์›Œํฌ ๋ถ„์„ ๋ฐ ํ•™์ˆ  ์—ฐ๊ตฌ
โ€ข DeFi ํ”„๋กœ์ ํŠธ: ํ”„๋กœํ† ์ฝœ ์‚ฌ์šฉ ํŒจํ„ด ๋ถ„์„ ๋ฐ ์ตœ์ ํ™”

๐Ÿ› ๏ธ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์ค€๋น„ํ•˜๊ธฐ

๋ณธ๊ฒฉ์ ์œผ๋กœ ํ”„๋กœ๊ทธ๋žจ์„ ๊ฐœ๋ฐœํ•˜๊ธฐ ์ „์— ํ•„์š”ํ•œ ๋„๊ตฌ๋“ค์„ ์ค€๋น„ํ•ด๋ณด์ž! ํŒŒ์ด์ฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ •์€ ์ƒ๊ฐ๋ณด๋‹ค ๊ฐ„๋‹จํ•ด. ๐Ÿ˜Ž

๐Ÿ“ฆ ํ•„์ˆ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜

๋ธ”๋ก์ฒด์ธ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃจ๋ ค๋ฉด ๋ช‡ ๊ฐ€์ง€ ํ•ต์‹ฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ•„์š”ํ•ด. ๊ฐ๊ฐ์˜ ์—ญํ• ์„ ์•Œ์•„๋ณด์ž!
Web3.py - ๋ธ”๋ก์ฒด์ธ๊ณผ์˜ ์†Œํ†ต ์ฐฝ๊ตฌ

์ด๋”๋ฆฌ์›€ ๋ธ”๋ก์ฒด์ธ๊ณผ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ๊ฐ€์žฅ ๋Œ€ํ‘œ์ ์ธ ํŒŒ์ด์ฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์•ผ. ๋ธ”๋ก ์ •๋ณด ์กฐํšŒ, ํŠธ๋žœ์žญ์…˜ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ, ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ ํ˜ธ์ถœ ๋“ฑ ๊ฑฐ์˜ ๋ชจ๋“  ์ž‘์—…์ด ๊ฐ€๋Šฅํ•ด!

๐Ÿ’ป ์„ค์น˜ ๋ช…๋ น์–ด
pip install web3
Pandas - ๋ฐ์ดํ„ฐ ๋ถ„์„์˜ ํ•ต์‹ฌ

์ˆ˜์ง‘ํ•œ ๋ธ”๋ก์ฒด์ธ ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œ ํ˜•ํƒœ๋กœ ์ •๋ฆฌํ•˜๊ณ  ๋ถ„์„ํ•˜๋Š” ๋ฐ ์ตœ๊ณ ์˜ ๋„๊ตฌ์•ผ. ์—‘์…€์ฒ˜๋Ÿผ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด์„œ ์ •๋ง ํŽธ๋ฆฌํ•ด! ๐Ÿ“Š

๐Ÿ’ป ์„ค์น˜ ๋ช…๋ น์–ด
pip install pandas numpy
Requests - API ํ†ต์‹ ์˜ ๊ธฐ๋ณธ

Etherscan, Infura ๊ฐ™์€ ๋ธ”๋ก์ฒด์ธ API ์„œ๋น„์Šค์™€ ํ†ต์‹ ํ•  ๋•Œ ํ•„์ˆ˜์ ์ด์•ผ. HTTP ์š”์ฒญ์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์–ด!

๐Ÿ’ป ์„ค์น˜ ๋ช…๋ น์–ด
pip install requests

๐Ÿ”‘ API ํ‚ค ๋ฐœ๊ธ‰๋ฐ›๊ธฐ

๋ธ”๋ก์ฒด์ธ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๋ ค๋ฉด API ํ‚ค๊ฐ€ ํ•„์š”ํ•ด. ๋ฌด๋ฃŒ๋กœ ๋ฐœ๊ธ‰๋ฐ›์„ ์ˆ˜ ์žˆ์œผ๋‹ˆ ๊ฑฑ์ • ๋งˆ! ๐Ÿ˜Š
Step 1: Infura ๊ฐ€์ž…
Infura(infura.io)์— ์ ‘์†ํ•ด์„œ ๋ฌด๋ฃŒ ๊ณ„์ •์„ ๋งŒ๋“ค์–ด. ์ด๋”๋ฆฌ์›€ ๋…ธ๋“œ์— ์ง์ ‘ ์—ฐ๊ฒฐํ•˜์ง€ ์•Š๊ณ ๋„ ๋ธ”๋ก์ฒด์ธ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์„œ๋น„์Šค์•ผ!
Step 2: ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
๋Œ€์‹œ๋ณด๋“œ์—์„œ ์ƒˆ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค๋ฉด ๊ณ ์œ ํ•œ Project ID๋ฅผ ๋ฐ›๊ฒŒ ๋ผ. ์ด๊ฒŒ ๋ฐ”๋กœ ๋„ˆ์˜ API ํ‚ค์•ผ! ๐Ÿ”
Step 3: Etherscan API ํ‚ค ๋ฐœ๊ธ‰
Etherscan(etherscan.io)์—์„œ๋„ ๋ฌด๋ฃŒ API ํ‚ค๋ฅผ ๋ฐœ๊ธ‰๋ฐ›์•„. ๋” ์ƒ์„ธํ•œ ๊ฑฐ๋ž˜ ์ •๋ณด์™€ ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์–ด!
โš ๏ธ ๋ณด์•ˆ ์ฃผ์˜์‚ฌํ•ญ

API ํ‚ค๋Š” ์ ˆ๋Œ€ ์ฝ”๋“œ์— ์ง์ ‘ ํ•˜๋“œ์ฝ”๋”ฉํ•˜์ง€ ๋งˆ! ํ™˜๊ฒฝ๋ณ€์ˆ˜๋‚˜ ๋ณ„๋„์˜ ์„ค์ • ํŒŒ์ผ์— ์ €์žฅํ•˜๊ณ  .gitignore์— ์ถ”๊ฐ€ํ•ด์„œ GitHub์— ์˜ฌ๋ผ๊ฐ€์ง€ ์•Š๋„๋ก ํ•ด์•ผ ํ•ด. ํ‚ค๊ฐ€ ๋…ธ์ถœ๋˜๋ฉด ์•…์šฉ๋  ์ˆ˜ ์žˆ๊ฑฐ๋“ ! ๐Ÿ”’

๐Ÿš€ ๊ธฐ๋ณธ ๋ธ”๋ก์ฒด์ธ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ ํ”„๋กœ๊ทธ๋žจ

์ž, ์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ์ฝ”๋”ฉ์„ ์‹œ์ž‘ํ•ด๋ณผ๊นŒ? ๋จผ์ € ๊ฐ„๋‹จํ•œ ๋ธ”๋ก ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ”„๋กœ๊ทธ๋žจ๋ถ€ํ„ฐ ๋งŒ๋“ค์–ด๋ณด์ž! ๐ŸŽจ

๐Ÿ“ก Web3 ์—ฐ๊ฒฐ ์„ค์ •

๊ฐ€์žฅ ๋จผ์ € ํ•ด์•ผ ํ•  ์ผ์€ ์ด๋”๋ฆฌ์›€ ๋„คํŠธ์›Œํฌ์— ์—ฐ๊ฒฐํ•˜๋Š” ๊ฑฐ์•ผ. Infura๋ฅผ ํ†ตํ•ด ์—ฐ๊ฒฐํ•˜๋ฉด ๋ณ„๋„์˜ ๋…ธ๋“œ ์šด์˜ ์—†์ด๋„ ๋ธ”๋ก์ฒด์ธ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์–ด!
๐Ÿ Python ์ฝ”๋“œ - Web3 ์ดˆ๊ธฐํ™”
from web3 import Web3
import json

# Infura ์—”๋“œํฌ์ธํŠธ ์„ค์ •
INFURA_URL = "https://mainnet.infura.io/v3/YOUR_PROJECT_ID"

# Web3 ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
w3 = Web3(Web3.HTTPProvider(INFURA_URL))

# ์—ฐ๊ฒฐ ํ™•์ธ
if w3.is_connected():
    print("โœ… ์ด๋”๋ฆฌ์›€ ๋ฉ”์ธ๋„ท์— ์„ฑ๊ณต์ ์œผ๋กœ ์—ฐ๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!")
    print(f"ํ˜„์žฌ ๋ธ”๋ก ๋†’์ด: {w3.eth.block_number}")
else:
    print("โŒ ์—ฐ๊ฒฐ ์‹คํŒจ! API ํ‚ค๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”.")
๐Ÿ’ก Pro Tip

is_connected() ๋ฉ”์„œ๋“œ๋กœ ์—ฐ๊ฒฐ ์ƒํƒœ๋ฅผ ํ•ญ์ƒ ํ™•์ธํ•˜๋Š” ์Šต๊ด€์„ ๋“ค์ด์ž! ๋„คํŠธ์›Œํฌ ๋ฌธ์ œ๋‚˜ API ํ‚ค ์˜ค๋ฅ˜๋ฅผ ๋ฏธ๋ฆฌ ์žก์„ ์ˆ˜ ์žˆ์–ด. ์‹ค์ œ ์„œ๋น„์Šค์—์„œ๋Š” ์žฌ์—ฐ๊ฒฐ ๋กœ์ง๋„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒŒ ์ข‹์•„! ๐Ÿ”„

๐Ÿ” ๋ธ”๋ก ์ •๋ณด ์กฐํšŒํ•˜๊ธฐ

์ด์ œ ์‹ค์ œ ๋ธ”๋ก ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€๋ณด์ž! ๊ฐ ๋ธ”๋ก์—๋Š” ์—„์ฒญ๋‚˜๊ฒŒ ๋งŽ์€ ์ •๋ณด๊ฐ€ ๋‹ด๊ฒจ์žˆ์–ด. ๋ธ”๋ก ํ•ด์‹œ, ํƒ€์ž„์Šคํƒฌํ”„, ๊ฑฐ๋ž˜ ๊ฐœ์ˆ˜, ์ฑ„๊ตด์ž ์ฃผ์†Œ ๋“ฑ๋“ฑ... ๐ŸŽฏ
๐Ÿ Python ์ฝ”๋“œ - ๋ธ”๋ก ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘
def get_block_info(block_number):
    """
    ํŠน์ • ๋ธ”๋ก์˜ ์ƒ์„ธ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜
    """
    try:
        # ๋ธ”๋ก ์ •๋ณด ์กฐํšŒ
        block = w3.eth.get_block(block_number, full_transactions=True)
        
        block_info = {
            'block_number': block['number'],
            'timestamp': block['timestamp'],
            'hash': block['hash'].hex(),
            'parent_hash': block['parentHash'].hex(),
            'miner': block['miner'],
            'difficulty': block['difficulty'],
            'total_difficulty': block['totalDifficulty'],
            'size': block['size'],
            'gas_used': block['gasUsed'],
            'gas_limit': block['gasLimit'],
            'transaction_count': len(block['transactions'])
        }
        
        return block_info
    
    except Exception as e:
        print(f"โŒ ๋ธ”๋ก ์ •๋ณด ์กฐํšŒ ์‹คํŒจ: {e}")
        return None

# ์ตœ์‹  ๋ธ”๋ก ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
latest_block = w3.eth.block_number
block_data = get_block_info(latest_block)

if block_data:
    print(f"\n๐Ÿ“ฆ ๋ธ”๋ก #{block_data['block_number']} ์ •๋ณด")
    print(f"โฐ ์ƒ์„ฑ ์‹œ๊ฐ„: {block_data['timestamp']}")
    print(f"๐Ÿ‘ท ์ฑ„๊ตด์ž: {block_data['miner']}")
    print(f"๐Ÿ’ผ ๊ฑฐ๋ž˜ ๊ฐœ์ˆ˜: {block_data['transaction_count']}")
    print(f"โ›ฝ ์‚ฌ์šฉ๋œ ๊ฐ€์Šค: {block_data['gas_used']:,}")
๐ŸŽ“ ์ฝ”๋“œ ํ•ด์„ค

full_transactions=True ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ํ•ต์‹ฌ์ด์•ผ! ์ด๊ฑธ True๋กœ ์„ค์ •ํ•˜๋ฉด ๋ธ”๋ก ์•ˆ์˜ ๋ชจ๋“  ๊ฑฐ๋ž˜ ์ •๋ณด๋ฅผ ์ƒ์„ธํ•˜๊ฒŒ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์–ด. False๋กœ ํ•˜๋ฉด ๊ฑฐ๋ž˜ ํ•ด์‹œ๋งŒ ๋ฐ›๊ฒŒ ๋ผ. ๋ฐ์ดํ„ฐ ์–‘์ด ๋งŽ์œผ๋‹ˆ๊นŒ ํ•„์š”์— ๋”ฐ๋ผ ์„ ํƒํ•˜๋ฉด ๋ผ! ๐Ÿ“š

๐Ÿ’ธ ๊ฑฐ๋ž˜ ๋‚ด์—ญ ์ƒ์„ธ ๋ถ„์„

๋ธ”๋ก ์ •๋ณด๋„ ์ข‹์ง€๋งŒ, ์ง„์งœ ์ค‘์š”ํ•œ ๊ฑด ๊ฑฐ๋ž˜ ๋‚ด์—ญ์ด์ง€! ๋ˆ„๊ฐ€ ๋ˆ„๊ตฌ์—๊ฒŒ ์–ผ๋งˆ๋ฅผ ๋ณด๋ƒˆ๋Š”์ง€, ์ˆ˜์ˆ˜๋ฃŒ๋Š” ์–ผ๋งˆ๋‚˜ ๋ƒˆ๋Š”์ง€ ๋“ฑ์„ ๋ถ„์„ํ•ด๋ณด์ž. ๐Ÿ’ฐ
๐Ÿ Python ์ฝ”๋“œ - ๊ฑฐ๋ž˜ ๋‚ด์—ญ ๋ถ„์„
def analyze_transactions(block_number):
    """
    ๋ธ”๋ก ๋‚ด ๋ชจ๋“  ๊ฑฐ๋ž˜๋ฅผ ๋ถ„์„ํ•˜๋Š” ํ•จ์ˆ˜
    """
    block = w3.eth.get_block(block_number, full_transactions=True)
    transactions = []
    
    for tx in block['transactions']:
        # Wei๋ฅผ Ether๋กœ ๋ณ€ํ™˜
        value_in_ether = w3.from_wei(tx['value'], 'ether')
        
        # ๊ฐ€์Šค๋น„ ๊ณ„์‚ฐ
        gas_price = w3.from_wei(tx['gasPrice'], 'gwei')
        
        tx_data = {
            'hash': tx['hash'].hex(),
            'from': tx['from'],
            'to': tx['to'] if tx['to'] else 'Contract Creation',
            'value': float(value_in_ether),
            'gas': tx['gas'],
            'gas_price': float(gas_price),
            'nonce': tx['nonce'],
            'transaction_index': tx['transactionIndex']
        }
        
        transactions.append(tx_data)
    
    return transactions

# ๊ฑฐ๋ž˜ ๋ถ„์„ ์‹คํ–‰
latest_block = w3.eth.block_number
transactions = analyze_transactions(latest_block)

print(f"\n๐Ÿ’ผ ๋ธ”๋ก #{latest_block}์˜ ๊ฑฐ๋ž˜ ๋ถ„์„")
print(f"์ด ๊ฑฐ๋ž˜ ์ˆ˜: {len(transactions)}")

# ๊ฑฐ๋ž˜๋Ÿ‰ ํ†ต๊ณ„
total_value = sum(tx['value'] for tx in transactions)
avg_gas_price = sum(tx['gas_price'] for tx in transactions) / len(transactions) if transactions else 0

print(f"๐Ÿ’ฐ ์ด ๊ฑฐ๋ž˜๋Ÿ‰: {total_value:.4f} ETH")
print(f"โ›ฝ ํ‰๊ท  ๊ฐ€์Šค ๊ฐ€๊ฒฉ: {avg_gas_price:.2f} Gwei")

# ์ƒ์œ„ 5๊ฐœ ๊ฑฐ๋ž˜ ์ถœ๋ ฅ
print("\n๐Ÿ” ์ƒ์œ„ 5๊ฐœ ๊ฑฐ๋ž˜:")
sorted_txs = sorted(transactions, key=lambda x: x['value'], reverse=True)[:5]
for i, tx in enumerate(sorted_txs, 1):
    print(f"{i}. {tx['value']:.4f} ETH - {tx['hash'][:10]}...")
๐Ÿ’ก ๋‹จ์œ„ ๋ณ€ํ™˜ ๊ฟ€ํŒ

์ด๋”๋ฆฌ์›€์—์„œ๋Š” Wei๋ผ๋Š” ๊ฐ€์žฅ ์ž‘์€ ๋‹จ์œ„๋ฅผ ์‚ฌ์šฉํ•ด. 1 ETH = 10^18 Wei์•ผ! ๊ทธ๋ž˜์„œ w3.from_wei() ํ•จ์ˆ˜๋กœ ๋ณ€ํ™˜ํ•ด์ค˜์•ผ ์‚ฌ๋žŒ์ด ์ฝ๊ธฐ ํŽธํ•œ ์ˆซ์ž๊ฐ€ ๋ผ. ๊ฐ€์Šค ๊ฐ€๊ฒฉ์€ ๋ณดํ†ต Gwei(10^9 Wei) ๋‹จ์œ„๋กœ ํ‘œ์‹œํ•˜๊ณ ! ๐Ÿ”ข

๐Ÿ“Š Pandas๋ฅผ ํ™œ์šฉํ•œ ๋ฐ์ดํ„ฐ ๋ถ„์„

์ด์ œ ์ˆ˜์ง‘ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ Pandas๋กœ ์ •๋ฆฌํ•˜๊ณ  ๋ถ„์„ํ•ด๋ณด์ž! ํ‘œ ํ˜•ํƒœ๋กœ ์ •๋ฆฌํ•˜๋ฉด ํ›จ์”ฌ ๋ณด๊ธฐ ์ข‹๊ณ  ๋ถ„์„ํ•˜๊ธฐ๋„ ์‰ฌ์›Œ์ ธ. ๐ŸŽฏ

๐Ÿ“ˆ DataFrame์œผ๋กœ ๋ฐ์ดํ„ฐ ์ •๋ฆฌ

๐Ÿ Python ์ฝ”๋“œ - Pandas ๋ฐ์ดํ„ฐ ๋ถ„์„
import pandas as pd
from datetime import datetime

def create_transaction_dataframe(start_block, end_block):
    """
    ์—ฌ๋Ÿฌ ๋ธ”๋ก์˜ ๊ฑฐ๋ž˜ ๋ฐ์ดํ„ฐ๋ฅผ DataFrame์œผ๋กœ ์ƒ์„ฑ
    """
    all_transactions = []
    
    print(f"๐Ÿ“ฅ ๋ธ”๋ก {start_block}๋ถ€ํ„ฐ {end_block}๊นŒ์ง€ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ ์ค‘...")
    
    for block_num in range(start_block, end_block + 1):
        try:
            block = w3.eth.get_block(block_num, full_transactions=True)
            block_timestamp = datetime.fromtimestamp(block['timestamp'])
            
            for tx in block['transactions']:
                tx_data = {
                    'block_number': block_num,
                    'timestamp': block_timestamp,
                    'tx_hash': tx['hash'].hex(),
                    'from_address': tx['from'],
                    'to_address': tx['to'] if tx['to'] else 'Contract',
                    'value_eth': float(w3.from_wei(tx['value'], 'ether')),
                    'gas_price_gwei': float(w3.from_wei(tx['gasPrice'], 'gwei')),
                    'gas_used': tx['gas'],
                    'tx_fee_eth': float(w3.from_wei(tx['gas'] * tx['gasPrice'], 'ether'))
                }
                all_transactions.append(tx_data)
            
            if block_num % 10 == 0:
                print(f"โœ… ๋ธ”๋ก {block_num} ์ฒ˜๋ฆฌ ์™„๋ฃŒ")
                
        except Exception as e:
            print(f"โŒ ๋ธ”๋ก {block_num} ์ฒ˜๋ฆฌ ์‹คํŒจ: {e}")
            continue
    
    df = pd.DataFrame(all_transactions)
    return df

# ์ตœ๊ทผ 50๊ฐœ ๋ธ”๋ก ๋ถ„์„
latest = w3.eth.block_number
df_transactions = create_transaction_dataframe(latest - 50, latest)

print(f"\n๐Ÿ“Š ์ˆ˜์ง‘๋œ ๋ฐ์ดํ„ฐ: {len(df_transactions)} ๊ฑด")
print("\n๋ฐ์ดํ„ฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ:")
print(df_transactions.head())

๐Ÿ”ฌ ํ†ต๊ณ„ ๋ถ„์„ ๋ฐ ์ธ์‚ฌ์ดํŠธ ๋„์ถœ

DataFrame์ด ์ค€๋น„๋์œผ๋ฉด ์ด์ œ ๋ณธ๊ฒฉ์ ์ธ ๋ถ„์„ ์‹œ๊ฐ„! ๋‹ค์–‘ํ•œ ํ†ต๊ณ„๋ฅผ ๋ฝ‘์•„๋‚ด๊ณ  ์˜๋ฏธ์žˆ๋Š” ํŒจํ„ด์„ ์ฐพ์•„๋ณด์ž. ๐Ÿ“‰
๐Ÿ Python ์ฝ”๋“œ - ํ†ต๊ณ„ ๋ถ„์„
# ๊ธฐ๋ณธ ํ†ต๊ณ„ ์ •๋ณด
print("\n๐Ÿ“ˆ ๊ฑฐ๋ž˜ ๊ธˆ์•ก ํ†ต๊ณ„")
print(df_transactions['value_eth'].describe())

print("\nโ›ฝ ๊ฐ€์Šค ๊ฐ€๊ฒฉ ํ†ต๊ณ„")
print(df_transactions['gas_price_gwei'].describe())

# ์‹œ๊ฐ„๋Œ€๋ณ„ ๊ฑฐ๋ž˜๋Ÿ‰ ๋ถ„์„
df_transactions['hour'] = df_transactions['timestamp'].dt.hour
hourly_stats = df_transactions.groupby('hour').agg({
    'value_eth': ['sum', 'mean', 'count'],
    'gas_price_gwei': 'mean'
}).round(4)

print("\nโฐ ์‹œ๊ฐ„๋Œ€๋ณ„ ๊ฑฐ๋ž˜ ํŒจํ„ด")
print(hourly_stats)

# ๊ณ ์•ก ๊ฑฐ๋ž˜ ํ•„ํ„ฐ๋ง
high_value_txs = df_transactions[df_transactions['value_eth'] > 10]
print(f"\n๐Ÿ’Ž 10 ETH ์ด์ƒ ๊ณ ์•ก ๊ฑฐ๋ž˜: {len(high_value_txs)}๊ฑด")
print(high_value_txs[['tx_hash', 'value_eth', 'from_address', 'to_address']].head())

# ๊ฐ€์žฅ ํ™œ๋ฐœํ•œ ์ฃผ์†Œ ์ฐพ๊ธฐ
top_senders = df_transactions['from_address'].value_counts().head(10)
print("\n๐Ÿ”ฅ ๊ฐ€์žฅ ํ™œ๋ฐœํ•œ ์†ก์‹  ์ฃผ์†Œ TOP 10")
for i, (address, count) in enumerate(top_senders.items(), 1):
    print(f"{i}. {address}: {count}๊ฑด")

# ์ปจํŠธ๋ž™ํŠธ ์ƒ์„ฑ ๊ฑฐ๋ž˜ ๋ถ„์„
contract_creations = df_transactions[df_transactions['to_address'] == 'Contract']
print(f"\n๐Ÿ—๏ธ ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ ์ƒ์„ฑ: {len(contract_creations)}๊ฑด")
๐ŸŽฏ ๋ถ„์„ ํฌ์ธํŠธ

โ€ข ์‹œ๊ฐ„๋Œ€๋ณ„ ํŒจํ„ด: ํŠน์ • ์‹œ๊ฐ„๋Œ€์— ๊ฑฐ๋ž˜๊ฐ€ ๋ชฐ๋ฆฌ๋Š”์ง€ ํ™•์ธ
โ€ข ๊ณ ์•ก ๊ฑฐ๋ž˜: ๊ณ ๋ž˜(๋Œ€๋Ÿ‰ ๋ณด์œ ์ž)์˜ ์›€์ง์ž„ ์ถ”์ 
โ€ข ํ™œ๋ฐœํ•œ ์ฃผ์†Œ: ๊ฑฐ๋ž˜์†Œ๋‚˜ ์ฃผ์š” ์„œ๋น„์Šค ์ฃผ์†Œ ํŒŒ์•…
โ€ข ๊ฐ€์Šค ๊ฐ€๊ฒฉ ๋ณ€๋™: ๋„คํŠธ์›Œํฌ ํ˜ผ์žก๋„ ์ธก์ •
โ€ข ์ปจํŠธ๋ž™ํŠธ ์ƒ์„ฑ: ์ƒˆ๋กœ์šด ํ”„๋กœ์ ํŠธ ๋Ÿฐ์นญ ๋ชจ๋‹ˆํ„ฐ๋ง

๐ŸŽจ ๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™”

์ˆซ์ž๋งŒ ๋ณด๋ฉด ์ง€๋ฃจํ•˜์ž–์•„? ๊ทธ๋ž˜ํ”„๋กœ ์‹œ๊ฐํ™”ํ•˜๋ฉด ํŒจํ„ด์ด ํ•œ๋ˆˆ์— ๋ณด์—ฌ! Matplotlib๊ณผ Seaborn์„ ํ™œ์šฉํ•ด๋ณด์ž. ๐Ÿ“Š

๐Ÿ Python ์ฝ”๋“œ - ๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™”
import matplotlib.pyplot as plt
import seaborn as sns

# ํ•œ๊ธ€ ํฐํŠธ ์„ค์ • (์„ ํƒ์‚ฌํ•ญ)
plt.rcParams['font.family'] = 'DejaVu Sans'
plt.rcParams['axes.unicode_minus'] = False

# ์Šคํƒ€์ผ ์„ค์ •
sns.set_style("whitegrid")
plt.figure(figsize=(15, 10))

# 1. ์‹œ๊ฐ„๋Œ€๋ณ„ ๊ฑฐ๋ž˜๋Ÿ‰ ์ถ”์ด
plt.subplot(2, 2, 1)
hourly_volume = df_transactions.groupby('hour')['value_eth'].sum()
plt.plot(hourly_volume.index, hourly_volume.values, marker='o', linewidth=2, color='#667eea')
plt.title('์‹œ๊ฐ„๋Œ€๋ณ„ ์ด ๊ฑฐ๋ž˜๋Ÿ‰', fontsize=14, fontweight='bold')
plt.xlabel('์‹œ๊ฐ„ (Hour)')
plt.ylabel('๊ฑฐ๋ž˜๋Ÿ‰ (ETH)')
plt.grid(True, alpha=0.3)

# 2. ๊ฐ€์Šค ๊ฐ€๊ฒฉ ๋ถ„ํฌ
plt.subplot(2, 2, 2)
plt.hist(df_transactions['gas_price_gwei'], bins=50, color='#764ba2', alpha=0.7, edgecolor='black')
plt.title('๊ฐ€์Šค ๊ฐ€๊ฒฉ ๋ถ„ํฌ', fontsize=14, fontweight='bold')
plt.xlabel('๊ฐ€์Šค ๊ฐ€๊ฒฉ (Gwei)')
plt.ylabel('๋นˆ๋„')
plt.grid(True, alpha=0.3)

# 3. ๊ฑฐ๋ž˜ ๊ธˆ์•ก ๋ถ„ํฌ (๋กœ๊ทธ ์Šค์ผ€์ผ)
plt.subplot(2, 2, 3)
value_data = df_transactions[df_transactions['value_eth'] > 0]['value_eth']
plt.hist(value_data, bins=50, color='#22c55e', alpha=0.7, edgecolor='black')
plt.yscale('log')
plt.title('๊ฑฐ๋ž˜ ๊ธˆ์•ก ๋ถ„ํฌ (๋กœ๊ทธ ์Šค์ผ€์ผ)', fontsize=14, fontweight='bold')
plt.xlabel('๊ฑฐ๋ž˜ ๊ธˆ์•ก (ETH)')
plt.ylabel('๋นˆ๋„ (๋กœ๊ทธ)')
plt.grid(True, alpha=0.3)

# 4. ๋ธ”๋ก๋ณ„ ๊ฑฐ๋ž˜ ์ˆ˜
plt.subplot(2, 2, 4)
block_tx_count = df_transactions.groupby('block_number').size()
plt.bar(block_tx_count.index, block_tx_count.values, color='#f59e0b', alpha=0.7)
plt.title('๋ธ”๋ก๋ณ„ ๊ฑฐ๋ž˜ ์ˆ˜', fontsize=14, fontweight='bold')
plt.xlabel('๋ธ”๋ก ๋ฒˆํ˜ธ')
plt.ylabel('๊ฑฐ๋ž˜ ์ˆ˜')
plt.xticks(rotation=45)
plt.grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig('blockchain_analysis.png', dpi=300, bbox_inches='tight')
print("\n๐Ÿ“Š ๊ทธ๋ž˜ํ”„๊ฐ€ 'blockchain_analysis.png'๋กœ ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!")
๐Ÿ’ก ์‹œ๊ฐํ™” ํŒ

๊ฑฐ๋ž˜ ๊ธˆ์•ก์€ ํŽธ์ฐจ๊ฐ€ ํฌ๊ธฐ ๋•Œ๋ฌธ์— ๋กœ๊ทธ ์Šค์ผ€์ผ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹์•„! 0.001 ETH๋ถ€ํ„ฐ 1000 ETH๊นŒ์ง€ ๋‹ค์–‘ํ•œ ๊ฑฐ๋ž˜๊ฐ€ ์„ž์—ฌ์žˆ๊ฑฐ๋“ . ๋กœ๊ทธ ์Šค์ผ€์ผ์„ ์“ฐ๋ฉด ์ „์ฒด ๋ถ„ํฌ๋ฅผ ํ•œ๋ˆˆ์— ๋ณผ ์ˆ˜ ์žˆ์–ด! ๐Ÿ“ˆ

๐Ÿค– ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ ๊ตฌ์ถ•

์ง€๊ธˆ๊นŒ์ง€๋Š” ๊ณผ๊ฑฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์„ํ–ˆ๋‹ค๋ฉด, ์ด์ œ๋Š” ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ƒˆ๋กœ์šด ๋ธ”๋ก๊ณผ ๊ฑฐ๋ž˜๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๋Š” ์‹œ์Šคํ…œ์„ ๋งŒ๋“ค์–ด๋ณด์ž! ๐Ÿšจ

โšก ์‹ค์‹œ๊ฐ„ ๋ธ”๋ก ๊ฐ์‹œ

๐Ÿ Python ์ฝ”๋“œ - ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง
import time
from datetime import datetime

class BlockchainMonitor:
    def __init__(self, w3_instance):
        self.w3 = w3_instance
        self.last_block = self.w3.eth.block_number
        self.alert_threshold = 100  # ETH ๋‹จ์œ„
        
    def check_new_blocks(self):
        """
        ์ƒˆ๋กœ์šด ๋ธ”๋ก์„ ํ™•์ธํ•˜๊ณ  ๋ถ„์„
        """
        current_block = self.w3.eth.block_number
        
        if current_block > self.last_block:
            print(f"\n๐Ÿ†• ์ƒˆ๋กœ์šด ๋ธ”๋ก ๋ฐœ๊ฒฌ: #{current_block}")
            
            # ์ƒˆ๋กœ์šด ๋ธ”๋ก๋“ค ์ฒ˜๋ฆฌ
            for block_num in range(self.last_block + 1, current_block + 1):
                self.analyze_block(block_num)
            
            self.last_block = current_block
    
    def analyze_block(self, block_number):
        """
        ๋ธ”๋ก ๋ถ„์„ ๋ฐ ์•Œ๋ฆผ
        """
        try:
            block = self.w3.eth.get_block(block_number, full_transactions=True)
            timestamp = datetime.fromtimestamp(block['timestamp'])
            
            print(f"\n๐Ÿ“ฆ ๋ธ”๋ก #{block_number}")
            print(f"โฐ ์‹œ๊ฐ„: {timestamp.strftime('%Y-%m-%d %H:%M:%S')}")
            print(f"๐Ÿ’ผ ๊ฑฐ๋ž˜ ์ˆ˜: {len(block['transactions'])}")
            
            # ๊ณ ์•ก ๊ฑฐ๋ž˜ ๊ฐ์ง€
            for tx in block['transactions']:
                value_eth = float(self.w3.from_wei(tx['value'], 'ether'))
                
                if value_eth >= self.alert_threshold:
                    print(f"\n๐Ÿšจ ๊ณ ์•ก ๊ฑฐ๋ž˜ ๊ฐ์ง€!")
                    print(f"   ๊ธˆ์•ก: {value_eth:.2f} ETH")
                    print(f"   From: {tx['from']}")
                    print(f"   To: {tx['to'] if tx['to'] else 'Contract Creation'}")
                    print(f"   Hash: {tx['hash'].hex()}")
                    
                    # ์—ฌ๊ธฐ์— ์•Œ๋ฆผ ๋กœ์ง ์ถ”๊ฐ€ (์ด๋ฉ”์ผ, ํ…”๋ ˆ๊ทธ๋žจ ๋“ฑ)
                    self.send_alert(tx, value_eth)
        
        except Exception as e:
            print(f"โŒ ๋ธ”๋ก ๋ถ„์„ ์‹คํŒจ: {e}")
    
    def send_alert(self, transaction, value):
        """
        ์•Œ๋ฆผ ์ „์†ก (์˜ˆ์‹œ)
        """
        # ์‹ค์ œ๋กœ๋Š” ์ด๋ฉ”์ผ, SMS, ํ…”๋ ˆ๊ทธ๋žจ ๋“ฑ์œผ๋กœ ์•Œ๋ฆผ ์ „์†ก
        alert_message = f"๊ณ ์•ก ๊ฑฐ๋ž˜ ๊ฐ์ง€: {value:.2f} ETH"
        print(f"๐Ÿ“ง ์•Œ๋ฆผ ์ „์†ก: {alert_message}")
    
    def run(self, interval=12):
        """
        ๋ชจ๋‹ˆํ„ฐ๋ง ์‹คํ–‰ (interval: ํ™•์ธ ์ฃผ๊ธฐ, ์ดˆ ๋‹จ์œ„)
        """
        print("๐Ÿ” ๋ธ”๋ก์ฒด์ธ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์ž‘...")
        print(f"ํ˜„์žฌ ๋ธ”๋ก: #{self.last_block}")
        print(f"์•Œ๋ฆผ ์ž„๊ณ„๊ฐ’: {self.alert_threshold} ETH")
        
        try:
            while True:
                self.check_new_blocks()
                time.sleep(interval)
        except KeyboardInterrupt:
            print("\nโน๏ธ ๋ชจ๋‹ˆํ„ฐ๋ง ์ข…๋ฃŒ")

# ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์ž‘
monitor = BlockchainMonitor(w3)
monitor.alert_threshold = 50  # 50 ETH ์ด์ƒ ๊ฑฐ๋ž˜ ์•Œ๋ฆผ

# ์‹คํ–‰ (Ctrl+C๋กœ ์ข…๋ฃŒ)
# monitor.run(interval=15)  # 15์ดˆ๋งˆ๋‹ค ํ™•์ธ
โš ๏ธ ์‹คํ–‰ ์‹œ ์ฃผ์˜์‚ฌํ•ญ

์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง์€ API ํ˜ธ์ถœ์„ ๊ณ„์†ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฌด๋ฃŒ ํ”Œ๋žœ์˜ ์š”์ฒญ ์ œํ•œ์— ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์–ด! Infura ๋ฌด๋ฃŒ ํ”Œ๋žœ์€ ํ•˜๋ฃจ 100,000 ์š”์ฒญ๊นŒ์ง€ ๊ฐ€๋Šฅํ•ด. interval์„ ๋„ˆ๋ฌด ์งง๊ฒŒ ์„ค์ •ํ•˜์ง€ ๋ง๊ณ , ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์œ ๋ฃŒ ํ”Œ๋žœ์„ ๊ณ ๋ คํ•ด๋ด! ๐Ÿ’ธ

๐ŸŽฏ ํŠน์ • ์ฃผ์†Œ ์ถ”์  ์‹œ์Šคํ…œ

ํŠน์ • ์ง€๊ฐ‘ ์ฃผ์†Œ์˜ ๋ชจ๋“  ๊ฑฐ๋ž˜๋ฅผ ์ถ”์ ํ•˜๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ์žˆ์ง€? ๊ณ ๋ž˜ ์ง€๊ฐ‘์ด๋‚˜ ๊ฑฐ๋ž˜์†Œ ์ง€๊ฐ‘์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๋ฉด ์‹œ์žฅ ์›€์ง์ž„์„ ์˜ˆ์ธกํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿ‹

๐Ÿ Python ์ฝ”๋“œ - ์ฃผ์†Œ ์ถ”์ 
class AddressTracker:
    def __init__(self, w3_instance, target_address):
        self.w3 = w3_instance
        self.target_address = w3_instance.to_checksum_address(target_address)
        self.transaction_history = []
        
    def get_balance(self):
        """
        ํ˜„์žฌ ์ž”์•ก ์กฐํšŒ
        """
        balance_wei = self.w3.eth.get_balance(self.target_address)
        balance_eth = self.w3.from_wei(balance_wei, 'ether')
        return float(balance_eth)
    
    def scan_blocks(self, start_block, end_block):
        """
        ํŠน์ • ๋ธ”๋ก ๋ฒ”์œ„์—์„œ ํ•ด๋‹น ์ฃผ์†Œ์˜ ๊ฑฐ๋ž˜ ์ฐพ๊ธฐ
        """
        print(f"๐Ÿ” ์ฃผ์†Œ {self.target_address} ์Šค์บ” ์ค‘...")
        found_transactions = []
        
        for block_num in range(start_block, end_block + 1):
            try:
                block = self.w3.eth.get_block(block_num, full_transactions=True)
                
                for tx in block['transactions']:
                    # ์†ก์‹ ์ž ๋˜๋Š” ์ˆ˜์‹ ์ž๊ฐ€ ํƒ€๊ฒŸ ์ฃผ์†Œ์ธ ๊ฒฝ์šฐ
                    if tx['from'] == self.target_address or tx['to'] == self.target_address:
                        tx_data = {
                            'block': block_num,
                            'timestamp': datetime.fromtimestamp(block['timestamp']),
                            'hash': tx['hash'].hex(),
                            'from': tx['from'],
                            'to': tx['to'] if tx['to'] else 'Contract',
                            'value': float(self.w3.from_wei(tx['value'], 'ether')),
                            'type': 'OUT' if tx['from'] == self.target_address else 'IN'
                        }
                        found_transactions.append(tx_data)
                        print(f"โœ… ๊ฑฐ๋ž˜ ๋ฐœ๊ฒฌ: ๋ธ”๋ก #{block_num}")
                
                if block_num % 100 == 0:
                    print(f"์ง„ํ–‰๋ฅ : ๋ธ”๋ก {block_num}/{end_block}")
                    
            except Exception as e:
                print(f"โŒ ๋ธ”๋ก {block_num} ์Šค์บ” ์‹คํŒจ: {e}")
                continue
        
        self.transaction_history.extend(found_transactions)
        return found_transactions
    
    def analyze_activity(self):
        """
        ๊ฑฐ๋ž˜ ํ™œ๋™ ๋ถ„์„
        """
        if not self.transaction_history:
            print("๊ฑฐ๋ž˜ ๋‚ด์—ญ์ด ์—†์Šต๋‹ˆ๋‹ค.")
            return
        
        df = pd.DataFrame(self.transaction_history)
        
        print(f"\n๐Ÿ“Š {self.target_address} ํ™œ๋™ ๋ถ„์„")
        print(f"์ด ๊ฑฐ๋ž˜ ์ˆ˜: {len(df)}")
        
        # ์ž…์ถœ๊ธˆ ํ†ต๊ณ„
        incoming = df[df['type'] == 'IN']
        outgoing = df[df['type'] == 'OUT']
        
        print(f"\n๐Ÿ’ฐ ์ž…๊ธˆ ๊ฑฐ๋ž˜: {len(incoming)}๊ฑด")
        print(f"   ์ด ์ž…๊ธˆ์•ก: {incoming['value'].sum():.4f} ETH")
        print(f"   ํ‰๊ท  ์ž…๊ธˆ์•ก: {incoming['value'].mean():.4f} ETH")
        
        print(f"\n๐Ÿ’ธ ์ถœ๊ธˆ ๊ฑฐ๋ž˜: {len(outgoing)}๊ฑด")
        print(f"   ์ด ์ถœ๊ธˆ์•ก: {outgoing['value'].sum():.4f} ETH")
        print(f"   ํ‰๊ท  ์ถœ๊ธˆ์•ก: {outgoing['value'].mean():.4f} ETH")
        
        print(f"\n๐Ÿ’ผ ํ˜„์žฌ ์ž”์•ก: {self.get_balance():.4f} ETH")
        
        # ๊ฑฐ๋ž˜ ์ƒ๋Œ€๋ฐฉ ๋ถ„์„
        print(f"\n๐Ÿค ์ฃผ์š” ๊ฑฐ๋ž˜ ์ƒ๋Œ€๋ฐฉ TOP 5")
        counterparties = []
        for _, tx in df.iterrows():
            if tx['type'] == 'IN':
                counterparties.append(tx['from'])
            else:
                counterparties.append(tx['to'])
        
        from collections import Counter
        top_counterparties = Counter(counterparties).most_common(5)
        for i, (address, count) in enumerate(top_counterparties, 1):
            print(f"{i}. {address}: {count}๊ฑด")
        
        return df

# ์‚ฌ์šฉ ์˜ˆ์‹œ
target_wallet = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"  # ์˜ˆ์‹œ ์ฃผ์†Œ
tracker = AddressTracker(w3, target_wallet)

# ์ตœ๊ทผ 1000๋ธ”๋ก ์Šค์บ”
latest = w3.eth.block_number
transactions = tracker.scan_blocks(latest - 1000, latest)

# ๋ถ„์„ ๊ฒฐ๊ณผ ์ถœ๋ ฅ
analysis_df = tracker.analyze_activity()
๐ŸŽฏ ํ™œ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค

โ€ข ๊ณ ๋ž˜ ์ถ”์ : ๋Œ€๋Ÿ‰ ๋ณด์œ ์ž์˜ ๋งค์ˆ˜/๋งค๋„ ํŒจํ„ด ๋ถ„์„
โ€ข ๊ฑฐ๋ž˜์†Œ ๋ชจ๋‹ˆํ„ฐ๋ง: ๊ฑฐ๋ž˜์†Œ ์ž…์ถœ๊ธˆ ํ๋ฆ„ ํŒŒ์•…
โ€ข ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ ๋ถ„์„: DeFi ํ”„๋กœํ† ์ฝœ ์‚ฌ์šฉ ํŒจํ„ด ์—ฐ๊ตฌ
โ€ข ํฌํŠธํด๋ฆฌ์˜ค ๊ด€๋ฆฌ: ์ž์‹ ์˜ ์ง€๊ฐ‘ ๊ฑฐ๋ž˜ ๋‚ด์—ญ ์ž๋™ ๊ธฐ๋ก
โ€ข ์„ธ๋ฌด ์ž๋ฃŒ: ์•”ํ˜ธํ™”ํ ๊ฑฐ๋ž˜ ๋‚ด์—ญ ์ •๋ฆฌ

๐Ÿ” ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ ์ƒํ˜ธ์ž‘์šฉ ๋ถ„์„

์ด๋”๋ฆฌ์›€์˜ ์ง„์งœ ๋งค๋ ฅ์€ ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ์ž–์•„? ERC-20 ํ† ํฐ, NFT, DeFi ํ”„๋กœํ† ์ฝœ ๋“ฑ ๋ชจ๋“  ๊ฒŒ ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ๋กœ ๋™์ž‘ํ•ด. ์ด๊ฒƒ๋“ค๊ณผ์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ๋ถ„์„ํ•ด๋ณด์ž! ๐ŸŽจ

๐Ÿช™ ERC-20 ํ† ํฐ ์ „์†ก ์ถ”์ 

๐Ÿ Python ์ฝ”๋“œ - ERC-20 ํ† ํฐ ๋ถ„์„
from web3 import Web3

# ERC-20 ํ‘œ์ค€ ABI (Transfer ์ด๋ฒคํŠธ)
ERC20_ABI = [
    {
        "anonymous": False,
        "inputs": [
            {"indexed": True, "name": "from", "type": "address"},
            {"indexed": True, "name": "to", "type": "address"},
            {"indexed": False, "name": "value", "type": "uint256"}
        ],
        "name": "Transfer",
        "type": "event"
    },
    {
        "constant": True,
        "inputs": [],
        "name": "name",
        "outputs": [{"name": "", "type": "string"}],
        "type": "function"
    },
    {
        "constant": True,
        "inputs": [],
        "name": "symbol",
        "outputs": [{"name": "", "type": "string"}],
        "type": "function"
    },
    {
        "constant": True,
        "inputs": [],
        "name": "decimals",
        "outputs": [{"name": "", "type": "uint8"}],
        "type": "function"
    },
    {
        "constant": True,
        "inputs": [{"name": "_owner", "type": "address"}],
        "name": "balanceOf",
        "outputs": [{"name": "balance", "type": "uint256"}],
        "type": "function"
    }
]

class TokenAnalyzer:
    def __init__(self, w3_instance, token_address):
        self.w3 = w3_instance
        self.token_address = w3_instance.to_checksum_address(token_address)
        self.contract = w3_instance.eth.contract(
            address=self.token_address,
            abi=ERC20_ABI
        )
        
    def get_token_info(self):
        """
        ํ† ํฐ ๊ธฐ๋ณธ ์ •๋ณด ์กฐํšŒ
        """
        try:
            name = self.contract.functions.name().call()
            symbol = self.contract.functions.symbol().call()
            decimals = self.contract.functions.decimals().call()
            
            return {
                'name': name,
                'symbol': symbol,
                'decimals': decimals,
                'address': self.token_address
            }
        except Exception as e:
            print(f"โŒ ํ† ํฐ ์ •๋ณด ์กฐํšŒ ์‹คํŒจ: {e}")
            return None
    
    def get_balance(self, address):
        """
        ํŠน์ • ์ฃผ์†Œ์˜ ํ† ํฐ ์ž”์•ก ์กฐํšŒ
        """
        try:
            address = self.w3.to_checksum_address(address)
            balance = self.contract.functions.balanceOf(address).call()
            decimals = self.contract.functions.decimals().call()
            
            # ์‹ค์ œ ์ž”์•ก์œผ๋กœ ๋ณ€ํ™˜
            actual_balance = balance / (10 ** decimals)
            return actual_balance
        except Exception as e:
            print(f"โŒ ์ž”์•ก ์กฐํšŒ ์‹คํŒจ: {e}")
            return 0
    
    def get_transfer_events(self, from_block, to_block):
        """
        Transfer ์ด๋ฒคํŠธ ์กฐํšŒ
        """
        try:
            # Transfer ์ด๋ฒคํŠธ ํ•„ํ„ฐ ์ƒ์„ฑ
            event_filter = self.contract.events.Transfer.create_filter(
                fromBlock=from_block,
                toBlock=to_block
            )
            
            events = event_filter.get_all_entries()
            transfers = []
            
            decimals = self.contract.functions.decimals().call()
            
            for event in events:
                transfer_data = {
                    'block': event['blockNumber'],
                    'tx_hash': event['transactionHash'].hex(),
                    'from': event['args']['from'],
                    'to': event['args']['to'],
                    'value': event['args']['value'] / (10 ** decimals)
                }
                transfers.append(transfer_data)
            
            return transfers
        
        except Exception as e:
            print(f"โŒ ์ด๋ฒคํŠธ ์กฐํšŒ ์‹คํŒจ: {e}")
            return []
    
    def analyze_transfers(self, from_block, to_block):
        """
        ํ† ํฐ ์ „์†ก ๋ถ„์„
        """
        token_info = self.get_token_info()
        if not token_info:
            return
        
        print(f"\n๐Ÿช™ ํ† ํฐ ๋ถ„์„: {token_info['name']} ({token_info['symbol']})")
        print(f"๐Ÿ“ ์ปจํŠธ๋ž™ํŠธ ์ฃผ์†Œ: {token_info['address']}")
        
        transfers = self.get_transfer_events(from_block, to_block)
        
        if not transfers:
            print("์ „์†ก ๋‚ด์—ญ์ด ์—†์Šต๋‹ˆ๋‹ค.")
            return
        
        df = pd.DataFrame(transfers)
        
        print(f"\n๐Ÿ“Š ์ „์†ก ํ†ต๊ณ„ (๋ธ”๋ก {from_block} ~ {to_block})")
        print(f"์ด ์ „์†ก ํšŸ์ˆ˜: {len(df)}")
        print(f"์ด ์ „์†ก๋Ÿ‰: {df['value'].sum():,.2f} {token_info['symbol']}")
        print(f"ํ‰๊ท  ์ „์†ก๋Ÿ‰: {df['value'].mean():,.2f} {token_info['symbol']}")
        print(f"์ตœ๋Œ€ ์ „์†ก๋Ÿ‰: {df['value'].max():,.2f} {token_info['symbol']}")
        
        # ์ƒ์œ„ ์†ก์‹ ์ž
        print(f"\n๐Ÿ“ค ์ƒ์œ„ ์†ก์‹ ์ž TOP 5")
        top_senders = df['from'].value_counts().head(5)
        for i, (address, count) in enumerate(top_senders.items(), 1):
            total_sent = df[df['from'] == address]['value'].sum()
            print(f"{i}. {address[:10]}...: {count}๊ฑด, {total_sent:,.2f} {token_info['symbol']}")
        
        # ์ƒ์œ„ ์ˆ˜์‹ ์ž
        print(f"\n๐Ÿ“ฅ ์ƒ์œ„ ์ˆ˜์‹ ์ž TOP 5")
        top_receivers = df['to'].value_counts().head(5)
        for i, (address, count) in enumerate(top_receivers.items(), 1):
            total_received = df[df['to'] == address]['value'].sum()
            print(f"{i}. {address[:10]}...: {count}๊ฑด, {total_received:,.2f} {token_info['symbol']}")
        
        return df

# USDT ํ† ํฐ ๋ถ„์„ ์˜ˆ์‹œ
usdt_address = "0xdAC17F958D2ee523a2206206994597C13D831ec7"
token_analyzer = TokenAnalyzer(w3, usdt_address)

# ํ† ํฐ ์ •๋ณด ์กฐํšŒ
token_info = token_analyzer.get_token_info()
print(token_info)

# ์ตœ๊ทผ 100๋ธ”๋ก์˜ ์ „์†ก ๋‚ด์—ญ ๋ถ„์„
latest = w3.eth.block_number
transfer_df = token_analyzer.analyze_transfers(latest - 100, latest)
๐Ÿ’ก ABI๋ž€?

ABI(Application Binary Interface)๋Š” ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ์™€ ํ†ต์‹ ํ•˜๊ธฐ ์œ„ํ•œ ์ธํ„ฐํŽ˜์ด์Šค์•ผ. ์–ด๋–ค ํ•จ์ˆ˜๊ฐ€ ์žˆ๊ณ , ์–ด๋–ค ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š”์ง€ ์ •์˜๋˜์–ด ์žˆ์–ด. ERC-20 ๊ฐ™์€ ํ‘œ์ค€์€ ABI๊ฐ€ ์ •ํ•ด์ ธ ์žˆ์–ด์„œ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€! ๐ŸŽฏ

๐Ÿ“ก Etherscan API ํ™œ์šฉํ•˜๊ธฐ

Web3.py๋งŒ์œผ๋กœ๋Š” ํ•œ๊ณ„๊ฐ€ ์žˆ์„ ๋•Œ๊ฐ€ ์žˆ์–ด. Etherscan API๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ๋” ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์‰ฝ๊ฒŒ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์–ด! ๐Ÿš€

๐Ÿ Python ์ฝ”๋“œ - Etherscan API ํ™œ์šฉ
import requests
import time

class EtherscanAPI:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://api.etherscan.io/api"
        
    def get_normal_transactions(self, address, startblock=0, endblock=99999999):
        """
        ์ผ๋ฐ˜ ์ด๋”๋ฆฌ์›€ ๊ฑฐ๋ž˜ ๋‚ด์—ญ ์กฐํšŒ
        """
        params = {
            'module': 'account',
            'action': 'txlist',
            'address': address,
            'startblock': startblock,
            'endblock': endblock,
            'sort': 'desc',
            'apikey': self.api_key
        }
        
        response = requests.get(self.base_url, params=params)
        data = response.json()
        
        if data['status'] == '1':
            return data['result']
        else:
            print(f"โŒ API ์˜ค๋ฅ˜: {data['message']}")
            return []
    
    def get_token_transactions(self, address, contractaddress=None):
        """
        ERC-20 ํ† ํฐ ๊ฑฐ๋ž˜ ๋‚ด์—ญ ์กฐํšŒ
        """
        params = {
            'module': 'account',
            'action': 'tokentx',
            'address': address,
            'sort': 'desc',
            'apikey': self.api_key
        }
        
        if contractaddress:
            params['contractaddress'] = contractaddress
        
        response = requests.get(self.base_url, params=params)
        data = response.json()
        
        if data['status'] == '1':
            return data['result']
        else:
            print(f"โŒ API ์˜ค๋ฅ˜: {data['message']}")
            return []
    
    def get_internal_transactions(self, address):
        """
        ๋‚ด๋ถ€ ๊ฑฐ๋ž˜ ๋‚ด์—ญ ์กฐํšŒ (์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ ์‹คํ–‰)
        """
        params = {
            'module': 'account',
            'action': 'txlistinternal',
            'address': address,
            'sort': 'desc',
            'apikey': self.api_key
        }
        
        response = requests.get(self.base_url, params=params)
        data = response.json()
        
        if data['status'] == '1':
            return data['result']
        else:
            return []
    
    def get_contract_abi(self, contract_address):
        """
        ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ ABI ์กฐํšŒ
        """
        params = {
            'module': 'contract',
            'action': 'getabi',
            'address': contract_address,
            'apikey': self.api_key
        }
        
        response = requests.get(self.base_url, params=params)
        data = response.json()
        
        if data['status'] == '1':
            return data['result']
        else:
            print(f"โŒ ABI ์กฐํšŒ ์‹คํŒจ: {data['message']}")
            return None
    
    def get_eth_price(self):
        """
        ํ˜„์žฌ ์ด๋”๋ฆฌ์›€ ๊ฐ€๊ฒฉ ์กฐํšŒ
        """
        params = {
            'module': 'stats',
            'action': 'ethprice',
            'apikey': self.api_key
        }
        
        response = requests.get(self.base_url, params=params)
        data = response.json()
        
        if data['status'] == '1':
            return {
                'usd': float(data['result']['ethusd']),
                'btc': float(data['result']['ethbtc'])
            }
        return None
    
    def analyze_address_complete(self, address):
        """
        ์ฃผ์†Œ ์ข…ํ•ฉ ๋ถ„์„
        """
        print(f"\n๐Ÿ” ์ฃผ์†Œ ์ข…ํ•ฉ ๋ถ„์„: {address}\n")
        
        # 1. ์ผ๋ฐ˜ ๊ฑฐ๋ž˜
        print("๐Ÿ“Š ์ผ๋ฐ˜ ETH ๊ฑฐ๋ž˜ ์กฐํšŒ ์ค‘...")
        normal_txs = self.get_normal_transactions(address)
        print(f"โœ… {len(normal_txs)}๊ฑด ๋ฐœ๊ฒฌ")
        
        time.sleep(0.2)  # API ์ œํ•œ ๊ณ ๋ ค
        
        # 2. ํ† ํฐ ๊ฑฐ๋ž˜
        print("\n๐Ÿช™ ํ† ํฐ ๊ฑฐ๋ž˜ ์กฐํšŒ ์ค‘...")
        token_txs = self.get_token_transactions(address)
        print(f"โœ… {len(token_txs)}๊ฑด ๋ฐœ๊ฒฌ")
        
        time.sleep(0.2)
        
        # 3. ๋‚ด๋ถ€ ๊ฑฐ๋ž˜
        print("\n๐Ÿ”„ ๋‚ด๋ถ€ ๊ฑฐ๋ž˜ ์กฐํšŒ ์ค‘...")
        internal_txs = self.get_internal_transactions(address)
        print(f"โœ… {len(internal_txs)}๊ฑด ๋ฐœ๊ฒฌ")
        
        # ํ†ต๊ณ„ ๋ถ„์„
        if normal_txs:
            df_normal = pd.DataFrame(normal_txs)
            df_normal['value'] = df_normal['value'].astype(float) / 1e18
            
            print(f"\n๐Ÿ’ฐ ETH ๊ฑฐ๋ž˜ ํ†ต๊ณ„")
            print(f"์ด ๊ฑฐ๋ž˜ ์ˆ˜: {len(df_normal)}")
            print(f"์ด ๊ฑฐ๋ž˜๋Ÿ‰: {df_normal['value'].sum():.4f} ETH")
            
            # ์ž…์ถœ๊ธˆ ๊ตฌ๋ถ„
            incoming = df_normal[df_normal['to'].str.lower() == address.lower()]
            outgoing = df_normal[df_normal['from'].str.lower() == address.lower()]
            
            print(f"์ž…๊ธˆ: {len(incoming)}๊ฑด, {incoming['value'].sum():.4f} ETH")
            print(f"์ถœ๊ธˆ: {len(outgoing)}๊ฑด, {outgoing['value'].sum():.4f} ETH")
        
        if token_txs:
            df_tokens = pd.DataFrame(token_txs)
            
            print(f"\n๐Ÿช™ ํ† ํฐ ๊ฑฐ๋ž˜ ํ†ต๊ณ„")
            print(f"์ด ํ† ํฐ ๊ฑฐ๋ž˜: {len(df_tokens)}๊ฑด")
            
            # ๊ฑฐ๋ž˜๋œ ํ† ํฐ ์ข…๋ฅ˜
            unique_tokens = df_tokens['tokenSymbol'].value_counts()
            print(f"\n๊ฑฐ๋ž˜๋œ ํ† ํฐ ์ข…๋ฅ˜ ({len(unique_tokens)}๊ฐœ):")
            for token, count in unique_tokens.head(10).items():
                print(f"  โ€ข {token}: {count}๊ฑด")
        
        return {
            'normal': normal_txs,
            'token': token_txs,
            'internal': internal_txs
        }

# Etherscan API ์‚ฌ์šฉ ์˜ˆ์‹œ
etherscan = EtherscanAPI("YOUR_ETHERSCAN_API_KEY")

# ์ด๋”๋ฆฌ์›€ ๊ฐ€๊ฒฉ ์กฐํšŒ
eth_price = etherscan.get_eth_price()
print(f"๐Ÿ’ต ํ˜„์žฌ ETH ๊ฐ€๊ฒฉ: ${eth_price['usd']:,.2f}")

# ์ฃผ์†Œ ์ข…ํ•ฉ ๋ถ„์„
target_address = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
analysis_result = etherscan.analyze_address_complete(target_address)
๐ŸŽฏ Etherscan API์˜ ์žฅ์ 

โ€ข ๋น ๋ฅธ ์กฐํšŒ: ์ธ๋ฑ์‹ฑ๋œ ๋ฐ์ดํ„ฐ๋กœ ๋น ๋ฅธ ๊ฒ€์ƒ‰
โ€ข ๋‹ค์–‘ํ•œ ์ •๋ณด: ์ผ๋ฐ˜ ๊ฑฐ๋ž˜, ํ† ํฐ ๊ฑฐ๋ž˜, ๋‚ด๋ถ€ ๊ฑฐ๋ž˜ ๋ชจ๋‘ ์กฐํšŒ
โ€ข ๊ฒ€์ฆ๋œ ๋ฐ์ดํ„ฐ: Etherscan์ด ๊ฒ€์ฆํ•œ ์ •ํ™•ํ•œ ์ •๋ณด
โ€ข ๋ฌด๋ฃŒ ์‚ฌ์šฉ: ํ•˜๋ฃจ 100,000 ์š”์ฒญ๊นŒ์ง€ ๋ฌด๋ฃŒ
โ€ข ABI ์ œ๊ณต: ๊ฒ€์ฆ๋œ ์ปจํŠธ๋ž™ํŠธ์˜ ABI ์ž๋™ ์กฐํšŒ

๐Ÿ’พ ๋ฐ์ดํ„ฐ ์ €์žฅ ๋ฐ ๊ด€๋ฆฌ

์ˆ˜์ง‘ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•ด! ๋งค๋ฒˆ ๋ธ”๋ก์ฒด์ธ์—์„œ ๋‹ค์‹œ ๊ฐ€์ ธ์˜ค๋ฉด ์‹œ๊ฐ„๋„ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๊ณ  API ์ œํ•œ์—๋„ ๊ฑธ๋ฆฌ๊ฑฐ๋“ . ๐Ÿ—„๏ธ

๐Ÿ“ CSV ํŒŒ์ผ๋กœ ์ €์žฅ

๐Ÿ Python ์ฝ”๋“œ - ๋ฐ์ดํ„ฐ ์ €์žฅ
import os
from datetime import datetime

class DataManager:
    def __init__(self, data_dir='blockchain_data'):
        self.data_dir = data_dir
        
        # ๋ฐ์ดํ„ฐ ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ
        if not os.path.exists(data_dir):
            os.makedirs(data_dir)
            print(f"๐Ÿ“ ๋ฐ์ดํ„ฐ ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ: {data_dir}")
    
    def save_transactions(self, df, filename=None):
        """
        ๊ฑฐ๋ž˜ ๋ฐ์ดํ„ฐ๋ฅผ CSV๋กœ ์ €์žฅ
        """
        if filename is None:
            timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
            filename = f"transactions_{timestamp}.csv"
        
        filepath = os.path.join(self.data_dir, filename)
        df.to_csv(filepath, index=False, encoding='utf-8-sig')
        print(f"๐Ÿ’พ ๋ฐ์ดํ„ฐ ์ €์žฅ ์™„๋ฃŒ: {filepath}")
        print(f"   ์ด {len(df)}๊ฑด์˜ ๊ฑฐ๋ž˜ ์ €์žฅ")
        
        return filepath
    
    def load_transactions(self, filename):
        """
        ์ €์žฅ๋œ ๊ฑฐ๋ž˜ ๋ฐ์ดํ„ฐ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
        """
        filepath = os.path.join(self.data_dir, filename)
        
        if not os.path.exists(filepath):
            print(f"โŒ ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {filepath}")
            return None
        
        df = pd.read_csv(filepath)
        print(f"๐Ÿ“‚ ๋ฐ์ดํ„ฐ ๋กœ๋“œ ์™„๋ฃŒ: {len(df)}๊ฑด")
        return df
    
    def list_saved_files(self):
        """
        ์ €์žฅ๋œ ํŒŒ์ผ ๋ชฉ๋ก ์กฐํšŒ
        """
        files = [f for f in os.listdir(self.data_dir) if f.endswith('.csv')]
        
        if not files:
            print("์ €์žฅ๋œ ํŒŒ์ผ์ด ์—†์Šต๋‹ˆ๋‹ค.")
            return []
        
        print(f"\n๐Ÿ“š ์ €์žฅ๋œ ํŒŒ์ผ ๋ชฉ๋ก ({len(files)}๊ฐœ)")
        for i, file in enumerate(sorted(files), 1):
            filepath = os.path.join(self.data_dir, file)
            size = os.path.getsize(filepath) / 1024  # KB
            print(f"{i}. {file} ({size:.2f} KB)")
        
        return files
    
    def merge_data(self, filenames):
        """
        ์—ฌ๋Ÿฌ ํŒŒ์ผ์˜ ๋ฐ์ดํ„ฐ ๋ณ‘ํ•ฉ
        """
        dfs = []
        
        for filename in filenames:
            df = self.load_transactions(filename)
            if df is not None:
                dfs.append(df)
        
        if not dfs:
            print("๋ณ‘ํ•ฉํ•  ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
            return None
        
        merged_df = pd.concat(dfs, ignore_index=True)
        
        # ์ค‘๋ณต ์ œ๊ฑฐ (tx_hash ๊ธฐ์ค€)
        if 'tx_hash' in merged_df.columns:
            before = len(merged_df)
            merged_df = merged_df.drop_duplicates(subset=['tx_hash'])
            after = len(merged_df)
            print(f"๐Ÿ”„ ์ค‘๋ณต ์ œ๊ฑฐ: {before - after}๊ฑด")
        
        print(f"โœ… ๋ณ‘ํ•ฉ ์™„๋ฃŒ: ์ด {len(merged_df)}๊ฑด")
        return merged_df
    
    def export_to_excel(self, df, filename):
        """
        Excel ํŒŒ์ผ๋กœ ๋‚ด๋ณด๋‚ด๊ธฐ
        """
        filepath = os.path.join(self.data_dir, filename)
        
        try:
            df.to_excel(filepath, index=False, engine='openpyxl')
            print(f"๐Ÿ“Š Excel ํŒŒ์ผ ์ƒ์„ฑ: {filepath}")
        except ImportError:
            print("โŒ openpyxl ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค: pip install openpyxl")

# ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ์ž ์ƒ์„ฑ
data_manager = DataManager()

# ๊ฑฐ๋ž˜ ๋ฐ์ดํ„ฐ ์ €์žฅ
if 'df_transactions' in locals():
    saved_file = data_manager.save_transactions(df_transactions)

# ์ €์žฅ๋œ ํŒŒ์ผ ๋ชฉ๋ก ํ™•์ธ
data_manager.list_saved_files()

# ๋ฐ์ดํ„ฐ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
# loaded_df = data_manager.load_transactions('transactions_20240101_120000.csv')

๐Ÿ—ƒ๏ธ SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ™œ์šฉ

๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ํ›จ์”ฌ ํšจ์œจ์ ์ด์•ผ! SQLite๋Š” ์„ค์น˜ ์—†์ด ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์„œ ํŽธ๋ฆฌํ•ด. ๐Ÿ˜Š
๐Ÿ Python ์ฝ”๋“œ - SQLite ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค
import sqlite3
from contextlib import contextmanager

class BlockchainDB:
    def __init__(self, db_name='blockchain.db'):
        self.db_name = db_name
        self.init_database()
    
    @contextmanager
    def get_connection(self):
        """
        ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์ปจํ…์ŠคํŠธ ๋งค๋‹ˆ์ €
        """
        conn = sqlite3.connect(self.db_name)
        try:
            yield conn
        finally:
            conn.close()
    
    def init_database(self):
        """
        ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ” ์ดˆ๊ธฐํ™”
        """
        with self.get_connection() as conn:
            cursor = conn.cursor()
            
            # ๊ฑฐ๋ž˜ ํ…Œ์ด๋ธ”
            cursor.execute('''
                CREATE TABLE IF NOT EXISTS transactions (
                    tx_hash TEXT PRIMARY KEY,
                    block_number INTEGER,
                    timestamp DATETIME,
                    from_address TEXT,
                    to_address TEXT,
                    value REAL,
                    gas_price REAL,
                    gas_used INTEGER,
                    tx_fee REAL,
                    status INTEGER
                )
            ''')
            
            # ํ† ํฐ ์ „์†ก ํ…Œ์ด๋ธ”
            cursor.execute('''
                CREATE TABLE IF NOT EXISTS token_transfers (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    tx_hash TEXT,
                    block_number INTEGER,
                    token_address TEXT,
                    token_symbol TEXT,
                    from_address TEXT,
                    to_address TEXT,
                    value REAL,
                    timestamp DATETIME
                )
            ''')
            
            # ์ฃผ์†Œ ํ…Œ์ด๋ธ”
            cursor.execute('''
                CREATE TABLE IF NOT EXISTS addresses (
                    address TEXT PRIMARY KEY,
                    label TEXT,
                    first_seen DATETIME,
                    last_seen DATETIME,
                    tx_count INTEGER DEFAULT 0,
                    total_received REAL DEFAULT 0,
                    total_sent REAL DEFAULT 0
                )
            ''')
            
            # ์ธ๋ฑ์Šค ์ƒ์„ฑ
            cursor.execute('CREATE INDEX IF NOT EXISTS idx_block ON transactions(block_number)')
            cursor.execute('CREATE INDEX IF NOT EXISTS idx_from ON transactions(from_address)')
            cursor.execute('CREATE INDEX IF NOT EXISTS idx_to ON transactions(to_address)')
            cursor.execute('CREATE INDEX IF NOT EXISTS idx_timestamp ON transactions(timestamp)')
            
            conn.commit()
            print("โœ… ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ดˆ๊ธฐํ™” ์™„๋ฃŒ")
    
    def insert_transaction(self, tx_data):
        """
        ๊ฑฐ๋ž˜ ๋ฐ์ดํ„ฐ ์‚ฝ์ž…
        """
        with self.get_connection() as conn:
            cursor = conn.cursor()
            
            try:
                cursor.execute('''
                    INSERT OR REPLACE INTO transactions 
                    (tx_hash, block_number, timestamp, from_address, to_address, 
                     value, gas_price, gas_used, tx_fee, status)
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                ''', (
                    tx_data['tx_hash'],
                    tx_data['block_number'],
                    tx_data['timestamp'],
                    tx_data['from_address'],
                    tx_data['to_address'],
                    tx_data['value'],
                    tx_data.get('gas_price', 0),
                    tx_data.get('gas_used', 0),
                    tx_data.get('tx_fee', 0),
                    tx_data.get('status', 1)
                ))
                
                conn.commit()
                return True
            
            except Exception as e:
                print(f"โŒ ๋ฐ์ดํ„ฐ ์‚ฝ์ž… ์‹คํŒจ: {e}")
                return False
    
    def insert_transactions_bulk(self, transactions):
        """
        ๋Œ€๋Ÿ‰ ๊ฑฐ๋ž˜ ๋ฐ์ดํ„ฐ ์‚ฝ์ž…
        """
        with self.get_connection() as conn:
            cursor = conn.cursor()
            
            data = [
                (
                    tx['tx_hash'],
                    tx['block_number'],
                    tx['timestamp'],
                    tx['from_address'],
                    tx['to_address'],
                    tx['value'],
                    tx.get('gas_price', 0),
                    tx.get('gas_used', 0),
                    tx.get('tx_fee', 0),
                    tx.get('status', 1)
                )
                for tx in transactions
            ]
            
            try:
                cursor.executemany('''
                    INSERT OR REPLACE INTO transactions 
                    (tx_hash, block_number, timestamp, from_address, to_address, 
                     value, gas_price, gas_used, tx_fee, status)
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                ''', data)
                
                conn.commit()
                print(f"โœ… {len(data)}๊ฑด์˜ ๊ฑฐ๋ž˜ ์ €์žฅ ์™„๋ฃŒ")
                return True
            
            except Exception as e:
                print(f"โŒ ๋Œ€๋Ÿ‰ ์‚ฝ์ž… ์‹คํŒจ: {e}")
                return False
    
    def get_transactions_by_address(self, address, limit=100):
        """
        ํŠน์ • ์ฃผ์†Œ์˜ ๊ฑฐ๋ž˜ ์กฐํšŒ
        """
        with self.get_connection() as conn:
            query = '''
                SELECT * FROM transactions 
                WHERE from_address = ? OR to_address = ?
                ORDER BY timestamp DESC
                LIMIT ?
            '''
            
            df = pd.read_sql_query(query, conn, params=(address, address, limit))
            return df
    
    def get_transactions_by_date(self, start_date, end_date):
        """
        ๋‚ ์งœ ๋ฒ”์œ„๋กœ ๊ฑฐ๋ž˜ ์กฐํšŒ
        """
        with self.get_connection() as conn:
            query = '''
                SELECT * FROM transactions 
                WHERE timestamp BETWEEN ? AND ?
                ORDER BY timestamp DESC
            '''
            
            df = pd.read_sql_query(query, conn, params=(start_date, end_date))
            return df
    
    def get_statistics(self):
        """
        ์ „์ฒด ํ†ต๊ณ„ ์กฐํšŒ
        """
        with self.get_connection() as conn:
            cursor = conn.cursor()
            
            # ์ด ๊ฑฐ๋ž˜ ์ˆ˜
            cursor.execute('SELECT COUNT(*) FROM transactions')
            total_txs = cursor.fetchone()[0]
            
            # ์ด ๊ฑฐ๋ž˜๋Ÿ‰
            cursor.execute('SELECT SUM(value) FROM transactions')
            total_volume = cursor.fetchone()[0] or 0
            
            # ๊ณ ์œ  ์ฃผ์†Œ ์ˆ˜
            cursor.execute('''
                SELECT COUNT(DISTINCT address) FROM (
                    SELECT from_address as address FROM transactions
                    UNION
                    SELECT to_address as address FROM transactions
                )
            ''')
            unique_addresses = cursor.fetchone()[0]
            
            # ๋ธ”๋ก ๋ฒ”์œ„
            cursor.execute('SELECT MIN(block_number), MAX(block_number) FROM transactions')
            block_range = cursor.fetchone()
            
            stats = {
                'total_transactions': total_txs,
                'total_volume': total_volume,
                'unique_addresses': unique_addresses,
                'block_range': block_range
            }
            
            return stats
    
    def export_to_csv(self, output_file, query=None):
        """
        ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋‚ด์šฉ์„ CSV๋กœ ๋‚ด๋ณด๋‚ด๊ธฐ
        """
        with self.get_connection() as conn:
            if query is None:
                query = 'SELECT * FROM transactions'
            
            df = pd.read_sql_query(query, conn)
            df.to_csv(output_file, index=False)
            print(f"๐Ÿ“Š CSV ํŒŒ์ผ ์ƒ์„ฑ: {output_file} ({len(df)}๊ฑด)")

# ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‚ฌ์šฉ ์˜ˆ์‹œ
db = BlockchainDB('my_blockchain.db')

# DataFrame์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ
if 'df_transactions' in locals():
    transactions_list = df_transactions.to_dict('records')
    db.insert_transactions_bulk(transactions_list)

# ํ†ต๊ณ„ ์กฐํšŒ
stats = db.get_statistics()
print(f"\n๐Ÿ“Š ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ†ต๊ณ„")
print(f"์ด ๊ฑฐ๋ž˜ ์ˆ˜: {stats['total_transactions']:,}")
print(f"์ด ๊ฑฐ๋ž˜๋Ÿ‰: {stats['total_volume']:.4f} ETH")
print(f"๊ณ ์œ  ์ฃผ์†Œ ์ˆ˜: {stats['unique_addresses']:,}")
print(f"๋ธ”๋ก ๋ฒ”์œ„: {stats['block_range'][0]} ~ {stats['block_range'][1]}")
๐Ÿ’ก ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค vs CSV

CSV ์žฅ์ : ๊ฐ„๋‹จํ•˜๊ณ  ์—‘์…€์—์„œ ๋ฐ”๋กœ ์—ด์–ด๋ณผ ์ˆ˜ ์žˆ์–ด
DB ์žฅ์ : ๋น ๋ฅธ ๊ฒ€์ƒ‰, ๋ณต์žกํ•œ ์ฟผ๋ฆฌ, ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์— ์œ ๋ฆฌ

์†Œ๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ๋Š” CSV, ๋Œ€๊ทœ๋ชจ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์ถ”์ฒœํ•ด! ๐ŸŽฏ

๐ŸŽจ ์‹ค์ „ ํ”„๋กœ์ ํŠธ: ๊ฑฐ๋ž˜์†Œ ์ž…์ถœ๊ธˆ ๋ชจ๋‹ˆํ„ฐ๋ง

์ด์ œ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์ข…ํ•ฉํ•ด์„œ ์‹ค์ „ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž! ๊ฑฐ๋ž˜์†Œ ์ง€๊ฐ‘์˜ ์ž…์ถœ๊ธˆ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๋Š” ์‹œ์Šคํ…œ์ด์•ผ. ์ด๊ฑด ์‹ค์ œ๋กœ ๋งŽ์ด ํ™œ์šฉ๋˜๋Š” ๊ธฐ๋Šฅ์ด๊ฑฐ๋“ ! ๐Ÿ’ผ

๐Ÿ Python ์ฝ”๋“œ - ๊ฑฐ๋ž˜์†Œ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ
class ExchangeMonitor:
    def __init__(self, w3_instance, db_instance):
        self.w3 = w3_instance
        self.db = db_instance
        self.exchange_addresses = {}
        self.alert_threshold = 100  # ETH
        
    def add_exchange(self, name, address):
        """
        ๋ชจ๋‹ˆํ„ฐ๋งํ•  ๊ฑฐ๋ž˜์†Œ ์ถ”๊ฐ€
        """
        checksum_address = self.w3.to_checksum_address(address)
        self.exchange_addresses[name] = checksum_address
        print(f"โœ… ๊ฑฐ๋ž˜์†Œ ์ถ”๊ฐ€: {name} ({checksum_address})")
    
    def check_balance(self, exchange_name):
        """
        ๊ฑฐ๋ž˜์†Œ ์ž”์•ก ํ™•์ธ
        """
        if exchange_name not in self.exchange_addresses:
            print(f"โŒ ๋“ฑ๋ก๋˜์ง€ ์•Š์€ ๊ฑฐ๋ž˜์†Œ: {exchange_name}")
            return None
        
        address = self.exchange_addresses[exchange_name]
        balance_wei = self.w3.eth.get_balance(address)
        balance_eth = float(self.w3.from_wei(balance_wei, 'ether'))
        
        return balance_eth
    
    def analyze_flow(self, exchange_name, hours=24):
        """
        ํŠน์ • ๊ธฐ๊ฐ„ ๋™์•ˆ์˜ ์ž…์ถœ๊ธˆ ํ๋ฆ„ ๋ถ„์„
        """
        if exchange_name not in self.exchange_addresses:
            return None
        
        address = self.exchange_addresses[exchange_name]
        
        # ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๊ฑฐ๋ž˜ ์กฐํšŒ
        from datetime import datetime, timedelta
        end_time = datetime.now()
        start_time = end_time - timedelta(hours=hours)
        
        df = self.db.get_transactions_by_date(
            start_time.strftime('%Y-%m-%d %H:%M:%S'),
            end_time.strftime('%Y-%m-%d %H:%M:%S')
        )
        
        # ํ•ด๋‹น ๊ฑฐ๋ž˜์†Œ ๊ด€๋ จ ๊ฑฐ๋ž˜๋งŒ ํ•„ํ„ฐ๋ง
        exchange_txs = df[
            (df['from_address'] == address) | 
            (df['to_address'] == address)
        ]
        
        if len(exchange_txs) == 0:
            print(f"์ตœ๊ทผ {hours}์‹œ๊ฐ„ ๋™์•ˆ ๊ฑฐ๋ž˜ ์—†์Œ")
            return None
        
        # ์ž…์ถœ๊ธˆ ๊ตฌ๋ถ„
        deposits = exchange_txs[exchange_txs['to_address'] == address]
        withdrawals = exchange_txs[exchange_txs['from_address'] == address]
        
        analysis = {
            'exchange': exchange_name,
            'period_hours': hours,
            'total_transactions': len(exchange_txs),
            'deposits': {
                'count': len(deposits),
                'total': deposits['value'].sum(),
                'average': deposits['value'].mean() if len(deposits) > 0 else 0
            },
            'withdrawals': {
                'count': len(withdrawals),
                'total': withdrawals['value'].sum(),
                'average': withdrawals['value'].mean() if len(withdrawals) > 0 else 0
            },
            'net_flow': deposits['value'].sum() - withdrawals['value'].sum()
        }
        
        return analysis
    
    def print_flow_report(self, analysis):
        """
        ์ž…์ถœ๊ธˆ ๋ถ„์„ ๋ฆฌํฌํŠธ ์ถœ๋ ฅ
        """
        if not analysis:
            return
        
        print(f"\n{'='*60}")
        print(f"๐Ÿฆ {analysis['exchange']} ์ž…์ถœ๊ธˆ ๋ถ„์„ ๋ฆฌํฌํŠธ")
        print(f"๐Ÿ“… ๋ถ„์„ ๊ธฐ๊ฐ„: ์ตœ๊ทผ {analysis['period_hours']}์‹œ๊ฐ„")
        print(f"{'='*60}")
        
        print(f"\n๐Ÿ“ฅ ์ž…๊ธˆ (Deposits)")
        print(f"   ๊ฑฐ๋ž˜ ์ˆ˜: {analysis['deposits']['count']}๊ฑด")
        print(f"   ์ด ์ž…๊ธˆ์•ก: {analysis['deposits']['total']:.4f} ETH")
        print(f"   ํ‰๊ท  ์ž…๊ธˆ์•ก: {analysis['deposits']['average']:.4f} ETH")
        
        print(f"\n๐Ÿ“ค ์ถœ๊ธˆ (Withdrawals)")
        print(f"   ๊ฑฐ๋ž˜ ์ˆ˜: {analysis['withdrawals']['count']}๊ฑด")
        print(f"   ์ด ์ถœ๊ธˆ์•ก: {analysis['withdrawals']['total']:.4f} ETH")
        print(f"   ํ‰๊ท  ์ถœ๊ธˆ์•ก: {analysis['withdrawals']['average']:.4f} ETH")
        
        net_flow = analysis['net_flow']
        flow_direction = "์œ ์ž…" if net_flow > 0 else "์œ ์ถœ"
        print(f"\n๐Ÿ’ฐ ์ˆœ ํ๋ฆ„: {abs(net_flow):.4f} ETH ({flow_direction})")
        
        # ํ˜„์žฌ ์ž”์•ก
        current_balance = self.check_balance(analysis['exchange'])
        print(f"๐Ÿ’ผ ํ˜„์žฌ ์ž”์•ก: {current_balance:.4f} ETH")
        
        print(f"{'='*60}\n")
    
    def monitor_large_transactions(self, block_number):
        """
        ๋Œ€๋Ÿ‰ ๊ฑฐ๋ž˜ ๋ชจ๋‹ˆํ„ฐ๋ง
        """
        try:
            block = self.w3.eth.get_block(block_number, full_transactions=True)
            alerts = []
            
            for tx in block['transactions']:
                value_eth = float(self.w3.from_wei(tx['value'], 'ether'))
                
                # ๊ฑฐ๋ž˜์†Œ ๊ด€๋ จ ๋Œ€๋Ÿ‰ ๊ฑฐ๋ž˜ ๊ฐ์ง€
                for exchange_name, exchange_address in self.exchange_addresses.items():
                    if value_eth >= self.alert_threshold:
                        if tx['from'] == exchange_address:
                            alert = {
                                'type': 'WITHDRAWAL',
                                'exchange': exchange_name,
                                'amount': value_eth,
                                'to': tx['to'],
                                'tx_hash': tx['hash'].hex()
                            }
                            alerts.append(alert)
                            self.send_alert(alert)
                        
                        elif tx['to'] == exchange_address:
                            alert = {
                                'type': 'DEPOSIT',
                                'exchange': exchange_name,
                                'amount': value_eth,
                                'from': tx['from'],
                                'tx_hash': tx['hash'].hex()
                            }
                            alerts.append(alert)
                            self.send_alert(alert)
            
            return alerts
        
        except Exception as e:
            print(f"โŒ ๋ธ”๋ก ๋ชจ๋‹ˆํ„ฐ๋ง ์‹คํŒจ: {e}")
            return []
    
    def send_alert(self, alert):
        """
        ์•Œ๋ฆผ ์ „์†ก
        """
        emoji = "๐Ÿ“ค" if alert['type'] == 'WITHDRAWAL' else "๐Ÿ“ฅ"
        print(f"\n๐Ÿšจ {emoji} ๋Œ€๋Ÿ‰ ๊ฑฐ๋ž˜ ๊ฐ์ง€!")
        print(f"   ๊ฑฐ๋ž˜์†Œ: {alert['exchange']}")
        print(f"   ์œ ํ˜•: {alert['type']}")
        print(f"   ๊ธˆ์•ก: {alert['amount']:.2f} ETH")
        
        if alert['type'] == 'WITHDRAWAL':
            print(f"   ์ˆ˜์‹ ์ž: {alert['to']}")
        else:
            print(f"   ์†ก์‹ ์ž: {alert['from']}")
        
        print(f"   TX: {alert['tx_hash']}")
    
    def run_monitoring(self, interval=15):
        """
        ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹คํ–‰
        """
        print("๐Ÿ” ๊ฑฐ๋ž˜์†Œ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์ž‘...")
        print(f"๋ชจ๋‹ˆํ„ฐ๋ง ๋Œ€์ƒ: {', '.join(self.exchange_addresses.keys())}")
        print(f"์•Œ๋ฆผ ์ž„๊ณ„๊ฐ’: {self.alert_threshold} ETH\n")
        
        last_block = self.w3.eth.block_number
        
        try:
            while True:
                current_block = self.w3.eth.block_number
                
                if current_block > last_block:
                    for block_num in range(last_block + 1, current_block + 1):
                        print(f"๐Ÿ“ฆ ๋ธ”๋ก #{block_num} ๋ถ„์„ ์ค‘...")
                        alerts = self.monitor_large_transactions(block_num)
                        
                        if alerts:
                            print(f"โœ… {len(alerts)}๊ฑด์˜ ์•Œ๋ฆผ ๋ฐœ์ƒ")
                    
                    last_block = current_block
                
                time.sleep(interval)
        
        except KeyboardInterrupt:
            print("\nโน๏ธ ๋ชจ๋‹ˆํ„ฐ๋ง ์ข…๋ฃŒ")

# ๊ฑฐ๋ž˜์†Œ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ ์„ค์ •
monitor = ExchangeMonitor(w3, db)

# ์ฃผ์š” ๊ฑฐ๋ž˜์†Œ ์ฃผ์†Œ ์ถ”๊ฐ€ (์˜ˆ์‹œ)
monitor.add_exchange("Binance", "0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE")
monitor.add_exchange("Coinbase", "0x71660c4005BA85c37ccec55d0C4493E66Fe775d3")
monitor.add_exchange("Kraken", "0x2910543Af39abA0Cd09dBb2D50200b3E800A63D2")

# ์ž…์ถœ๊ธˆ ํ๋ฆ„ ๋ถ„์„
analysis = monitor.analyze_flow("Binance", hours=24)
monitor.print_flow_report(analysis)

# ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์ž‘ (์ฃผ์„ ํ•ด์ œํ•˜์—ฌ ์‹คํ–‰)
# monitor.run_monitoring(interval=15)
๐ŸŽฏ ์‹ค์ „ ํ™œ์šฉ ํŒ

โ€ข ๊ฑฐ๋ž˜์†Œ ์ฃผ์†Œ ํ™•์ธ: Etherscan์—์„œ "Top Accounts" ๋ฉ”๋‰ด๋กœ ์ฃผ์š” ๊ฑฐ๋ž˜์†Œ ์ฃผ์†Œ ํ™•์ธ
โ€ข ์•Œ๋ฆผ ์—ฐ๋™: ํ…”๋ ˆ๊ทธ๋žจ ๋ด‡์ด๋‚˜ ์ด๋ฉ”์ผ๋กœ ์‹ค์‹œ๊ฐ„ ์•Œ๋ฆผ ์ „์†ก
โ€ข ํŒจํ„ด ๋ถ„์„: ๋Œ€๋Ÿ‰ ์ถœ๊ธˆ ํ›„ ๊ฐ€๊ฒฉ ๋ณ€๋™ ํŒจํ„ด ํ•™์Šต
โ€ข ๋‹ค์ค‘ ์ฒด์ธ: BSC, Polygon ๋“ฑ ๋‹ค๋ฅธ ์ฒด์ธ๋„ ๋™์‹œ ๋ชจ๋‹ˆํ„ฐ๋ง
โ€ข ์ž๋™ ๊ฑฐ๋ž˜: ํŠน์ • ํŒจํ„ด ๊ฐ์ง€ ์‹œ ์ž๋™ ๋งค๋งค ์ „๋žต ์‹คํ–‰

๐Ÿ”ง ์„ฑ๋Šฅ ์ตœ์ ํ™” ๋ฐ ํŒ

์‹ค์ œ ์šด์˜ ํ™˜๊ฒฝ์—์„œ๋Š” ์„ฑ๋Šฅ๊ณผ ์•ˆ์ •์„ฑ์ด ์ •๋ง ์ค‘์š”ํ•ด! ๋ช‡ ๊ฐ€์ง€ ์ตœ์ ํ™” ๊ธฐ๋ฒ•์„ ์•Œ์•„๋ณด์ž. โšก

๐Ÿš€ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์œผ๋กœ ์†๋„ ํ–ฅ์ƒ

๐Ÿ Python ์ฝ”๋“œ - ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ
from concurrent.futures import ThreadPoolExecutor, as_completed
import threading

class ParallelBlockAnalyzer:
    def __init__(self, w3_instance, max_workers=5):
        self.w3 = w3_instance
        self.max_workers = max_workers
        self.lock = threading.Lock()
        self.results = []
    
    def analyze_single_block(self, block_number):
        """
        ๋‹จ์ผ ๋ธ”๋ก ๋ถ„์„
        """
        try:
            block = self.w3.eth.get_block(block_number, full_transactions=True)
            
            block_data = {
                'block_number': block_number,
                'timestamp': block['timestamp'],
                'tx_count': len(block['transactions']),
                'total_value': sum(
                    float(self.w3.from_wei(tx['value'], 'ether')) 
                    for tx in block['transactions']
                )
            }
            
            return block_data
        
        except Exception as e:
            print(f"โŒ ๋ธ”๋ก {block_number} ๋ถ„์„ ์‹คํŒจ: {e}")
            return None
    
    def analyze_blocks_parallel(self, start_block, end_block):
        """
        ์—ฌ๋Ÿฌ ๋ธ”๋ก์„ ๋ณ‘๋ ฌ๋กœ ๋ถ„์„
        """
        block_numbers = range(start_block, end_block + 1)
        results = []
        
        print(f"๐Ÿš€ {len(block_numbers)}๊ฐœ ๋ธ”๋ก ๋ณ‘๋ ฌ ๋ถ„์„ ์‹œ์ž‘...")
        print(f"์›Œ์ปค ์ˆ˜: {self.max_workers}")
        
        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            # ๋ชจ๋“  ๋ธ”๋ก ๋ถ„์„ ์ž‘์—… ์ œ์ถœ
            future_to_block = {
                executor.submit(self.analyze_single_block, block_num): block_num 
                for block_num in block_numbers
            }
            
            # ์™„๋ฃŒ๋œ ์ž‘์—… ์ฒ˜๋ฆฌ
            completed = 0
            for future in as_completed(future_to_block):
                block_num = future_to_block[future]
                try:
                    result = future.result()
                    if result:
                        results.append(result)
                    
                    completed += 1
                    if completed % 10 == 0:
                        progress = (completed / len(block_numbers)) * 100
                        print(f"์ง„ํ–‰๋ฅ : {progress:.1f}% ({completed}/{len(block_numbers)})")
                
                except Exception as e:
                    print(f"โŒ ๋ธ”๋ก {block_num} ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜: {e}")
        
        print(f"โœ… ๋ถ„์„ ์™„๋ฃŒ: {len(results)}๊ฐœ ๋ธ”๋ก")
        return results

# ๋ณ‘๋ ฌ ๋ถ„์„ ์‹คํ–‰
analyzer = ParallelBlockAnalyzer(w3, max_workers=10)
latest = w3.eth.block_number

# ์ตœ๊ทผ 100๊ฐœ ๋ธ”๋ก์„ ๋ณ‘๋ ฌ๋กœ ๋ถ„์„
results = analyzer.analyze_blocks_parallel(latest - 100, latest)

# ๊ฒฐ๊ณผ๋ฅผ DataFrame์œผ๋กœ ๋ณ€ํ™˜
df_results = pd.DataFrame(results)
print(f"\n๐Ÿ“Š ๋ถ„์„ ๊ฒฐ๊ณผ ์š”์•ฝ")
print(f"ํ‰๊ท  ๊ฑฐ๋ž˜ ์ˆ˜: {df_results['tx_count'].mean():.2f}")
print(f"ํ‰๊ท  ๊ฑฐ๋ž˜๋Ÿ‰: {df_results['total_value'].mean():.4f} ETH")
๐Ÿ’ก ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ์ฃผ์˜์‚ฌํ•ญ

์›Œ์ปค ์ˆ˜๋ฅผ ๋„ˆ๋ฌด ๋งŽ์ด ์„ค์ •ํ•˜๋ฉด API ์ œํ•œ์— ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์–ด! Infura ๋ฌด๋ฃŒ ํ”Œ๋žœ์€ ์ดˆ๋‹น 10 ์š”์ฒญ๊นŒ์ง€ ๊ฐ€๋Šฅํ•˜๋‹ˆ๊นŒ, max_workers๋Š” 5~10 ์ •๋„๊ฐ€ ์ ๋‹นํ•ด. ์œ ๋ฃŒ ํ”Œ๋žœ์ด๋ฉด ๋” ๋Š˜๋ ค๋„ ๋ผ! ๐ŸŽฏ

๐Ÿ’พ ์บ์‹ฑ์œผ๋กœ ์ค‘๋ณต ์š”์ฒญ ๋ฐฉ์ง€

๐Ÿ Python ์ฝ”๋“œ - ์บ์‹ฑ ์‹œ์Šคํ…œ
from functools import lru_cache
import pickle
import hashlib

class CachedWeb3:
    def __init__(self, w3_instance, cache_dir='cache'):
        self.w3 = w3_instance
        self.cache_dir = cache_dir
        
        if not os.path.exists(cache_dir):
            os.makedirs(cache_dir)
    
    def _get_cache_key(self, method, *args):
        """
        ์บ์‹œ ํ‚ค ์ƒ์„ฑ
        """
        key_string = f"{method}_{args}"
        return hashlib.md5(key_string.encode()).hexdigest()
    
    def _get_cache_path(self, cache_key):
        """
        ์บ์‹œ ํŒŒ์ผ ๊ฒฝ๋กœ
        """
        return os.path.join(self.cache_dir, f"{cache_key}.pkl")
    
    def _load_from_cache(self, cache_key):
        """
        ์บ์‹œ์—์„œ ๋ฐ์ดํ„ฐ ๋กœ๋“œ
        """
        cache_path = self._get_cache_path(cache_key)
        
        if os.path.exists(cache_path):
            try:
                with open(cache_path, 'rb') as f:
                    return pickle.load(f)
            except:
                return None
        
        return None
    
    def _save_to_cache(self, cache_key, data):
        """
        ๋ฐ์ดํ„ฐ๋ฅผ ์บ์‹œ์— ์ €์žฅ
        """
        cache_path = self._get_cache_path(cache_key)
        
        try:
            with open(cache_path, 'wb') as f:
                pickle.dump(data, f)
        except Exception as e:
            print(f"โš ๏ธ ์บ์‹œ ์ €์žฅ ์‹คํŒจ: {e}")
    
    def get_block(self, block_number, full_transactions=False):
        """
        ๋ธ”๋ก ์ •๋ณด ์กฐํšŒ (์บ์‹ฑ)
        """
        cache_key = self._get_cache_key('block', block_number, full_transactions)
        
        # ์บ์‹œ ํ™•์ธ
        cached_data = self._load_from_cache(cache_key)
        if cached_data:
            print(f"๐Ÿ’พ ์บ์‹œ์—์„œ ๋ธ”๋ก #{block_number} ๋กœ๋“œ")
            return cached_data
        
        # ๋ธ”๋ก์ฒด์ธ์—์„œ ์กฐํšŒ
        print(f"๐Ÿ” ๋ธ”๋ก์ฒด์ธ์—์„œ ๋ธ”๋ก #{block_number} ์กฐํšŒ")
        block = self.w3.eth.get_block(block_number, full_transactions=full_transactions)
        
        # ์บ์‹œ์— ์ €์žฅ
        self._save_to_cache(cache_key, block)
        
        return block
    
    def clear_cache(self):
        """
        ์บ์‹œ ์ „์ฒด ์‚ญ์ œ
        """
        import shutil
        if os.path.exists(self.cache_dir):
            shutil.rmtree(self.cache_dir)
            os.makedirs(self.cache_dir)
            print("๐Ÿ—‘๏ธ ์บ์‹œ ์‚ญ์ œ ์™„๋ฃŒ")

# ์บ์‹ฑ ์ ์šฉ
cached_w3 = CachedWeb3(w3)

# ์ฒซ ๋ฒˆ์งธ ํ˜ธ์ถœ - ๋ธ”๋ก์ฒด์ธ์—์„œ ์กฐํšŒ
block1 = cached_w3.get_block(19000000)

# ๋‘ ๋ฒˆ์งธ ํ˜ธ์ถœ - ์บ์‹œ์—์„œ ๋กœ๋“œ (ํ›จ์”ฌ ๋น ๋ฆ„!)
block2 = cached_w3.get_block(19000000)

โš ๏ธ ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๋ฐ ์žฌ์‹œ๋„ ๋กœ์ง

๐Ÿ Python ์ฝ”๋“œ - ์•ˆ์ •์ ์ธ ์—๋Ÿฌ ์ฒ˜๋ฆฌ
import time
from functools import wraps

def retry_on_failure(max_retries=3, delay=1, backoff=2):
    """
    ์‹คํŒจ ์‹œ ์žฌ์‹œ๋„ํ•˜๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ
    """
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            retries = 0
            current_delay = delay
            
            while retries < max_retries:
                try:
                    return func(*args, **kwargs)
                
                except Exception as e:
                    retries += 1
                    if retries >= max_retries:
                        print(f"โŒ {func.__name__} ์ตœ์ข… ์‹คํŒจ: {e}")
                        raise
                    
                    print(f"โš ๏ธ {func.__name__} ์‹คํŒจ (์‹œ๋„ {retries}/{max_retries}): {e}")
                    print(f"   {current_delay}์ดˆ ํ›„ ์žฌ์‹œ๋„...")
                    time.sleep(current_delay)
                    current_delay *= backoff
            
            return None
        
        return wrapper
    return decorator

class RobustBlockchainClient:
    def __init__(self, w3_instance):
        self.w3 = w3_instance
    
    @retry_on_failure(max_retries=3, delay=1, backoff=2)
    def get_block_safe(self, block_number):
        """
        ์•ˆ์ „ํ•œ ๋ธ”๋ก ์กฐํšŒ
        """
        return self.w3.eth.get_block(block_number, full_transactions=True)
    
    @retry_on_failure(max_retries=5, delay=2, backoff=1.5)
    def get_transaction_receipt(self, tx_hash):
        """
        ์•ˆ์ „ํ•œ ํŠธ๋žœ์žญ์…˜ ์˜์ˆ˜์ฆ ์กฐํšŒ
        """
        return self.w3.eth.get_transaction_receipt(tx_hash)
    
    def batch_get_blocks(self, block_numbers):
        """
        ์—ฌ๋Ÿฌ ๋ธ”๋ก์„ ์•ˆ์ „ํ•˜๊ฒŒ ์กฐํšŒ
        """
        results = []
        failed = []
        
        for block_num in block_numbers:
            try:
                block = self.get_block_safe(block_num)
                results.append(block)
            except Exception as e:
                print(f"โŒ ๋ธ”๋ก {block_num} ์กฐํšŒ ์‹คํŒจ")
                failed.append(block_num)
        
        print(f"\nโœ… ์„ฑ๊ณต: {len(results)}๊ฐœ")
        print(f"โŒ ์‹คํŒจ: {len(failed)}๊ฐœ")
        
        if failed:
            print(f"์‹คํŒจํ•œ ๋ธ”๋ก: {failed}")
        
        return results, failed

# ์•ˆ์ •์ ์ธ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์šฉ
robust_client = RobustBlockchainClient(w3)

# ๋ธ”๋ก ์กฐํšŒ (์ž๋™ ์žฌ์‹œ๋„)
try:
    block = robust_client.get_block_safe(19000000)
    print("โœ… ๋ธ”๋ก ์กฐํšŒ ์„ฑ๊ณต")
except Exception as e:
    print(f"โŒ ๋ชจ๋“  ์žฌ์‹œ๋„ ์‹คํŒจ: {e}")
โš ๏ธ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ ์ฒดํฌ๋ฆฌ์ŠคํŠธ
  • โœ… API ํ‚ค ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ๊ด€๋ฆฌ
  • โœ… ์—๋Ÿฌ ๋กœ๊น… ์‹œ์Šคํ…œ ๊ตฌ์ถ•
  • โœ… ์žฌ์‹œ๋„ ๋กœ์ง ๊ตฌํ˜„
  • โœ… ์บ์‹ฑ์œผ๋กœ ์ค‘๋ณต ์š”์ฒญ ๋ฐฉ์ง€
  • โœ… ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐฑ์—… ์ž๋™ํ™”
  • โœ… ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ์•Œ๋ฆผ ์‹œ์Šคํ…œ
  • โœ… API ์š”์ฒญ ์ œํ•œ ์ค€์ˆ˜
  • โœ… ๋ณด์•ˆ ์ทจ์•ฝ์  ์ ๊ฒ€

๐ŸŒŸ ์žฌ๋Šฅ๋„ท์—์„œ ๋ธ”๋ก์ฒด์ธ ๊ฐœ๋ฐœ ๋ฐฐ์šฐ๊ธฐ

์—ฌ๊ธฐ๊นŒ์ง€ ๋”ฐ๋ผ์™”๋‹ค๋ฉด ์ •๋ง ๋Œ€๋‹จํ•ด! ๐ŸŽ‰ ํ•˜์ง€๋งŒ ํ˜ผ์ž ๊ณต๋ถ€ํ•˜๋‹ค ๋ง‰ํžˆ๋Š” ๋ถ€๋ถ„์ด ์žˆ์„ ์ˆ˜ ์žˆ์ž–์•„? ๊ทธ๋Ÿด ๋•Œ๋Š” ์žฌ๋Šฅ๋„ท์„ ํ™œ์šฉํ•ด๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ!

์žฌ๋Šฅ๋„ท(https://www.jaenung.net)์—๋Š” ๋ธ”๋ก์ฒด์ธ ๊ฐœ๋ฐœ, ํŒŒ์ด์ฌ ํ”„๋กœ๊ทธ๋ž˜๋ฐ, ๋ฐ์ดํ„ฐ ๋ถ„์„ ๋“ฑ ๋‹ค์–‘ํ•œ ๋ถ„์•ผ์˜ ์ „๋ฌธ๊ฐ€๋“ค์ด ์žˆ์–ด. 1:1 ๋ฉ˜ํ† ๋ง์„ ๋ฐ›๊ฑฐ๋‚˜, ํ”„๋กœ์ ํŠธ๋ฅผ ์˜๋ขฐํ•˜๊ฑฐ๋‚˜, ํ•จ๊ป˜ ๊ณต๋ถ€ํ•  ์Šคํ„ฐ๋”” ๊ทธ๋ฃน์„ ์ฐพ์„ ์ˆ˜๋„ ์žˆ์ง€! ๐Ÿ˜Š

ํŠนํžˆ ๋ธ”๋ก์ฒด์ธ ๋ถ„์•ผ๋Š” ๋น ๋ฅด๊ฒŒ ๋ณ€ํ™”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์‹ค๋ฌด ๊ฒฝํ—˜์ด ์žˆ๋Š” ์ „๋ฌธ๊ฐ€์˜ ์กฐ์–ธ์ด ์ •๋ง ์ค‘์š”ํ•ด. ์žฌ๋Šฅ๋„ท์—์„œ ์ž์‹ ์—๊ฒŒ ๋งž๋Š” ๋ฉ˜ํ† ๋ฅผ ์ฐพ์•„๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ์„ ํƒ์ด ๋  ๊ฑฐ์•ผ! ๐Ÿ’ช

๐ŸŽ“ ๋” ๋‚˜์•„๊ฐ€๊ธฐ

๊ธฐ๋ณธ์ ์ธ ๋ธ”๋ก์ฒด์ธ ๋ถ„์„ ํ”„๋กœ๊ทธ๋žจ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ฒŒ ๋๋‹ค๋ฉด, ์ด์ œ ๋” ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์— ๋„์ „ํ•ด๋ณผ ์ฐจ๋ก€์•ผ! ๐Ÿš€

๐Ÿ“š ์ถ”๊ฐ€ ํ•™์Šต ์ฃผ์ œ

๐Ÿค– ๋จธ์‹ ๋Ÿฌ๋‹ ์ ์šฉ

๊ฑฐ๋ž˜ ํŒจํ„ด์„ ํ•™์Šตํ•ด์„œ ์ด์ƒ๊ฑฐ๋ž˜๋ฅผ ์ž๋™์œผ๋กœ ๊ฐ์ง€ํ•˜๊ฑฐ๋‚˜, ๊ฐ€๊ฒฉ ์˜ˆ์ธก ๋ชจ๋ธ์„ ๋งŒ๋“ค์–ด๋ณผ ์ˆ˜ ์žˆ์–ด. scikit-learn์ด๋‚˜ TensorFlow๋ฅผ ํ™œ์šฉํ•ด๋ณด์ž!
๐Ÿ“Š ๋Œ€์‹œ๋ณด๋“œ ๊ตฌ์ถ•

Streamlit์ด๋‚˜ Dash๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ๋งŒ๋“ค์–ด๋ด. ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ฐ”๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์„œ ํŽธ๋ฆฌํ•ด!
๐Ÿ”— ๋ฉ€ํ‹ฐ์ฒด์ธ ์ง€์›

์ด๋”๋ฆฌ์›€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ BSC, Polygon, Avalanche ๋“ฑ ๋‹ค์–‘ํ•œ ์ฒด์ธ์„ ๋™์‹œ์— ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๋Š” ์‹œ์Šคํ…œ์œผ๋กœ ํ™•์žฅํ•ด๋ณด์ž!
๐Ÿ’ฌ ์•Œ๋ฆผ ์‹œ์Šคํ…œ

ํ…”๋ ˆ๊ทธ๋žจ ๋ด‡, ๋””์Šค์ฝ”๋“œ ์›นํ›…, ์ด๋ฉ”์ผ ๋“ฑ ๋‹ค์–‘ํ•œ ์•Œ๋ฆผ ์ฑ„๋„์„ ์—ฐ๋™ํ•ด์„œ ์ค‘์š”ํ•œ ์ด๋ฒคํŠธ๋ฅผ ๋†“์น˜์ง€ ์•Š๋„๋ก ํ•˜์ž!

๐Ÿ› ๏ธ ์œ ์šฉํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฐ ๋„๊ตฌ

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ/๋„๊ตฌ ์šฉ๋„ ํŠน์ง•
Web3.py ์ด๋”๋ฆฌ์›€ ์ƒํ˜ธ์ž‘์šฉ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ด๊ณ  ํ•„์ˆ˜์ ์ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
Brownie ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ ๊ฐœ๋ฐœ ํ…Œ์ŠคํŠธ์™€ ๋ฐฐํฌ๊ฐ€ ์‰ฌ์šด ํ”„๋ ˆ์ž„์›Œํฌ
The Graph ๋ธ”๋ก์ฒด์ธ ๋ฐ์ดํ„ฐ ์ฟผ๋ฆฌ GraphQL๋กœ ํšจ์œจ์ ์ธ ๋ฐ์ดํ„ฐ ์กฐํšŒ
Dune Analytics ๋ฐ์ดํ„ฐ ๋ถ„์„ ํ”Œ๋žซํผ SQL๋กœ ๋ธ”๋ก์ฒด์ธ ๋ฐ์ดํ„ฐ ๋ถ„์„
Alchemy ๋ธ”๋ก์ฒด์ธ API Infura์˜ ๊ฐ•๋ ฅํ•œ ๋Œ€์•ˆ
Moralis Web3 ๋ฐฑ์—”๋“œ NFT, DeFi ๋ฐ์ดํ„ฐ ์‰ฝ๊ฒŒ ์กฐํšŒ

๐Ÿ’ก ์‹ค์ „ ํŒ & ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค

๋งˆ์ง€๋ง‰์œผ๋กœ ์‹ค์ œ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•  ๋•Œ ๊ผญ ์•Œ์•„์•ผ ํ•  ํŒ๋“ค์„ ์ •๋ฆฌํ•ด๋ณผ๊ฒŒ! ๐ŸŽฏ

1๏ธโƒฃ ์ž‘๊ฒŒ ์‹œ์ž‘ํ•˜๊ณ  ์ ์ง„์ ์œผ๋กœ ํ™•์žฅ
์ฒ˜์Œ๋ถ€ํ„ฐ ์™„๋ฒฝํ•œ ์‹œ์Šคํ…œ์„ ๋งŒ๋“ค๋ ค๊ณ  ํ•˜์ง€ ๋งˆ! ๋จผ์ € ๋‹จ์ˆœํ•œ ๊ธฐ๋Šฅ๋ถ€ํ„ฐ ๊ตฌํ˜„ํ•˜๊ณ , ์ž˜ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•œ ํ›„์— ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•ด๋‚˜๊ฐ€๋Š” ๊ฒŒ ์ข‹์•„.
2๏ธโƒฃ ํ…Œ์ŠคํŠธ๋„ท์—์„œ ์ถฉ๋ถ„ํžˆ ํ…Œ์ŠคํŠธ
๋ฉ”์ธ๋„ท์—์„œ ๋ฐ”๋กœ ํ…Œ์ŠคํŠธํ•˜๋ฉด ์‹ค์ œ ๊ฐ€์Šค๋น„๊ฐ€ ๋‚˜๊ฐ€! Goerli, Sepolia ๊ฐ™์€ ํ…Œ์ŠคํŠธ๋„ท์—์„œ ์ถฉ๋ถ„ํžˆ ํ…Œ์ŠคํŠธํ•œ ํ›„์— ๋ฉ”์ธ๋„ท์œผ๋กœ ์˜ฎ๊ธฐ์ž.
3๏ธโƒฃ ์ฝ”๋“œ ๋ฌธ์„œํ™”๋Š” ํ•„์ˆ˜
๋‚˜์ค‘์— ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ๋ณผ ๋•Œ ๋ฌด์Šจ ์˜๋ฏธ์ธ์ง€ ๋ชจ๋ฅผ ์ˆ˜ ์žˆ์–ด. ์ฃผ์„๊ณผ docstring์„ ๊ผผ๊ผผํ•˜๊ฒŒ ์ž‘์„ฑํ•˜๋Š” ์Šต๊ด€์„ ๋“ค์ด์ž!
4๏ธโƒฃ ๋ณด์•ˆ์„ ์ตœ์šฐ์„ ์œผ๋กœ
API ํ‚ค, ํ”„๋ผ์ด๋น— ํ‚ค ๋“ฑ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋Š” ์ ˆ๋Œ€ ์ฝ”๋“œ์— ํ•˜๋“œ์ฝ”๋”ฉํ•˜์ง€ ๋ง๊ณ , ํ™˜๊ฒฝ๋ณ€์ˆ˜๋‚˜ ๋ณ„๋„์˜ ์„ค์ • ํŒŒ์ผ๋กœ ๊ด€๋ฆฌํ•ด์•ผ ํ•ด.
5๏ธโƒฃ ์ปค๋ฎค๋‹ˆํ‹ฐ ํ™œ์šฉ
Stack Overflow, Reddit, Discord ๋“ฑ์—์„œ ํ™œ๋ฐœํ•˜๊ฒŒ ์งˆ๋ฌธํ•˜๊ณ  ๋‹ต๋ณ€ํ•˜๋ฉด์„œ ๋ฐฐ์šฐ์ž. ํ˜ผ์ž ๊ณ ๋ฏผํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ํ›จ์”ฌ ๋น ๋ฅด๊ฒŒ ์„ฑ์žฅํ•  ์ˆ˜ ์žˆ์–ด!

๐ŸŽฌ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์™€! ์—ฌ๊ธฐ๊นŒ์ง€ ์ •๋ง ๊ธด ์—ฌ์ •์ด์—ˆ์–ด! ๐ŸŽ‰ ํŒŒ์ด์ฌ์œผ๋กœ ๋ธ”๋ก์ฒด์ธ ๊ฑฐ๋ž˜๋‚ด์—ญ์„ ๋ถ„์„ํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ์„ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋๊นŒ์ง€ ์‚ดํŽด๋ดค์–ด.

์šฐ๋ฆฌ๊ฐ€ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์ •๋ฆฌํ•ด๋ณด๋ฉด:

โœ… Web3.py๋กœ ๋ธ”๋ก์ฒด์ธ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘
โœ… Pandas๋กœ ๋ฐ์ดํ„ฐ ๋ถ„์„ ๋ฐ ์‹œ๊ฐํ™”
โœ… ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ ๊ตฌ์ถ•
โœ… ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ ์ƒํ˜ธ์ž‘์šฉ
โœ… ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ™œ์šฉ ๋ฐ ์ตœ์ ํ™”
โœ… ์‹ค์ „ ํ”„๋กœ์ ํŠธ ๊ตฌํ˜„

์ด์ œ ๋„ˆ๋งŒ์˜ ๋ธ”๋ก์ฒด์ธ ๋ถ„์„ ๋„๊ตฌ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ๊ธฐ์ดˆ๋Š” ๋‹ค ๊ฐ–์ถฐ์กŒ์–ด! ๋ฌผ๋ก  ์ฒ˜์Œ์—๋Š” ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์ง€๋งŒ, ํ•˜๋‚˜์”ฉ ์ฐจ๊ทผ์ฐจ๊ทผ ๋”ฐ๋ผํ•˜๋‹ค ๋ณด๋ฉด ์–ด๋А์ƒˆ ๋ฉ‹์ง„ ํ”„๋กœ๊ทธ๋žจ์„ ๋งŒ๋“ค๊ณ  ์žˆ๋Š” ์ž์‹ ์„ ๋ฐœ๊ฒฌํ•˜๊ฒŒ ๋  ๊ฑฐ์•ผ. ๐Ÿ’ช

๋ธ”๋ก์ฒด์ธ ๊ธฐ์ˆ ์€ ๊ณ„์† ๋ฐœ์ „ํ•˜๊ณ  ์žˆ๊ณ , ์ƒˆ๋กœ์šด ๊ธฐํšŒ๋“ค์ด ๋Š์ž„์—†์ด ์ƒ๊ฒจ๋‚˜๊ณ  ์žˆ์–ด. ์ด ๊ธ€์—์„œ ๋ฐฐ์šด ๋‚ด์šฉ์„ ๋ฐ”ํƒ•์œผ๋กœ ์ž์‹ ๋งŒ์˜ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•ด๋ณด๋Š” ๊ฑด ์–ด๋•Œ? ๐Ÿš€

ํ˜น์‹œ ๋ง‰ํžˆ๋Š” ๋ถ€๋ถ„์ด ์žˆ๊ฑฐ๋‚˜ ๋” ๊นŠ์ด ๋ฐฐ์šฐ๊ณ  ์‹ถ๋‹ค๋ฉด, ์žฌ๋Šฅ๋„ท์—์„œ ์ „๋ฌธ๊ฐ€์˜ ๋„์›€์„ ๋ฐ›๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ. ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๋Š” ๊ฐœ๋ฐœ์ž ์ปค๋ฎค๋‹ˆํ‹ฐ์—์„œ ๋งŽ์€ ๊ฒƒ์„ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ!

์ž, ์ด์ œ ์ง์ ‘ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์‹คํ—˜ํ•ด๋ณผ ์‹œ๊ฐ„์ด์•ผ! ํ–‰์šด์„ ๋นŒ์–ด! ๐Ÿ€โœจ

๐Ÿš€ Happy Coding!

๋ธ”๋ก์ฒด์ธ์˜ ์„ธ๊ณ„๋Š” ๋ฌด๊ถ๋ฌด์ง„ํ•ด!
๊ณ„์† ํƒํ—˜ํ•˜๊ณ  ๋ฐฐ์šฐ๋ฉด์„œ ์„ฑ์žฅํ•˜์ž! ๐Ÿ’Ž

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0