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
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;
}
}
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(""));
}
}
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
- The function calls
Owner()
on theVulnerableContract
to get the current owner's address. - 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"))
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.
-
**collect()**
fetches and encodes the contract owner's address. -
**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);
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..."
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
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.