Suspicious transaction monitoring system development

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
Suspicious transaction monitoring system development
Complex
~1-2 weeks
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

Setup of Automated Smart Contract Testing

Automated testing separates contracts with $10K TVL from those with $100M TVL. Not because audit replaces tests, but without test coverage audit costs 3–5x more and finds fewer issues: auditor spends time understanding basic behavior instead of analyzing edge cases.

Good test infrastructure — Foundry for fast unit/fuzz tests + coverage reports + CI pipeline preventing merge with failing tests.

Foundry as primary tool

Foundry — modern Solidity testing standard. Tests written in Solidity (not JavaScript), direct EVM internals access without ABI layer. Compilation and test execution in seconds vs minutes for Hardhat on large projects.

# Installation
curl -L https://foundry.paradigm.xyz | bash
foundryup

# Project structure
forge init my-project
# src/       - contracts
# test/       - tests
# script/    - deploy scripts
# lib/       - dependencies (git submodules)

Basic test structure

// test/Token.t.sol
pragma solidity ^0.8.20;

import {Test, console} from "forge-std/Test.sol";
import {MyToken} from "../src/MyToken.sol";

contract TokenTest is Test {
    MyToken token;
    address alice = makeAddr("alice");
    address bob = makeAddr("bob");
    
    function setUp() public {
        token = new MyToken(1_000_000e18);
        token.transfer(alice, 1000e18);
    }
    
    function test_transfer_success() public {
        vm.prank(alice);
        token.transfer(bob, 100e18);
        
        assertEq(token.balanceOf(bob), 100e18);
        assertEq(token.balanceOf(alice), 900e18);
    }
    
    function test_transfer_revertIfInsufficientBalance() public {
        vm.prank(alice);
        vm.expectRevert("ERC20: transfer amount exceeds balance");
        token.transfer(bob, 2000e18);
    }
}

Key vm cheat codes:

  • vm.prank(addr) — next call from addr
  • vm.startPrank(addr) / vm.stopPrank() — all subsequent calls from addr
  • vm.warp(timestamp) — set block.timestamp
  • vm.roll(blockNumber) — set block.number
  • vm.deal(addr, amount) — set ETH balance
  • vm.expectEmit(...) — expect specific event
  • vm.expectRevert(...) — expect revert with message

Fuzz testing

Fuzz testing — automatic test case generation. Foundry runs function thousands of times with different random inputs, searching for invariant violations.

// Fuzz test: function with parameters = fuzz test
function testFuzz_deposit_neverOverflows(uint256 amount) public {
    // bound limits input data range
    amount = bound(amount, 1, 1_000_000e18);
    
    deal(address(token), alice, amount);
    
    vm.prank(alice);
    token.approve(address(vault), amount);
    
    vm.prank(alice);
    vault.deposit(amount);
    
    // Invariant: shares always > 0 when deposit > 0
    assertGt(vault.balanceOf(alice), 0);
    // Invariant: total assets grows by amount
    assertEq(vault.totalAssets(), amount);
}

Foundry runs 256 iterations by default. In CI recommend 1000–10000 for critical functions:

# foundry.toml
[fuzz]
runs = 1000
seed = "0x1" # reproducibility with same seed

bound(amount, min, max) is critical. Without it, fuzz generates many zeros and max uint256 values, often failing obviously, missing interesting middle range.

Invariant testing

Invariant tests are more powerful. Foundry randomly calls any contract functions in any order and checks invariants never violated.

contract VaultInvariantTest is Test {
    Vault vault;
    ERC20Mock token;
    VaultHandler handler;
    
    function setUp() public {
        token = new ERC20Mock();
        vault = new Vault(address(token));
        handler = new VaultHandler(vault, token);
        
        // Tell Foundry to work only via handler
        targetContract(address(handler));
    }
    
    // Runs after each random action sequence
    function invariant_totalAssetsMatchBalances() public {
        assertEq(
            vault.totalAssets(),
            token.balanceOf(address(vault)),
            "Total assets mismatch"
        );
    }
}

// Handler controls allowed actions
contract VaultHandler is Test {
    Vault vault;
    ERC20Mock token;
    uint256 public lastRecordedPrice;
    
    function deposit(uint256 amount) external {
        amount = bound(amount, 1, 1e30);
        token.mint(address(this), amount);
        token.approve(address(vault), amount);
        vault.deposit(amount, address(this));
        lastRecordedPrice = vault.pricePerShare();
    }
    
    function withdraw(uint256 shares) external {
        shares = bound(shares, 0, vault.balanceOf(address(this)));
        if (shares > 0) vault.withdraw(shares, address(this), address(this));
    }
}

Invariant tests find complex inter-function bugs unit tests miss. ERC-4626 vault invariants (totalAssets = contract balance, share price monotonic) — classic example.

Fork testing

Fork tests work with real mainnet state, allowing integration testing with real Uniswap, Aave, Chainlink.

contract ForkTest is Test {
    uint256 mainnetFork;
    
    address constant UNISWAP_V3_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
    address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
    
    function setUp() public {
        // Fork mainnet at specific block (determinism)
        mainnetFork = vm.createFork(vm.envString("ETH_RPC_URL"), 19_500_000);
        vm.selectFork(mainnetFork);
    }
    
    function test_swapOnFork() public {
        address whale = 0x40ec5B33f54e0E8A33A975908C5BA1c14e5BbbDf;
        vm.prank(whale);
        IERC20(USDC).transfer(address(this), 10_000e6);
        
        IERC20(USDC).approve(UNISWAP_V3_ROUTER, 10_000e6);
        
        // ... swap logic
        
        uint256 amountOut = ISwapRouter(UNISWAP_V3_ROUTER).exactInputSingle(params);
        assertGt(amountOut, 0);
    }
}

RPC caching: Foundry caches RPC responses locally (~/.foundry/cache/). Rerunning fork tests on same block doesn't make new RPC requests.

Coverage and quality metrics

# Generate coverage report
forge coverage --report lcov

# Detailed terminal report
forge coverage --report summary

Coverage shows % of lines/functions/branches covered. Target metrics for production contracts:

Contract type Line coverage Branch coverage
Core logic (vault, AMM) >95% >90%
Peripheral (router, helper) >85% >75%
Admin/governance >90% >85%

But coverage isn't everything. 100% line coverage without assertions doesn't guarantee anything. Count assert* and vm.expectRevert per function.

CI/CD integration

# .github/workflows/test.yml
name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Install Foundry
        uses: foundry-rs/foundry-toolchain@v1

      - name: Run tests
        run: forge test --fork-url ${{ secrets.ETH_RPC_URL }} -vvv

      - name: Check coverage
        run: |
          forge coverage --report summary | tee coverage.txt
          python3 -c "
          import re, sys
          with open('coverage.txt') as f:
              content = f.read()
          match = re.search(r'Lines.*?(\d+\.\d+)%', content)
          if match and float(match.group(1)) < 90:
              print(f'Coverage {match.group(1)}% < 90%')
              sys.exit(1)
          "

Additional tools

Slither (static analysis, Trail of Bits): find known vulnerability patterns. Runs in seconds, integrates in CI.

Echidna (property-based fuzzer, Trail of Bits): more powerful fuzzer for specific invariants.

Halmos (formal verification): symbolically check all execution paths. Finds vulnerabilities statistical fuzzing misses.

Optimal setup for most projects: Foundry (unit + fuzz + invariant) + Slither (CI) + coverage gate. Echidna and Halmos for critical components.

Setup complete testing infrastructure: 1–2 weeks for medium project. Writing comprehensive tests for existing codebase — 3–6 weeks depending on size and complexity.