Using Drosera Traps to Investigate a Vulnerable Smart Contract

Scofield Idehen - Feb 17 - - Dev Community

Drosera is an automated protocol that simplifies the process of creating monitoring systems for decentralized applications.

This guide focuses on building a simple smart contract with a bug and investigating it using Drosera Traps.

To follow this guide you must have basic ideas on the following:

  • Solidity
  • Foundry
  • Metamask

To get started, spin up a boilerplate npx @drosera/create-drosera-app or set up one using forge init -t drosera-network/trap-foundry-template

If you are doing this for the first time, you will have to install bun.

# install Bun 
curl -fsSL https://bun.sh/install | bash

# install node modules
bun install
Enter fullscreen mode Exit fullscreen mode

After installation create a vunerable.sol in the src folder, for this guide we will be using a contract that allows anyone to take ownership.

pragma solidity ^0.8.12;
contract VulnerableContract {
    address public Owner;
    // Constructor to set the initial owner
    constructor(address _owner) {
        Owner = _owner;
    }
    // Function to change the owner (no access control)
    function changeOwner(address _newOwner) public {
        Owner = _newOwner;
    }
}
Enter fullscreen mode Exit fullscreen mode

Next, deploy the contract using remix and grab the deployed contract address.

Make sure Metamask is connected on remix, and if your wallet does not have faucet, you can fund it using this link.

Compile your contract and once conplete, paste the wallet address and deploy the contract, it will take a couple of minutes and once deployed you can copy the contract address which we will hardcode into our Trap.

Next, we are going to set up the trap, populate the HelloWorldTrap.sol file with our edited copy below.

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.12;

import {ITrap} from "drosera-contracts/interfaces/ITrap.sol"; 
import {VulnerableContract} from "./Vulnerable.sol"; 


contract HelloWorldTrap is ITrap {
    // Hardcode the address of the deployed VulnerableContract
    VulnerableContract public vulnerableContract = VulnerableContract(0x4096cdC78dD7b8f74E7228dbD9214bD919dbbA3E);

    function collect() external view returns (bytes memory) {
       // This function only reads state and does not modify it.
        address currentOwner = vulnerableContract.Owner();
        return abi.encode(currentOwner);
    }

    function shouldRespond(bytes[] calldata data) external pure returns (bool, bytes memory) {
    if (data.length == 0) {
        return (false, bytes(""));
    }
    address currentOwner = abi.decode(data[0], (address));
    // Hardcoded expected owner: 0xf0ff7A28fB3545CEC7E3dBeE2F5ce00111c394f0
    if (currentOwner != 0xf0ff7A28fB3545CEC7E3dBeE2F5ce00111c394f0) {
        return (true, abi.encode("Owner has changed"));
    }
    return (false, bytes(""));
}
}
Enter fullscreen mode Exit fullscreen mode

The HelloWorldTrap contract we created tracks ownership changes in VulnerableContract. It achieves this through two key functions: collect() and shouldRespond().

collect() Function – Gathering Blockchain Data

The **collect()** function is responsible for retrieving the current owner of the VulnerableContract. It is marked as view, meaning it can read blockchain data but cannot modify it.

How It Works

  1. The function calls Owner() on the VulnerableContract to get the current owner's address.
  2. It encodes this address into a byte format, which allows it to be stored or processed later.

Why It Matters

  • This function acts as a data collector, continuously fetching the contract’s owner information.
  • The collected data is essential for tracking changes over time.

shouldRespond() Function – Detecting Ownership Changes

The **shouldRespond()** function is responsible for deciding whether a change has occurred in the contract’s ownership. Unlike collect(), this function is marked as pure, meaning it does not read blockchain data or modify any state—it only processes input data.

How It Works

  • It first checks if there is any data available. If there isn’t, it returns **false**, meaning no response is needed.
  • It extracts the most recent owner’s address from the encoded data.
  • It compares this address with a hardcoded expected owner:

    0xf0ff7A28fB3545CEC7E3dBeE2F5ce00111c394f0

If the current owner does not match the expected owner, the function returns:

(true, abi.encode("Owner has changed"))
Enter fullscreen mode Exit fullscreen mode

This signals that an action should be triggered. If the owner remains unchanged, the function returns (false, ""), meaning no further action is needed.

Why It Matters

  • This function validates the collected data without accessing the blockchain, making it highly efficient.
  • It prevents unnecessary responses, ensuring that actions are only triggered when a real change occurs.
  • It plays a crucial role in security monitoring, helping detect potential ownership takeovers.

Finally, the HelloWorldTrap contract works as a watchdog for the VulnerableContract. Here’s how both functions work together.

  1. **collect()** fetches and encodes the contract owner's address.
  2. **shouldRespond()** checks if the owner has changed and, if so, signals that an action is required.

Next, after populating the contract with our code, hardcode the contract address here.

VulnerableContract public vulnerableContract = VulnerableContract(0x4096cdC78dD7b8f74E7228dbD9214bD919dbbA3E);
Enter fullscreen mode Exit fullscreen mode

By hardcoding the contract address, we ensure that HelloWorldTrap interacts with the specific deployed instance of VulnerableContract. If VulnerableContract is redeployed, this address must be updated manually.

Compile

Confirm your contract by compiling, run forge deploy, but if you do not have foundry installed, you can set it up by running

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

Next we have to set up our private key, to do this, copy the private key from your Metamask and add it to your drosera.toml file.

private_key = "0d53..."
Enter fullscreen mode Exit fullscreen mode

Make sure you have enough faucet in your address before continuing.

Deployment

To deploy the trap, run the following commands:

#Compile the Trap
forge build

# if private key is stored in drosera.toml
drosera apply
Enter fullscreen mode Exit fullscreen mode

After successfully deploying the trap, the CLI will add an address field to the drosera.toml file.

We can check out our deployed project using the Explorar link attached.

Click on the Explorar link to see the deployed contract.

Next, copy your address and paste it on trap explorar and with this step your trap is live.

In the next article, I will show you how to set up an operator and how to Opt in and out as an operator and how to get your address whitelisted.

Conclusion

In this guide, we demonstrated how to use Drosera Traps to monitor a vulnerable smart contract for ownership changes. By setting up a simple contract with a security flaw and deploying it, we utilized Drosera to track potential unauthorized changes. This approach highlights the importance of proactive security monitoring in decentralized applications and sets the stage for further exploration into managing operators and securing smart contracts.

Resources

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .