Разработка системы rate-limiting для мостов

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Разработка системы rate-limiting для мостов
Сложная
~3-5 рабочих дней
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1221
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1163
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    855
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1056
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    828

Разработка системы детекции атак на governance

Атаки на DeFi governance — класс угроз, который недооценивают. DeFi-разработчики традиционно фокусируются на безопасности core контрактов: reentrancy, overflow, oracle manipulation. Но governance — это backdoor в любой протокол: успешная атака на governance даёт атакующему право менять любые параметры, дренировать treasury, апгрейдить контракты к malicious версиям.

Beanstalk Farms ($182M, апрель 2022) — атака была выполнена через легитимный governance процесс. Атакующий использовал flash loan для получения большинства votes и моментально прошёл злонамеренный пропозал. Не было взломано ни одного смарт-контракта — только воспользовались отсутствием защит в governance механизме.

Система детекции атак — это on-chain мониторинг + off-chain аналитика, которые идентифицируют подозрительные паттерны в governance активности до того как атака успевает причинить ущерб.

Классификация атак на governance

Класс 1: Flash loan governance attack

Атакующий одалживает governance токены на одну транзакцию, голосует, возвращает. Работает только если:

  • Нет voting delay (пропозал можно исполнить сразу)
  • Snapshot голосования = текущий блок
  • Нет требования длительного holding period

Mitigation уже в контракте: ERC-20Votes checkpoints + voting delay. Детекция: мониторинг транзакций где flash loan и governance vote происходят в одном tx.

Класс 2: Whale accumulation attack

Атакующий постепенно накапливает governing tokens через OTC, DEX покупки, заёмные позиции — не раскрывая намерения. Когда достигает достаточного порога — создаёт и исполняет злонамеренный пропозал.

Это легальная активность до момента атаки. Детекция: паттерны аномально быстрого накопления tokens на неизвестных адресах.

Класс 3: Bribe и voting coercion

Атакующий подкупает делегатов через bribing протоколы (Hidden Hand, Votium) или напрямую, чтобы те проголосовали за злонамеренный пропозал. Делегаты могут действовать добросовестно, не понимая последствий сложного технического пропозала.

Детекция: резкий рост bribe activity для конкретного пропозала, несоответствие сложности пропозала и скорости его прохождения.

Класс 4: Long-range накопление и координированная атака

Несколько адресов, которые кажутся независимыми, накапливают tokens на протяжении месяцев. Координированно голосуют. On-chain они не связаны — только общий источник финансирования или общие паттерны активности.

Детекция: graph analysis транзакционных связей, timing correlation между адресами.

Класс 5: Proposal complexity exploit

Легитимный-выглядящий пропозал содержит скрытый malicious эффект. Например, апгрейд контракта где новая реализация имеет backdoor. Или multi-action пропозал где одно действие из пяти — drain treasury.

Детекция: автоматический анализ calldata пропозалов, симуляция исполнения.

On-chain компоненты детекции

Vote Weight Anomaly Detector

contract GovernanceMonitor {
    IGovernor public governor;
    IVotes public votingToken;
    
    // Threshold: если один адрес голосует > X% от кворума — алерт
    uint256 public constant WHALE_THRESHOLD_PERCENT = 20;
    
    // Хранение истории voting power для детекции резкого роста
    mapping(address => uint256) public lastKnownVotingPower;
    mapping(address => uint256) public lastUpdateBlock;
    
    event WhaleVoteDetected(
        uint256 indexed proposalId,
        address indexed voter,
        uint256 votingPower,
        uint256 percentOfQuorum,
        uint8 support
    );
    
    event RapidPowerAccumulation(
        address indexed account,
        uint256 previousPower,
        uint256 currentPower,
        uint256 percentIncrease,
        uint256 blocksElapsed
    );
    
    function checkVote(
        uint256 proposalId,
        address voter,
        uint8 support,
        uint256 weight
    ) external {
        uint256 quorum = governor.quorum(governor.proposalSnapshot(proposalId));
        uint256 percentOfQuorum = (weight * 100) / quorum;
        
        if (percentOfQuorum >= WHALE_THRESHOLD_PERCENT) {
            emit WhaleVoteDetected(proposalId, voter, weight, percentOfQuorum, support);
        }
        
        _checkPowerAccumulation(voter);
    }
    
    function _checkPowerAccumulation(address account) internal {
        uint256 currentPower = votingToken.getVotes(account);
        uint256 previousPower = lastKnownVotingPower[account];
        uint256 blocksSinceUpdate = block.number - lastUpdateBlock[account];
        
        if (previousPower > 0 && blocksSinceUpdate < 1000) { // ~3.5 часа
            uint256 percentIncrease = ((currentPower - previousPower) * 100) / previousPower;
            
            if (percentIncrease > 50) { // >50% роста за <3.5 часа
                emit RapidPowerAccumulation(
                    account,
                    previousPower,
                    currentPower,
                    percentIncrease,
                    blocksSinceUpdate
                );
            }
        }
        
        lastKnownVotingPower[account] = currentPower;
        lastUpdateBlock[account] = block.number;
    }
}

Этот контракт вызывается через Governor hook (если Governor поддерживает) или через off-chain мониторинг с последующим on-chain записью алерта.

Proposal Simulation Engine

Автоматическая симуляция исполнения каждого нового пропозала до его обсуждения сообществом.

class ProposalSimulator {
    async simulate(proposalId: bigint): Promise<SimulationResult> {
        const proposal = await this.getProposalDetails(proposalId);
        
        // Форк mainnet на текущем блоке
        const fork = await this.createFork();
        
        const results: ActionResult[] = [];
        
        for (const action of proposal.actions) {
            try {
                // Симулируем исполнение через eth_call
                const result = await fork.simulate({
                    from: timelockAddress,
                    to: action.target,
                    data: action.calldata,
                    value: action.value
                });
                
                results.push({
                    success: true,
                    action,
                    stateChanges: await this.analyzeStateChanges(fork, result),
                    tokenTransfers: await this.extractTransfers(result.logs)
                });
            } catch (error) {
                results.push({ success: false, action, error: error.message });
            }
        }
        
        // Анализ опасных паттернов в результатах
        const risks = await this.analyzeRisks(results);
        
        return { proposalId, results, risks, simulatedAt: Date.now() };
    }
    
    async analyzeRisks(results: ActionResult[]): Promise<Risk[]> {
        const risks: Risk[] = [];
        
        for (const result of results) {
            // Трансферы из treasury > $1M
            const largeTransfers = result.tokenTransfers.filter(
                t => t.from === TREASURY_ADDRESS && t.valueUSD > 1_000_000
            );
            if (largeTransfers.length > 0) {
                risks.push({
                    level: 'HIGH',
                    type: 'LARGE_TREASURY_TRANSFER',
                    details: largeTransfers
                });
            }
            
            // Изменение owner/admin критических контрактов
            const ownerChanges = result.stateChanges.filter(
                c => c.slot === OWNER_SLOT && CRITICAL_CONTRACTS.includes(c.address)
            );
            if (ownerChanges.length > 0) {
                risks.push({
                    level: 'CRITICAL',
                    type: 'OWNERSHIP_TRANSFER',
                    details: ownerChanges
                });
            }
            
            // Upgrade proxy к неизвестной implementation
            const upgrades = result.stateChanges.filter(
                c => c.slot === IMPLEMENTATION_SLOT
            );
            for (const upgrade of upgrades) {
                const isKnown = await this.isKnownContract(upgrade.newValue);
                if (!isKnown) {
                    risks.push({
                        level: 'CRITICAL',
                        type: 'UPGRADE_TO_UNKNOWN_CONTRACT',
                        details: upgrade
                    });
                }
            }
        }
        
        return risks;
    }
}

