Risk: Attacker triggers expensive SSTORE operations to drain gas budgets.
Attack pattern:
// If contract allows arbitrary key writes:
function store(uint256 key, uint256 value) external {
data[key] = value; // 2M+ gas per new slot
}
// Attacker calls with 1000 unique keys = 2B gasPrevention:
- Validate keys against whitelist
- Limit operations per transaction
- Charge users for storage costs
Risk: Contract logic depends on block.timestamp precision.
Attack pattern:
// Auction ends at timestamp
if (block.timestamp >= auctionEnd) {
// MegaETH block.timestamp has 1s granularity
// But mini-blocks happen every 10ms
// Attacker can exploit timing window
}Prevention:
- Use high-precision oracle for time-sensitive logic
- Add grace periods for timing-dependent operations
- Consider mini-block timing in design
MegaETH has fast finality (<10ms) but reorgs are theoretically possible until L1 finalization.
Prevention:
- For high-value operations, wait for confirmation count
- Use
eth_sendRawTransactionSyncreceipt as soft-finality - Consider L1 finalization for irreversible actions
These apply equally to MegaETH:
// ❌ Vulnerable
function withdraw() external {
uint256 amount = balances[msg.sender];
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
balances[msg.sender] = 0;
}
// ✅ Checks-Effects-Interactions
function withdraw() external {
uint256 amount = balances[msg.sender];
balances[msg.sender] = 0; // Effect before interaction
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
}// ✅ Always validate
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
modifier onlyValidSigner(bytes memory signature) {
require(verifySignature(signature), "Invalid signature");
_;
}Solidity 0.8+ has built-in overflow checks. For older code:
// Use OpenZeppelin SafeMath or upgrade to 0.8+// ❌ Don't trust random RPCs
const client = createClient({ rpc: userProvidedUrl });
// ✅ Use known endpoints, verify responses
const TRUSTED_RPCS = [
'https://mainnet.megaeth.com/rpc',
'https://rpc.alchemy.com/megaeth'
];// Always simulate before signing
const simulation = await client.simulateTransaction(tx);
if (simulation.error) {
throw new Error(`Simulation failed: ${simulation.error}`);
}// MegaETH blocks are fast - blockhash expires quickly
// Retry with fresh blockhash on failure
async function submitWithRetry(tx: Transaction, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const blockhash = await client.getLatestBlockhash();
tx.blockhash = blockhash;
return await client.sendTransaction(tx);
} catch (e) {
if (!e.message.includes('blockhash')) throw e;
}
}
throw new Error('Transaction expired');
}- Use mega-evme for transaction replay and gas profiling
- Test with realistic gas costs — fork testnet, don't simulate locally
- Review SSTORE patterns — each new slot is expensive
- Check volatile data usage — 20M total compute gas cap (retroactive) when block metadata is accessed
- Verify CPI targets — don't allow arbitrary external calls
- Can an attacker trigger expensive storage operations?
- Does the contract rely on block.timestamp precision?
- Are there timing windows between mini-blocks that can be exploited?
- Is the contract vulnerable to reorgs before L1 finalization?
- Are external calls validated and limited?
- Is gas estimation done remotely (not locally)?
// Set up transaction monitoring
const monitor = new TransactionMonitor({
rpc: 'https://mainnet.megaeth.com/rpc',
contracts: ['0x...'],
onSuspicious: (tx) => {
// Alert on unusual patterns
// - High gas usage
// - Repeated failures
// - Unusual call patterns
}
});Always validate user inputs with strict allowlists, never blocklists:
// ❌ Blocklist — misses null bytes, emoji, control chars, <script>, etc.
function validate(string memory input) internal pure {
// Only blocks dots
require(!containsDot(input), "No dots");
// Everything else passes — including malicious inputs
}
// ✅ Allowlist — only permits known-safe characters
function validate(string memory label) internal pure returns (string memory) {
bytes memory b = bytes(label);
require(b.length > 0 && b.length <= 255, "Invalid length");
require(b[0] != 0x2d && b[b.length - 1] != 0x2d, "No leading/trailing hyphens");
for (uint256 i; i < b.length; i++) {
bytes1 c = b[i];
require(
(c >= 0x61 && c <= 0x7a) || // a-z
(c >= 0x30 && c <= 0x39) || // 0-9
c == 0x2d, // hyphen
"Invalid character"
);
}
return label;
}Lesson: A blocklist approach once let null bytes, spaces, <script> tags, emoji, and control characters through validation. 43 functional tests all passed but missed these cases. Always test inputs adversarially.
- MegaEVM Spec: https://github.com/megaeth-labs/mega-evm/blob/main/specs/MiniRex.md
- Security auditors: Cantina, Spearbit (recommended by MegaETH team)