Smart Contract Pause System on Anomaly Detection

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Smart Contract Pause System on Anomaly Detection
Medium
~2-3 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Development of Smart Contract Pause System on Anomaly

Pause mechanism—basic security element for any DeFi protocol handling user funds. When exploit starts, team has minutes, sometimes seconds, to stop bleeding. Manual reaction impossible—need automation. But wrongly triggered pause also damages: blocked transactions, user distrust, potential liquidations from inability to manage positions.

Task—build system pausing contract on real anomalies with minimal false positives, without itself becoming attack vector.

Pausable Contract: Basic Implementation

OpenZeppelin Pausable—standard starting point:

import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";

contract ProtectedVault is Pausable, AccessControl {
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE");

    function pause() external onlyRole(GUARDIAN_ROLE) {
        _pause();
    }

    function unpause() external onlyRole(DEFAULT_ADMIN_ROLE) {
        // DEFAULT_ADMIN = timelock / multisig governance
        _unpause();
    }

    function deposit(uint256 amount) external whenNotPaused {
        // ...
    }

    function withdraw(uint256 amount) external whenNotPaused {
        // ...
    }
}

Key: different roles for pause and unpause. Automatic guardian can be compromised or wrong—but unpause only via multisig/governance. Intentional asymmetry.

Anomalies: What to Detect

TVL Anomaly—Sharp Drop

If > X% TVL exits in N blocks—something wrong. May be legitimate (whale withdrawal) or exploit.

uint256 public constant MAX_TVL_DROP_BPS = 1000; // 10% per period
uint256 public constant MONITORING_WINDOW = 100; // blocks

function checkTVLAnomaly(uint256 currentTVL) internal {
    if (block.number >= snapshotBlock + MONITORING_WINDOW) {
        tvlSnapshot = currentTVL;
        snapshotBlock = block.number;
        return;
    }

    if (tvlSnapshot == 0) return;

    uint256 dropBps = ((tvlSnapshot - currentTVL) * 10000) / tvlSnapshot;
    if (currentTVL < tvlSnapshot && dropBps > MAX_TVL_DROP_BPS) {
        _triggerPause("TVL anomaly detected");
    }
}

Problem: if protocol allows legitimate large withdrawals, triggers on large LP exit. Needs tuning per protocol.

Unusually Large Single Transaction

uint256 public constant SINGLE_TX_THRESHOLD_BPS = 500; // 5% TVL

modifier checkWithdrawAnomaly(uint256 amount) {
    uint256 tvl = totalAssets();
    if (tvl > 0 && (amount * 10000 / tvl) > SINGLE_TX_THRESHOLD_BPS) {
        emit LargeWithdrawal(msg.sender, amount, tvl);
    }
    _;
}

Auto-pause on large withdrawals risky—legitimate for big participants. Better: emit events for off-chain monitoring.

Reentrancy Detection On-Chain

uint256 private _callDepth;

modifier noDeepCalls() {
    _callDepth++;
    if (_callDepth > 1) {
        _triggerPause("Reentrancy detected");
        revert("Reentrancy");
    }
    _;
    _callDepth--;
}

Addition to standard nonReentrant. Instead of revert—pause entire contract.

Off-Chain Monitoring with Auto-Pause

On-chain detectors limited: see only current transaction. More powerful pattern—off-chain monitoring + privileged pause transaction.

OpenZeppelin Defender

Sentinel + Autotask—standard stack:

async function(credentials) {
    const provider = new ethers.providers.JsonRpcProvider(credentials.secrets.ALCHEMY_URL);
    const vault = new ethers.Contract(VAULT_ADDRESS, VAULT_ABI, provider);
    
    const tvl = await vault.totalAssets();
    const threshold = await vault.pauseThreshold();

    if (tvl < threshold) {
        const signer = credentials.relayer.getSigner();
        const guardian = new ethers.Contract(GUARDIAN_ADDRESS, GUARDIAN_ABI, signer);
        
        await guardian.emergencyPause();
        await notifySlack(`Paused. TVL: ${tvl}, Threshold: ${threshold}`);
    }
}

Forta Network

Decentralized detection bot network. Write detection bot:

import { Finding, HandleTransaction, FindingSeverity, FindingType } from 'forta-agent';

const handleTransaction = async (txEvent) => {
    const findings = [];

    // Check large vault withdrawals
    const withdrawalEvents = txEvent.filterLog('Withdrawal(address,uint256,uint256)', VAULT_ADDRESS);

    for (const event of withdrawalEvents) {
        const amount = event.args.assets;
        if (amount > LARGE_WITHDRAWAL_THRESHOLD) {
            findings.push(
                Finding.fromObject({
                    name: 'Large Vault Withdrawal',
                    description: `${amount} assets withdrawn`,
                    alertId: 'VAULT-LARGE-WITHDRAWAL',
                    severity: FindingSeverity.High,
                    type: FindingType.Suspicious,
                })
            );
        }
    }

    return findings;
};

Circuit Breaker Pattern

More flexible than full pause—rate limiting on anomalies:

contract CircuitBreaker {
    enum Status { Normal, Restricted, Paused }
    Status public status;

    function withdraw(uint256 amount) external {
        require(status != Status.Paused, "Paused");

        if (status == Status.Restricted) {
            require(amount <= restrictedWithdrawLimit, "Exceeds restricted limit");
        }

        // Daily limit check
        uint256 today = block.timestamp / 1 days;
        if (today > lastResetDay) {
            dailyWithdrawn = 0;
            lastResetDay = today;
        }

        dailyWithdrawn += amount;
        require(dailyWithdrawn <= dailyWithdrawLimit, "Daily limit exceeded");

        // ... withdraw logic
    }
}

Advantage over full pause: On daily limit exceed, protocol doesn't pause—just rejects over-limit tx. Users continue within normal volume.

Governance over Pause Mechanism

Pause function itself can become attack vector: compromised guardian pauses, making funds inaccessible (DOS). Protections:

Time-limited pause: Auto-unpause after N blocks unless governance extends:

uint256 public pauseExpiry;
uint256 public constant MAX_AUTO_PAUSE_DURATION = 1 days;

function pause() external onlyGuardian {
    _pause();
    pauseExpiry = block.timestamp + MAX_AUTO_PAUSE_DURATION;
}

function checkAutoUnpause() public {
    if (paused() && block.timestamp > pauseExpiry) {
        _unpause();
    }
}

Multisig pause: Pause requires 2-of-3 guardians. One compromised key insufficient.

Timeline

Basic Pausable with OZ Defender monitoring—2-3 weeks. Full system with circuit breaker, Forta, governance—5-7 weeks.