Decoding и человекочитаемое описание calldata

Технический пропозал должен быть переведён на человеческий язык для community review.

const FUNCTION_REGISTRY: Record<string, FunctionDescriptor> = {
    // Compound Governor
    '0x3c3e4f7a': {
        name: 'setCollateralFactor',
        protocol: 'Compound',
        category: 'risk-parameter',
        description: (params) => 
            `Change collateral factor for ${params.cToken} from current to ${formatPercent(params.newCollateralFactorMantissa)}`
    },
    // Uniswap Governor
    '0x4f1ef286': {
        name: 'upgradeToAndCall',
        protocol: 'Generic',
        category: 'upgrade',
        description: (params) =>
            `Upgrade proxy implementation to ${params.newImplementation}`
    }
};

async function decodeProposalAction(target: string, calldata: string): Promise<string> {
    const selector = calldata.slice(0, 10);
    const descriptor = FUNCTION_REGISTRY[selector];
    
    if (!descriptor) {
        // Неизвестная функция — высокий риск
        return `⚠️ Unknown function call to ${target}: selector ${selector}`;
    }
    
    const iface = new ethers.Interface([`function ${descriptor.name}(...)`]);
    const params = iface.decodeFunctionData(descriptor.name, calldata);
    
    return descriptor.description(params);
}

Off-chain аналитика: graph analysis

Координированные атаки выявляются через анализ транзакционного графа.

Кластеризация адресов

import networkx as nx
from collections import defaultdict

