Create a Trap for A **Raffle Draw Smart Contract Using Drosera**

In this guide, I will demonstrate how to construct a targeted trap for a Raffle Draw Smart Contract using Drosera. This guide will explore how seemingly harmless logic can be manipulated, and why security in smart contracts must go beyond surface-level checks. Using Drosera, I will set up multiple traps to monitor and intercept key interactions within a Raffle Draw smart contract. My goal is to implement strategic watch that reveals how the contract behaves under specific conditions. If you're interested in smart contract security this guide is for you. Installing Foundry’s Prerequisites To get started: VSCode - A code editor to write your solidity contracts Foundry - For local blockchain emulation and deployment Drosera CLI - Trap deployment If you are an absolute beginner, check out a beginner guide here: Drosera HandBook: The ABC of Traps. Getting Started with Raffle Contract After Installing Vscode, start a new terminal; once the terminal is up, run the following, if you have done this already, you can skip. // Install Foundry curl -L https://foundry.paradigm.xyz | bash Note: Foundry install script sometimes times-out. If you face these issues, try connecting through a VPN service first. With the core tools installed, launch a new terminal and enter: foundryup This updates Foundry to the latest version. Crosscheck to see if the following are installed. anvil --version forge --version cast --version Create Raffle Draw Contract The Raffle draw contract I will be using can be found here; Git clone the repo and navigate into the folder using code . but if it does not work, open the folder and import it. git clone https://github.com/Scofield-Idehen/Raffle-Test.git Run a forge build to install dependencies. Understanding the Raffle Contract To understand the contract, let me break it down. The SPDX license identifier ensures the open-source licensing of our code. We target Solidity version 0.8.18 to use the latest features. The NatSpec comments provide a title, description, author, and usage notice for our Raffle contract. Defining State Variables Next, we define state variables to store essential contract data. The i_ticketPrice will store the ticket price set at deployment. We make it immutable so it can never change. An error Raffle__InsufficientETH() will be thrown when the Ether sent is less than the ticket price. Finally, we use a dynamic array s_participants to store the addresses of all players. Setting the Ticket Price The contract needs to know the ticket price, so we add a constructor that sets it. Anyone deploying the contract must pass ticketPrice, which gets stored in the immutable state variable. i_ticketPrice Adding the Enter Function We now need a way for players to enter the raffle by paying the entrance fee. We first use an if statement to check that the Ether sent is greater than the ticket price. If not, we revert with a custom error that gets caught on the front end. Note we could have used the required require(msg.value >= i_ticketPrice, "Raffle: Insufficient amount");I discovered it would cost more gas using require when we tested it on remix. To understand these two lines of code properly, we must first understand Event Events on the Blockchain Blockchain-based applications generate data that we sometimes want external services to be aware of. For example, our Raffle contract must be able to perform randomness to pick a winner, but as we are aware, blockchain is deterministic. We must rely on a decentralized oracle leveraging Verifiable Random Function, and we would use the Chainlink VRF to connect our smart contracts to verifiable randomness. Defining Events Events are a way for smart contracts to emit messages to interested external listeners. Here is an event definition in our contract: event RafflesEntered(address indexed player); This RafflesEntered event contains one parameter named player of type address. The indexed keyword allows efficient filtering for that address later. Emitting Events We can trigger events from within functions. For example, when a player enters the raffle: The emit keyword triggers the event, passing the player's address captured from the global msg.sender. Listening for Events Using Chainlink VRF Chainlink nodes listen for our event via the Chainlink VRF oracle. When detected, the oracle uses the key to track the request and provide a verifiable random number corresponding to that key. We can access the random number by implementing a fulfillRandomness callback in our contract. Chainlink VRF triggers it automatically once randomness generation is complete. Events are powerful for decoupling smart contract logic from notifications. Defining, emitting, and listening for events unlocks reactive application possibilities on Ethereum. Update the fou

Apr 18, 2025 - 15:34
 0
Create a Trap for A **Raffle Draw Smart Contract Using Drosera**

In this guide, I will demonstrate how to construct a targeted trap for a Raffle Draw Smart Contract using Drosera. This guide will explore how seemingly harmless logic can be manipulated, and why security in smart contracts must go beyond surface-level checks.

Using Drosera, I will set up multiple traps to monitor and intercept key interactions within a Raffle Draw smart contract. My goal is to implement strategic watch that reveals how the contract behaves under specific conditions.

If you're interested in smart contract security this guide is for you.

Installing Foundry’s Prerequisites

To get started:

  • VSCode - A code editor to write your solidity contracts
  • Foundry - For local blockchain emulation and deployment
  • Drosera CLI - Trap deployment

If you are an absolute beginner, check out a beginner guide here: Drosera HandBook: The ABC of Traps.

Getting Started with Raffle Contract

After Installing Vscode, start a new terminal; once the terminal is up, run the following, if you have done this already, you can skip.

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

Note: Foundry install script sometimes times-out. If you face these issues, try connecting through a VPN service first.

With the core tools installed, launch a new terminal and enter: foundryup

This updates Foundry to the latest version. Crosscheck to see if the following are installed.

anvil --version 
forge --version 
cast --version  

Create Raffle Draw Contract

The Raffle draw contract I will be using can be found here; Git clone the repo and navigate into the folder using code . but if it does not work, open the folder and import it.

git clone https://github.com/Scofield-Idehen/Raffle-Test.git

Run a forge build to install dependencies.

Understanding the Raffle Contract

To understand the contract, let me break it down.

The SPDX license identifier ensures the open-source licensing of our code. We target Solidity version 0.8.18 to use the latest features. The NatSpec comments provide a title, description, author, and usage notice for our Raffle contract.

Defining State Variables

Next, we define state variables to store essential contract data.

The i_ticketPrice will store the ticket price set at deployment. We make it immutable so it can never change.

An error Raffle__InsufficientETH() will be thrown when the Ether sent is less than the ticket price. Finally, we use a dynamic array s_participants to store the addresses of all players.

Setting the Ticket Price

The contract needs to know the ticket price, so we add a constructor that sets it.

Anyone deploying the contract must pass ticketPrice, which gets stored in the immutable state variable. i_ticketPrice

Adding the Enter Function

We now need a way for players to enter the raffle by paying the entrance fee.

We first use an if statement to check that the Ether sent is greater than the ticket price. If not, we revert with a custom error that gets caught on the front end.

Note we could have used the required require(msg.value >= i_ticketPrice, "Raffle: Insufficient amount");I discovered it would cost more gas using require when we tested it on remix.

To understand these two lines of code properly, we must first understand Event

Events on the Blockchain

Blockchain-based applications generate data that we sometimes want external services to be aware of. For example, our Raffle contract must be able to perform randomness to pick a winner, but as we are aware, blockchain is deterministic.

We must rely on a decentralized oracle leveraging Verifiable Random Function, and we would use the Chainlink VRF to connect our smart contracts to verifiable randomness.

Defining Events

Events are a way for smart contracts to emit messages to interested external listeners. Here is an event definition in our contract:

event RafflesEntered(address indexed player);

This RafflesEntered event contains one parameter named player of type address. The indexed keyword allows efficient filtering for that address later.

Emitting Events

We can trigger events from within functions. For example, when a player enters the raffle:

The emit keyword triggers the event, passing the player's address captured from the global msg.sender.

Listening for Events Using Chainlink VRF

Chainlink nodes listen for our event via the Chainlink VRF oracle. When detected, the oracle uses the key to track the request and provide a verifiable random number corresponding to that key.

We can access the random number by implementing a fulfillRandomness callback in our contract. Chainlink VRF triggers it automatically once randomness generation is complete.

Events are powerful for decoupling smart contract logic from notifications. Defining, emitting, and listening for events unlocks reactive application possibilities on Ethereum.

Update the foundry.toml file with the correct remapping.

[profile.default]
src = "src"
out = "out"
libs = ["lib", "node_modules"]
remappings = [
  "@chainlink/contracts=lib/chainlink-brownie-contracts/contracts"
]
[rpc_endpoints]
mainnet = "https://eth.llamarpc.com"

Deploying The Raffle Draw Contract

To deploy the contract, I will set up a deployment script in my previous guide i explained how to set up a deployment using Remix, I will be using script for this deployment.

  • Deploying the Raffle Contract Using Foundry Scripts

Open the DeployedRaffleTicket.s.sol and update the code base with the script below. Deployment scripts are efficient way to deploy smart contract. Foundry, a powerful framework for Ethereum development automates the process of broadcasting your transaction.

First, I imported Foundry’s scripting utilities (forge-std/Script.sol) and the actual contract (raffle.sol).

Inside the run() function, I retrieve the environment variables using vm.envUint, including the my private key (PRIVATE_KEY), the ticket price, and the interval duration. These are fetched securely from a .env file to avoid hardcoding sensitive or configurable data.

Set up your .env file like this.

PRIVATE_KEY="0x61..."
TICKET_PRICE=10000000000000000   # 0.01 ether in wei
INTERVAL=300                     # 300 seconds (5 minutes)
RPC_URL="https://eth-holesky.g.alchemy.com/v2/7jl_hPvjjEmayZTegWpMuRt-tkVDjV7t"
CHAIN_ID=17000

The script then uses vm.startBroadcast(privateKey) to begin broadcasting transactions from the specified private key. This is followed by deploying the raffleTicket contract with the provided ticket price and interval values. After deployment, it logs the address of the deployed contract, which is useful for verifying and interacting with it later.

Finally, vm.stopBroadcast() ends the broadcasting session, signaling the end of deployment.

Update the Foundry.toml file

Next, I will update the foundry file with the Alchemy RPC,

[profile.default]
src = "src"
out = "out"
libs = ["lib", "node_modules"]
remappings = [
  "@chainlink/contracts=lib/chainlink-brownie-contracts/contracts"
]
[rpc_endpoints]
mainnet = "https://eth.llamarpc.com"
holesky="https://eth-holesky.g.alchemy.com/v2/7jl_hPvjjEmayZTegWpMuRt-tkVDjV7t"

Run a forge compile and make sure it compiles correctly before running.

forge script scripts/DeployRaffleTicket.s.sol:DeployRaffleTicket \
  --rpc-url $RPC_URL \
  --broadcast \
  --chain-id $CHAIN_ID 

This would deploy your contract.

Once deployed, copy the contract address and check the deployed contract on etherscan.

Setting a Trap

To set up a trap, you must have the drosera CLI installed, if you have it, run a droseraup to install the latest version.

Create a trap.sol in the src folder and update it with the following.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {ITrap} from "drosera-contracts/interfaces/ITrap.sol";
import {raffleTicket} from "./raffle.sol";
contract RaffleStuckTrap is ITrap {
    raffleTicket public target = raffleTicket(0x307C59D0dB3124Cd1C9f935285226D5622289BCD);
    function collect() external view override returns (bytes memory) {
        address[] memory players = target.getParticipants();
        uint playerCount = players.length;
        uint8 currentState = uint8(target.getRaffleState());
        uint balance = address(target).balance;
        address winner = target.getWinner();
        return abi.encode(players, playerCount, currentState, balance, winner);
    }
    function shouldRespond(bytes[] calldata data) external pure override returns (bool, bytes memory) {
        (
            ,
            uint playerCount,
            uint8 currentState,
            uint balance,
            address winner
        ) = abi.decode(data[0], (address[], uint, uint8, uint, address));
        bool stuckInCalculating = currentState == 1 && playerCount > 0 && balance > 0 && winner == address(0);
        return (stuckInCalculating, bytes(""));
    }
}

The RaffleStuckTrap contract is designed to monitor the state of a raffle system and ensure it doesn’t get stuck in an incomplete state, particularly when the contract is attempting to select a winner but fails to do so.

Let’s go through the key components of the code.

  • Contract Declaration and Target Setup

    contract RaffleStuckTrap is ITrap {
    raffleTicket public target = raffleTicket(0x307C59D0dB3124Cd1C9f935285226D5622289BCD);

  • The target variable holds the address of the raffle contract (an instance of the raffleTicket contract). This is the contract I will be monitoring for potential issues.

  • The raffleTicket contract is already deployed at (0x307C59D0dB3124Cd1C9f935285226D5622289BCD), and RaffleStuckTrap will interact with it to track its state.

  • Collect Function

    function collect() external view override returns (bytes memory) {
    address[] memory players = target.getParticipants();
    uint playerCount = players.length;
    uint8 currentState = uint8(target.getRaffleState());
    uint balance = address(target).balance;
    address winner = target.getWinner();
    return abi.encode(players, playerCount, currentState, balance, winner);
    }

  • Purpose: This function collects critical state data from the raffle contract and encodes it for use.

  • It calls various view functions from the raffleTicket contract:

    • getParticipants() to retrieve the list of participants in the raffle.
    • getRaffleState() to check the current state of the raffle (whether it's open or in the process of selecting a winner).
    • address(target).balance to get the current balance of the raffle contract (which should match the total prize pool).
    • getWinner() to check the address of the winner (if any).
  • The collected data (players, count, state, balance, and winner) is then encoded into a byte array using abi.encode(), which can be used by other functions or external systems.

  • shouldRespond Function


function shouldRespond(bytes[] calldata data) external pure override returns (bool, bytes memory) {
        (
            ,
            uint playerCount,
            uint8 currentState,
            uint balance,
            address winner
        ) = abi.decode(data[0], (address[], uint, uint8, uint, address));
        bool stuckInCalculating = currentState == 1 && playerCount > 0 && balance > 0 && winner == address(0);
        return (stuckInCalculating, bytes(""));
    }
  • Purpose: This function checks the collected data to determine if the raffle is stuck in the "calculating" state.

  • The function decodes the data passed to it (which is the byte array returned from the collect() function) to retrieve the necessary state variables.

  • It checks if:

    • The raffle is in the "CALCULATING" state (currentState == 1).
    • There are participants in the raffle (playerCount > 0).
    • The contract has a balance greater than zero (balance > 0).
    • The winner has not been selected (winner == address(0)).
  • If all these conditions are met, it indicates that the raffle is stuck in the calculation process without selecting a winner, and the function returns true. This triggers a response, signaling that the raffle needs attention.

  • If the raffle is not stuck, the function returns false, indicating no action is required.

The RaffleStuckTrap contract acts as a safeguard against the raffle contract becoming "stuck" in an incomplete state. In the context of decentralized applications, this trap ensures that the raffle system doesn't remain in the "calculating" state without concluding. It helps to ensure that if an issue arises where the winner is not selected despite the raffle having participants and funds, the system can detect and respond to the problem.

Deploy Trap

Run drosera apply with the --private-key command to deploy your trap, make sure you have Holesky faucet before running.

Get faucet from Drosera.

If you use a public node you can get rate-limited so use a private node. Use Alchemy node.

Click on the explorar link to find the deployed trap.

Hydrate A Trap

Finally, I will hydrate the trap. To do this make sure your address has $DRO, if you don’t have, ask in the community.

Next, grab the trap address from the deployed trap.

Run

drosera hydrate --trap-address 0x1cC58c1fd599E3E461695Ec59Db2193A6813c7AE --dro-amount 4 --private-key 616...

Here i am hydrating this trap with $4 DRO.

Our trap has been hydrated. Check it on Drosera trap explorer.

Conclusion

In this guide, I demonstrated how to create and deploy a targeted trap for a Raffle Draw Smart Contract using Drosera. The approach highlights how real-time monitoring can uncover subtle issues that traditional testing might miss.

By observing contract behavior during execution, I was able to pinpoint conditions like a stuck raffleState, proving the value of dynamic analysis in smart contract security.

This method adds an important layer to the testing process, especially for contracts with complex state transitions or randomness.

Resource