Smart contract and blockchain challenge helpers.
- Use explorers/analyzers to understand contract flows before manual calls
- Look for mis-set ownership, insecure delegatecalls, or unchecked external calls
- Check for integer overflow in Solidity < 0.8.0
- Always verify msg.sender vs tx.origin usage
// VULNERABLE
function withdraw() public {
uint amount = balances[msg.sender];
(bool success, ) = msg.sender.call{value: amount}(""); // External call BEFORE state update
balances[msg.sender] = 0; // State change after external call!
}
// FIXED - Checks-Effects-Interactions pattern
function withdraw() public {
uint amount = balances[msg.sender];
balances[msg.sender] = 0; // State change BEFORE external call
(bool success, ) = msg.sender.call{value: amount}("");
}// Solidity < 0.8.0 - vulnerable
uint8 balance = 255;
balance += 1; // Now 0!
uint8 balance = 0;
balance -= 1; // Now 255!// VULNERABLE - can be exploited via phishing
function transfer(address to, uint amount) public {
require(tx.origin == owner); // Bad!
// ...
}
// FIXED
function transfer(address to, uint amount) public {
require(msg.sender == owner); // Good
// ...
}- Missing
onlyOwnermodifiers - Unprotected
selfdestruct - Improper visibility (public vs private)
- Constructor typos (Solidity < 0.5.0)
// DANGEROUS - context preservation
// Caller's storage is modified, not callee's
(bool success, ) = targetContract.delegatecall(data);# Foundry (recommended)
curl -L https://foundry.paradigm.xyz | bash
foundryup
# Create project
forge init myproject
forge build
forge test -vvv
# Deploy
forge create --rpc-url $RPC_URL --private-key $PRIVATE_KEY src/Contract.sol:Contractnpx hardhat init
npx hardhat compile
npx hardhat test
npx hardhat run scripts/deploy.jsfrom web3 import Web3
# Connect
w3 = Web3(Web3.HTTPProvider('http://localhost:8545'))
# Load contract
contract = w3.eth.contract(address=addr, abi=abi)
# Read
result = contract.functions.someFunction().call()
# Write
tx = contract.functions.someFunction().transact({'from': account})# Call function
cast call $CONTRACT "balanceOf(address)" $ADDR --rpc-url $RPC
# Send transaction
cast send $CONTRACT "transfer(address,uint256)" $TO 1000 --rpc-url $RPC --private-key $KEY
# Decode calldata
cast calldata-decode "transfer(address,uint256)" $DATAconst { ethers } = require("ethers");
const provider = new ethers.JsonRpcProvider("http://localhost:8545");
const contract = new ethers.Contract(address, abi, provider);
// Read
const result = await contract.someFunction();
// Write (needs signer)
const signer = new ethers.Wallet(privateKey, provider);
const tx = await contract.connect(signer).someFunction();- Etherscan - Ethereum mainnet
- Polygonscan - Polygon
- BscScan - Binance Smart Chain
- Arbiscan - Arbitrum
- Dedaub - Contract decompiler
- Etherscan Decompiler
- Remix IDE - Browser IDE
- Slither - Static analyzer
- Mythril - Security analysis
# Anvil (Foundry)
anvil
# Hardhat
npx hardhat node
# Ganache
ganache-cli# Anvil provides funded accounts
# Use first account private key for testing
cast send --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 $TARGET 1ether