class AddressCluster:
    def __init__(self, provider):
        self.provider = provider
        self.graph = nx.DiGraph()
    
    def build_funding_graph(self, addresses: list[str], lookback_blocks: int):
        """Строит граф финансирования между адресами"""
        for addr in addresses:
            txs = self.get_outgoing_transfers(addr, lookback_blocks)
            for tx in txs:
                self.graph.add_edge(
                    tx['from'], 
                    tx['to'],
                    weight=tx['value'],
                    token=tx['token']
                )
    
    def find_common_funders(self, voters: list[str]) -> dict:
        """Находит общих финансировщиков для набора voters"""
        common_sources = defaultdict(list)
        
        for voter in voters:
            # Все адреса, от которых получал funds (глубина 2)
            ancestors = nx.ancestors(self.graph, voter)
            for ancestor in ancestors:
                common_sources[ancestor].append(voter)
        
        # Адреса финансирующие > 2 voters — подозрительны
        suspicious = {
            source: voters 
            for source, voters in common_sources.items() 
            if len(voters) >= 3
        }
        
        return suspicious
    
    def detect_timing_correlation(self, voters: list[str], window_blocks: int = 100):
        """Выявляет voters с одинаковым timing активности"""
        activity_windows = {}
        
        for voter in voters:
            txs = self.get_all_txs(voter, 10000) # последние 10k блоков
            window_ids = set(tx['blockNumber'] // window_blocks for tx in txs)
            activity_windows[voter] = window_ids
        
        # Jaccard similarity между voters
        correlations = []
        for i, v1 in enumerate(voters):
            for v2 in voters[i+1:]:
                intersection = len(activity_windows[v1] & activity_windows[v2])
                union = len(activity_windows[v1] | activity_windows[v2])
                similarity = intersection / union if union > 0 else 0
                
                if similarity > 0.7: # >70% одинаковых активных окон
                    correlations.append((v1, v2, similarity))
        
        return correlations

Bribe detection

Votium, Hidden Hand, Paladin — основные bribing протоколы. Мониторинг:

// Отслеживаем bribe events для конкретного proposal
const votiumContract = new ethers.Contract(VOTIUM_ADDRESS, VOTIUM_ABI, provider);
const filter = votiumContract.filters.NewIncentive(proposalId);

votiumContract.on(filter, async (proposalId, token, amount, event) => {
    const amountUSD = await priceOracle.getUSD(token, amount);
    
    if (amountUSD > BRIBE_ALERT_THRESHOLD) {
        await alertChannel.send({
            type: 'LARGE_BRIBE',
            proposalId,
            amount: amountUSD,
            token,
            txHash: event.transactionHash
        });
    }
});

Alerting и response система

Уровни алертов

Уровень Триггер Действие
INFO Новый пропозал создан Публикация в Discord/Telegram
MEDIUM Whale vote (>20% quorum) Уведомление security multisig
HIGH Rapid accumulation или flash loan в tx Пуш-уведомление команде
CRITICAL Симуляция выявила treasury drain / unknown upgrade Автопауза (если контракт позволяет) + экстренный созыв

Emergency response автоматика

// Guardian контракт: может моментально отменить пропозал при алерте
contract GovernanceGuardian {
    IGovernor public governor;
    address[] public guardians; // multisig members
    uint256 public threshold;
    
    mapping(bytes32 => uint256) public guardianSignatures;
    
    function signCancelProposal(uint256 proposalId) external {
        require(isGuardian[msg.sender], "Not guardian");
        bytes32 key = keccak256(abi.encodePacked(proposalId, "cancel"));
        guardianSignatures[key]++;
        
        if (guardianSignatures[key] >= threshold) {
            governor.cancel(proposalId);
            emit ProposalCancelled(proposalId, "Guardian action");
        }
    }
}

Guardian не может принимать положительные действия — только отменять пропозалы. Это сохраняет децентрализацию (guardian не может украсть governance), но даёт экстренный рычаг.

Интеграция с Forta Network

Forta — decentralized network ботов для on-chain мониторинга. Публикация detection бота:

// forta-agent.js
const { Finding, FindingSeverity, FindingType, ethers } = require('forta-agent');

const GOVERNOR_ADDRESS = '0x...';
const GOVERNOR_ABI = [...];

async function handleTransaction(txEvent) {
    const findings = [];
    
    // Детектируем Vote events
    const voteEvents = txEvent.filterLog(
        'event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason)',
        GOVERNOR_ADDRESS
    );
    
    for (const event of voteEvents) {
        const { voter, proposalId, weight } = event.args;
        
        // Проверяем наличие flash loan в той же транзакции
        const flashLoanEvents = txEvent.filterLog([
            'event FlashLoan(address indexed target, address indexed initiator, address indexed asset, uint256 amount, uint256 premium, uint16 referralCode)',
        ]);
        
        if (flashLoanEvents.length > 0) {
            findings.push(Finding.fromObject({
                name: 'Flash Loan Governance Vote',
                description: `Address ${voter} voted on proposal ${proposalId} in same tx as flash loan`,
                alertId: 'GOVERNANCE-FLASH-LOAN',
                severity: FindingSeverity.Critical,
                type: FindingType.Exploit,
                metadata: { voter, proposalId: proposalId.toString(), weight: weight.toString() }
            }));
        }
    }
    
    return findings;
}

module.exports = { handleTransaction };

Стек и сроки

On-chain: Solidity + Foundry. GovernanceMonitor контракт + Guardian контракт. Off-chain: TypeScript + Node.js + viem. ProposalSimulator + GraphAnalyzer + BribeMonitor. Alerting: Forta Network + PagerDuty / Telegram webhook. Storage: PostgreSQL для истории proposals и аномалий.

Компонент Срок
On-chain monitor контракт 1–2 недели
Proposal simulation engine 2–3 недели
Graph analysis (кластеризация) 2 недели
Bribe monitoring 1 неделя
Forta bot 1 неделя
Alerting pipeline 1 неделя
Dashboard 2–3 недели

Полная система детекции: 2–3 месяца. Базовый мониторинг с алертами (без graph analysis): 4–6 недель.

Стоимость зависит от количества отслеживаемых протоколов и требуемой глубины аналитики. Интеграция с существующим протоколом на базе OpenZeppelin Governor обходится дешевле — Governor уже предоставляет нужные события.