From e5acdc0fde05a61710f9b859fa7d1f955f3a6b56 Mon Sep 17 00:00:00 2001 From: hyapiskan Date: Sat, 20 Sep 2025 04:57:47 +0200 Subject: [PATCH 1/2] feat: Add BalanceDropTrap example with working config and tests --- foundry/balance-drop-trap/foundry.toml | 8 ++++ .../balance-drop-trap/src/BalanceDropTrap.sol | 45 ++++++++++++++++++ .../test/BalanceDropTrap.t.sol | 47 +++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 foundry/balance-drop-trap/foundry.toml create mode 100644 foundry/balance-drop-trap/src/BalanceDropTrap.sol create mode 100644 foundry/balance-drop-trap/test/BalanceDropTrap.t.sol diff --git a/foundry/balance-drop-trap/foundry.toml b/foundry/balance-drop-trap/foundry.toml new file mode 100644 index 0000000..4b65823 --- /dev/null +++ b/foundry/balance-drop-trap/foundry.toml @@ -0,0 +1,8 @@ +[profile.default] +src = 'src' +test = 'test' +libs = ['../../node_modules'] +remappings = [ + 'drosera-contracts/=../../node_modules/contracts/src/', + 'forge-std/=../../node_modules/forge-std/src/' +] diff --git a/foundry/balance-drop-trap/src/BalanceDropTrap.sol b/foundry/balance-drop-trap/src/BalanceDropTrap.sol new file mode 100644 index 0000000..8da61ed --- /dev/null +++ b/foundry/balance-drop-trap/src/BalanceDropTrap.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {ITrap} from "drosera-contracts/interfaces/ITrap.sol"; + +// Bu tuzak, belirli bir cüzdanın ETH bakiyesindeki ani düşüşleri tespit eder. +contract BalanceDropTrap is ITrap { + // İzlenecek cüzdanın adresi (Örnek olarak Vitalik Buterin'in adresi kullanılmıştır) + address constant TARGET_WALLET = 0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B; + // Alarm için eşik yüzdesi (Örn: %50'den fazla düşerse) + uint256 constant THRESHOLD_PERCENT = 50; + + // Bu fonksiyon her blokta çalışır ve cüzdanın o anki ETH bakiyesini kaydeder. + function collect() external view returns (bytes memory) { + return abi.encode(TARGET_WALLET.balance); + } + + // Bu fonksiyon, geçmiş bloklardaki verileri analiz eder. + function shouldRespond(bytes[] calldata data) external pure returns (bool, bytes memory) { + // Analiz için en az 2 blok verisi (önceki ve şimdiki bakiye) gerekir. + if (data.length < 2) { + return (false, bytes("")); + } + + uint256 previousBalance = abi.decode(data[data.length - 2], (uint256)); + uint256 currentBalance = abi.decode(data[data.length - 1], (uint256)); + + // Bakiye düşmediyse veya önceki bakiye 0 ise, sorun yoktur. + if (currentBalance >= previousBalance || previousBalance == 0) { + return (false, bytes("")); + } + + // Düşüş miktarını ve yüzdesini hesapla. + uint256 dropAmount = previousBalance - currentBalance; + uint256 dropPercentage = (dropAmount * 100) / previousBalance; + + // Eğer düşüş yüzdesi belirlediğimiz eşikten büyükse, ALARM VER! + if (dropPercentage >= THRESHOLD_PERCENT) { + return (true, abi.encode(previousBalance, currentBalance)); + } + + // Eşikten küçükse, sorun yoktur. + return (false, bytes("")); + } +} diff --git a/foundry/balance-drop-trap/test/BalanceDropTrap.t.sol b/foundry/balance-drop-trap/test/BalanceDropTrap.t.sol new file mode 100644 index 0000000..a0bfd39 --- /dev/null +++ b/foundry/balance-drop-trap/test/BalanceDropTrap.t.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {Test} from "forge-std/Test.sol"; +import {BalanceDropTrap} from "../../src/BalanceDropTrap.sol"; + +// Test kontratımız, Foundry'nin Test kontratından miras alır. +contract BalanceDropTrapTest is Test { + BalanceDropTrap public trap; + + // Bu fonksiyon, her testten önce çalışır ve tuzağımızı kurar. + function setUp() public { + trap = new BalanceDropTrap(); + } + + // Bu test, bakiye %50'den fazla düştüğünde tuzağın alarm vermesi gerektiğini doğrular. + function testShouldRespondOnSignificantDrop() public { + // Geçmiş verileri simüle ediyoruz. + bytes[] memory data = new bytes[](2); + + // Önceki bakiye: 100 ETH + data[0] = abi.encode(100 ether); + // Şimdiki bakiye: 40 ETH (yani %60'lık bir düşüş) + data[1] = abi.encode(40 ether); + + // Tuzağımızın shouldRespond fonksiyonunu bu sahte verilerle çağırıyoruz. + (bool should, ) = trap.shouldRespond(data); + + // Sonucun 'true' olmasını bekliyoruz ve bunu doğruluyoruz. + assertTrue(should, "Trap should respond on a >50% balance drop"); + } + + // Bu test, bakiye %50'den az düştüğünde tuzağın alarm VERMEMESİ gerektiğini doğrular. + function testShouldNotRespondOnInsignificantDrop() public { + bytes[] memory data = new bytes[](2); + + // Önceki bakiye: 100 ETH + data[0] = abi.encode(100 ether); + // Şimdiki bakiye: 80 ETH (yani %20'lik bir düşüş) + data[1] = abi.encode(80 ether); + + (bool should, ) = trap.shouldRespond(data); + + // Sonucun 'false' olmasını bekliyoruz ve bunu doğruluyoruz. + assertFalse(should, "Trap should NOT respond on a <50% balance drop"); + } +} From 3200e573d530ecdc0d743896e0620f24eef2256e Mon Sep 17 00:00:00 2001 From: hyapiskan Date: Fri, 26 Sep 2025 01:32:01 +0200 Subject: [PATCH 2/2] fix: Apply suggestions from review --- .../balance-drop-trap/src/BalanceDropTrap.sol | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/foundry/balance-drop-trap/src/BalanceDropTrap.sol b/foundry/balance-drop-trap/src/BalanceDropTrap.sol index 8da61ed..39d3be8 100644 --- a/foundry/balance-drop-trap/src/BalanceDropTrap.sol +++ b/foundry/balance-drop-trap/src/BalanceDropTrap.sol @@ -3,43 +3,46 @@ pragma solidity ^0.8.20; import {ITrap} from "drosera-contracts/interfaces/ITrap.sol"; -// Bu tuzak, belirli bir cüzdanın ETH bakiyesindeki ani düşüşleri tespit eder. +// This trap detects sudden drops in the ETH balance of a specific wallet. contract BalanceDropTrap is ITrap { - // İzlenecek cüzdanın adresi (Örnek olarak Vitalik Buterin'in adresi kullanılmıştır) + // The wallet address to monitor (Example: Vitalik Buterin's address) address constant TARGET_WALLET = 0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B; - // Alarm için eşik yüzdesi (Örn: %50'den fazla düşerse) - uint256 constant THRESHOLD_PERCENT = 50; - - // Bu fonksiyon her blokta çalışır ve cüzdanın o anki ETH bakiyesini kaydeder. - function collect() external view returns (bytes memory) { - return abi.encode(TARGET_WALLET.balance); + + // Using BPS for finer control (10,000 = 100%) + uint256 constant BPS = 10_000; + uint256 constant THRESHOLD_BPS = 5_000; // 50% threshold + + // This function runs on every block and records the wallet's current ETH balance. + function collect() external view override returns (bytes memory) { + // Including block.number for easier debugging as suggested + return abi.encode(block.number, TARGET_WALLET.balance); } - // Bu fonksiyon, geçmiş bloklardaki verileri analiz eder. - function shouldRespond(bytes[] calldata data) external pure returns (bool, bytes memory) { - // Analiz için en az 2 blok verisi (önceki ve şimdiki bakiye) gerekir. + // This function analyzes the data from past blocks. + function shouldRespond(bytes[] calldata data) external pure override returns (bool, bytes memory) { + // At least 2 blocks of data are needed for analysis. if (data.length < 2) { return (false, bytes("")); } - uint256 previousBalance = abi.decode(data[data.length - 2], (uint256)); - uint256 currentBalance = abi.decode(data[data.length - 1], (uint256)); - - // Bakiye düşmediyse veya önceki bakiye 0 ise, sorun yoktur. + // Drosera passes samples newest first: data[0] is current, data[1] is previous. + ( , uint256 currentBalance) = abi.decode(data[0], (uint256, uint256)); + ( , uint256 previousBalance) = abi.decode(data[1], (uint256, uint256)); + + // If the balance has not dropped or the previous balance was 0, there is no issue. if (currentBalance >= previousBalance || previousBalance == 0) { return (false, bytes("")); } - // Düşüş miktarını ve yüzdesini hesapla. - uint256 dropAmount = previousBalance - currentBalance; - uint256 dropPercentage = (dropAmount * 100) / previousBalance; + // Calculate the drop amount and percentage in BPS. + uint256 dropBps = ((previousBalance - currentBalance) * BPS) / previousBalance; - // Eğer düşüş yüzdesi belirlediğimiz eşikten büyükse, ALARM VER! - if (dropPercentage >= THRESHOLD_PERCENT) { + // If the drop is greater than our threshold, TRIGGER THE ALARM! + if (dropBps >= THRESHOLD_BPS) { return (true, abi.encode(previousBalance, currentBalance)); } - // Eşikten küçükse, sorun yoktur. + // If the drop is less than the threshold, there is no issue. return (false, bytes("")); } }