Przejmowanie kontroli nad DAO dawno już przeniosło się z publicznych wątków na Discourse do anonimowych pul płynności i zamkniętych grup na Telegramie. To tam, w ciągu zaledwie kilku bloków, rozstrzygają się losy miliardów dolarów.
Klasyczny atak 51% na protokół governance poprzez skupowanie natywnych tokenów z rynku (np. UNI czy AAVE) mija się z celem pod kątem ekonomicznym – poślizg cenowy (slippage) i mechanizmy stakingu błyskawicznie wywindują kurs w kosmos. Znacznie taniej jest "wypożyczyć" cudze głosy, wykorzystując mechanizm Voter Bribes (łapówki dla głosujących). Gdy cały ten proceder wychodzi poza przejrzyste platformy w stylu Votium czy Hidden Hand i ucieka w darmowe dilerki OTC albo zaszyfrowane smart kontrakty, rodzi się zjawisko Shadow Governance – ukryte zarządzanie, które potrafi wyczyścić skarbiec projektu do zera albo zmienić parametry ryzyka bez wiedzy zwykłego komunitu.
Anatomia Shadow Governance: Od Curve Wars po prywatne dark poole
Korzenie całego rynku łapówkowego sięgają architektury veTokenomics (vote-escrowed), przetestowanej przez Curve Finance. Blokując CRV nawet na 4 lata, użytkownik zgarniał veCRV, które decydowało o kierowaniu emisji tokenów do konkretnych pul płynności. Inne projekty szybko zwąchały pismo nosem: po co skupować CRV z rynku i zamrażać kapitał na 4 lata, skoro taniej wychodzi płacić cotygodniową "działkę" posiadaczom veCRV za oddanie głosu na wskazaną pulę?
Tak otworzyły się drzwi dla oficjalnych giełd łapówek:
- Votium (dla ekosystemu Convex/Curve)
- Hidden Hand od Redacted Cartel (spinający Balancera, Fraxa, Aurę)
Oficjalne przekupstwa mają jednak z punktu widzenia grubych ryb jedną zasadniczą wadę – są w pełni publiczne. Każdy analityk może odpalić dashboard i sprawdzić na tacy, kto komu płaci i za jaką pulę.
W Shadow Governance wjeżdżają zupełnie inne patenty:
- Flashloans Governance Interception: Przejmowanie protokołów bez blokad ve w mgnieniu oka przy użyciu pożyczek błyskawicznych (flash loans). Atakujący bierze pożyczkę, przepycha propozycję natychmiastowym zastawem i spłaca dług w tej samej transakcji.
- Off-Chain / OTC Bribe Matching: Umowy pod stołem, w których więksi gracze instytucjonalni trzymający ve-tokeny przytulają stablecoiny, altcoiny albo przydziały w przyszłych umowach SAFT na portfele powiernicze w zamian za ustawienie delegacji głosów.
- Private Dark Pools (Wrapped Voting Power): Owijanie (wrapping) tokenów governance w smart kontrakty, które rozdzielają wartość czysto ekonomiczną od prawa głosu. Same prawa do głosowania są tokenizowane i licytowane na ślepych aukcjach (Holenderskich).
Akcje z życia: Jak protokoły leciały na łeb przez ciche przekręty
Case 1: Atak na Beanstalk Farms (Kwiecień 2022) – $182 mln w plecy
Choć na papierze wyglądało to na klasyczny exploit z użyciem flash loana, w rzeczywistości był to podręcznikowy "błyskawiczny wrogi przejazd nad governance". Atakujący zgarnął z Aave pożyczkę flash loan na okrągły $1 mld w Lido stETH, Bean i innych tokenach, przejął ponad 70% siły głosu (Voting Power) w propozycji BIP-18 (Beanstalk Improvement Proposal) i w tej samej sekundzie wyprowadził cały skarbiec na swój adres.
Gdzie tu Shadow Governance: Protokół zliczał siłę głosu na żywo z salda tokenów w bieżącym bloku – zero opóźnień czasowych (Timelock), zero wymogu wcześniejszego stakingu.
Case 2: Przejęcie Tornado Cash Governance (Maj 2023)
Haker wrzucił proposal, który na pierwszy rzut oka wyglądał kropka w kropkę jak wcześniej przyjęty wniosek. Pod maską zaszył jednak złośliwy kod. Kiedy społecznośćklepnęła wniosek, atakujący odpalił funkcję selfdestruct, podmienił kod kontraktu pod tym samym adresem (używając CREATE2), przypisał sobie z powietrza 1.2 mln fikcyjnych głosów TORN, po czym przejął pełną władzę nad DAO i wyciągnął z treasury $2.1 mln.
Gdzie tu Shadow Governance: Gość użył sprytnej sztuczki z metamorficznymi kontraktami (metamorphic contracts), co pozwoliło mu całkowicie ominąć wizualne audyty wniosków i podmienić logikę tuż przed samym głosowaniem.
Jak namierzyć Shadow Governance bezpośrednio na chainie
Wyśledzenie ustaleń OTC przy piwie czy na Telegramie graniczy z cudem, ale w momencie egzekucji na blockchainie zawsze zostaje jakiś ślad. Cała analityka sprowadza się do wyłapywania anomaliów w ruchach grubych portfeli (wielorybów) oraz w kontraktach delegacji.
Legitny Bribe vs Shadow Governance – zestawienie
| Parametr | Jawny Bribe (Votium / Hidden Hand) | Shadow Governance (OTC / Private) |
|---|---|---|
| Przejrzystość wypłat | Dystrybucja on-chain przez Merkle Tree | Przelewy przez Tornado/Railgun/CEX albo świeżo upieczone portfele |
| Źródło środków | Publiczne smart kontrakty rynkowe | Zamknięte multisigi, portfele EOA, boty MEV |
| Delegowanie głosów | Zautomatyzowane przez meta-repozytoria | Nagłe, nietypowe roszady delegatów tuż przed samym głosowaniem |
| Zwrot z inwestycji (ROI) | Ograniczony rynkowym ROI z samej emisji | Absurdalnie wysokie nagrody za przepchnięcie konkretnego punktu |
| Timelock | Przestrzegany zgodnie ze standardowymi zasadami DAO | Omijany przy użyciu awaryjnych multisigów |
Praktyczny algorytm tropienia anomalii na chainie
Żeby wyłapać ukryte machnoje, inżynierowie backendu i analitycy stawiają monitoring pod kątem następujących anomalii:
- Śledzenie wzorców delegacji (Delegate Tracking): Nasłuchiwanie zdarzenia
DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate). Jeśli gruby portfel (top 20 holderów) przerzuca siłę głosu na pusty adres na 2-3 bloki przed zamknięciem okienka votingowego, to masz jak w banku, że wjechał układ OTC. - Ruchy środków z mikserów (Flash-Mixers): Jeśli portfel, na który spłynęły delegacje, dostaje strzał ze stablecoinów albo natywnych coinów z mikserów na kilka godzin przed głosowaniem, na 99% trwa właśnie rozliczanie zlecenia.
- Mempool i podchody przez MEV (MEV-driven Voting): Dopychanie propozycji w ostatnim możliwym bloku. Atakujący wysyłają prywatną transakcję bezpośrednio do buildera (np. przez Flashbots), żeby do samego końca ukryć budowanie krytycznej masy głosów.
Wyłapywanie anomalii: Skrypt w Pythonie do monitorowania delegacji
Poniżej znajdziesz lekki skrypt w Pythonie z użyciem biblioteki web3.py, który wyłapuje nagłe przerzuty dużej siły głosu (voting power) w tokenach ERC20Votes (architektura Compound/OpenZeppelin) tuż przed głosowaniami.
import time
import logging
from collections import defaultdict
from web3 import Web3
# Konfiguracja logowania
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
RPC_URL = "https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY"
RAW_TOKEN_ADDRESS = "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984" # UNI
# Bufor bezpieczeństwa przed reorgami sieci (12 bloków ~= 2.5 minuty)
SAFE_CONFIRMATIONS = 12
BATCH_STEP = 1000
ABI = [
{
"inputs": [],
"name": "decimals",
"outputs": [{"type": "uint8"}],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalSupply",
"outputs": [{"type": "uint256"}],
"stateMutability": "view",
"type": "function"
},
{
"anonymous": False,
"inputs": [
{"indexed": True, "name": "delegate", "type": "address"},
{"indexed": False, "name": "previousBalance", "type": "uint256"},
{"indexed": False, "name": "newBalance", "type": "uint256"}
],
"name": "DelegateVotesChanged",
"type": "event"
},
{
"anonymous": False,
"inputs": [
{"indexed": True, "name": "delegator", "type": "address"},
{"indexed": True, "name": "fromDelegate", "type": "address"},
{"indexed": True, "name": "toDelegate", "type": "address"}
],
"name": "DelegateChanged",
"type": "event"
}
]
# 1. Weryfikacja połączenia RPC
w3 = Web3(Web3.HTTPProvider(RPC_URL))
if not w3.is_connected():
raise ConnectionError("RPC nie odpowiada. Sprawdź klucz API lub stan węzła.")
# 2. Checksum i załadowanie kontraktu
token_address = w3.to_checksum_address(RAW_TOKEN_ADDRESS)
contract = w3.eth.contract(address=token_address, abi=ABI)
# 3. Parametry dynamiczne
try:
TOKEN_DECIMALS = contract.functions.decimals().call()
TOTAL_SUPPLY = contract.functions.totalSupply().call()
logging.info(f"Token załadowany. Decimals: {TOKEN_DECIMALS} | Total Supply: {TOTAL_SUPPLY / (10 ** TOKEN_DECIMALS):,.0f}")
except Exception as e:
logging.warning(f"Nie udało się pobrać parametrów tokena, ustawiam wartości domyślne: {e}")
TOKEN_DECIMALS = 18
TOTAL_SUPPLY = 1000000000 * (10 ** 18) # Domyślnie 1B
# Próg anomalii: 100,000 tokenów
VOTE_THRESHOLD = 100000 * (10 ** TOKEN_DECIMALS)
def get_logs_batched(event_obj, from_block, to_block):
"""Bezpieczne pobieranie logów w paczkach. Zwraca (logs, is_success)."""
all_events = []
for start in range(from_block, to_block + 1, BATCH_STEP):
end = min(start + BATCH_STEP - 1, to_block)
try:
logs = event_obj.get_logs(fromBlock=start, toBlock=end)
all_events.extend(logs)
except Exception as e:
logging.error(f"Błąd podczas pobierania logów dla bloków {start}-{end}: {e}")
return [], False
return all_events, True
def analyze_governance_shifts(from_block, to_block):
"""Analityka przetasowań głosów z wyliczeniem udziału w Total Supply."""
vote_changes, ok1 = get_logs_batched(contract.events.DelegateVotesChanged, from_block, to_block)
delegations, ok2 = get_logs_batched(contract.events.DelegateChanged, from_block, to_block)
if not (ok1 and ok2):
return False # Błąd w logach, NIE przesuwamy wskaźnika bloku!
# Grupowanie zdarzeń DelegateChanged w ramach jednej transakcji
delegation_map = defaultdict(list)
for d in delegations:
tx_hash = d.transactionHash.hex()
delegation_map[tx_hash].append({
"delegator": d.args.delegator,
"from": d.args.fromDelegate,
"to": d.args.toDelegate
})
for event in vote_changes:
prev_bal = event.args.previousBalance
new_bal = event.args.newBalance
delta = new_bal - prev_bal
if abs(delta) >= VOTE_THRESHOLD:
tx_hash = event.transactionHash.hex()
delegate = event.args.delegate
# Wyliczenie wskaźników względem Total Supply
fmt_delta = delta / (10 ** TOKEN_DECIMALS)
fmt_new = new_bal / (10 ** TOKEN_DECIMALS)
share_of_supply = (new_bal / TOTAL_SUPPLY) * 100
delta_share_of_supply = (abs(delta) / TOTAL_SUPPLY) * 100
# Poziom krytyczności
severity = "SKOK KRYTYCZNY" if share_of_supply >= 1.0 else "UWAGA"
logging.warning(f"[{severity}] Zmiana Voting Power: {fmt_delta:+,.0f} tokenów ({delta_share_of_supply:.3f}% podaży)")
logging.info(f" Delegat: {delegate}")
logging.info(f" Ostateczna siła adresu: {fmt_new:,.0f} głosów ({share_of_supply:.3f}% Total Supply)")
logging.info(f" Tx Hash: {tx_hash}")
contexts = delegation_map.get(tx_hash, [])
if contexts:
logging.info(f" Przyczyna: BEZPOŚREDNIA ZMIANA DELEGATA ({len(contexts)} zdarzeń w Tx)")
for ctx in contexts:
logging.info(f" • Delegator: {ctx['delegator']} | {ctx['from']} -> {ctx['to']}")
else:
logging.info(" Przyczyna: Zmiana salda obecnego delegata (Transfer / Claim / Mint / Burn)")
print("-" * 75)
return True
def start_realtime_monitoring(poll_interval=12):
"""Demon czasu rzeczywistego pilnujący bufora potwierdzeń."""
latest_block = w3.eth.block_number
last_processed_block = latest_block - SAFE_CONFIRMATIONS - 1
logging.info(f"Start demon. Początkowy bezpieczny blok: #{last_processed_block} (Confirmations = {SAFE_CONFIRMATIONS})")
while True:
try:
current_block = w3.eth.block_number
safe_block = current_block - SAFE_CONFIRMATIONS
if safe_block > last_processed_block:
success = analyze_governance_shifts(last_processed_block + 1, safe_block)
if success:
last_processed_block = safe_block
else:
logging.warning("Zakres nie został w pełni przetworzony przez błąd RPC. Ponawiam próbę...")
except Exception as e:
logging.error(f"Krytyczny błąd w pętli głównej: {e}")
time.sleep(poll_interval)
if __name__ == "__main__":
start_realtime_monitoring(poll_interval=12)Jak zabezpieczyć się przed Shadow Governance
Rynek powoli wyciąga wnioski z kolejnych ataków na systemy zarządzania. Pobieżny audyt smart kontraktu to dziś za mało – obrona musi być wbudowana w samą architekturę DAO.
- Timelocki z funkcją Veto: Zmiany w kodzie nie mogą wchodzić od strzała. Wdrożenie bufora bezpieczeństwa na minimum 48–72 godziny daje czas radzie bezpieczeństwa (Security Council) na zablokowanie podejrzanej propozycji.
- Przejście na ve-Model z zamrożonym odblokowaniem: Gdy tokeny są zablokowane na twardo bez możliwości szybkiego obrotu, koszt przeprowadzenia błyskawicznego ataku rośnie drastycznie.
- Zarządzanie optymistyczne (Optimistic Governance): Podejście wdrożone m.in. przez Moonwell czy Lido – wnioski przechodzą automatycznie, a pełne głosowanie odpala się tylko wtedy, gdy społeczność zgłosi sprzeciw (veto).
- Dual-Token Governance: Rozdzielenie władzy między posiadaczy tokenów użytkowych a dostawców płynności / użytkowników protokołu (dokładnie tak działa to w Lido via Dual Governance, gdzie trzymający stETH mogą uwalić decyzje podejmowane przez holderów LDO).
Shadow Governance to naturalna konsekwencja faktu, że prawa do głosowania stały się zwykłym towarem. Zadaniem analityków i inżynierów blockchain w EXMON jest wyłapywanie takich anomalii w mempoolu oraz na poziomie smart kontraktów na tyle wcześnie, by zapewnić pełne bezpieczeństwo i przejrzystość wszystkim uczestnikom naszego ekosystemu.