Debug Playbook
1. Revert Decoding
Debugging smart contracts on the Somnia Network requires an understanding of how and why transactions fail. Every reverted transaction carries encoded data that can reveal the root cause of failure, whether it’s due to logic errors, insufficient gas, failed access checks, or internal Solidity panics.
1.1 Anatomy of a Revert
When a transaction fails on Somnia, the EVM halts execution and returns revert data, an ABI-encoded payload that follows one of these three formats:
Error(string)
0x08c379a0
Standard revert reason with message
require(balance > 0, "Zero balance");
Panic(uint256)
0x4e487b71
Internal error (e.g., overflow, div/0, invalid enum)
assert(x > 0);
Custom Errors
Function selector of custom error
error Unauthorized(address caller);
On Somnia, these behave identically to Ethereum but may differ in gas costs and stack trace length depending on the validator node configuration.
1.2 Catching and Displaying Reverts in Hardhat
When running tests or scripts on Somnia, wrap calls in try/catch to capture the revert reason.
try {
  await treasury.withdraw(1000);
} catch (error: any) {
  console.log('Revert reason:', error.reason || error.message);
  console.log('Full error data:', error.data || error.error?.data);
}In Chai matchers:
await expect(treasury.connect(user).withdraw(1000))
  .to.be.revertedWith('Insufficient funds');If your contract uses custom errors, Hardhat will not automatically print the name. Decode it manually:
const iface = new ethers.utils.Interface(contractABI);
try {
  await treasury.connect(attacker).withdraw(9999);
} catch (error: any) {
  const data = error.data || error.error?.data;
  if (data) {
    const decoded = iface.parseError(data);
    console.log(`Custom Error: ${decoded.name}`);
    console.log('Arguments:', decoded.args);
  }
}1.3 Decoding Panic Codes
Internal Solidity panics correspond to low-level EVM exceptions. Somnia propagates these codes like any other EVM chain.
0x01
Assertion failed
Logic invariant broken
0x11
Arithmetic overflow/underflow
Unchecked math operation
0x12
Division by zero
Incorrect math division
0x21
Invalid enum conversion
Out-of-bounds value
0x31
Storage array index out of bounds
Bad loop or mapping access
0x32
Memory array index out of bounds
Corrupt array operation
To detect Panic errors dynamically:
if (error.data?.startsWith('0x4e487b71')) {
  const code = parseInt(error.data.slice(10), 16);
  console.log('Panic Code:', `0x${code.toString(16)}`);
}1.4 Advanced Revert Inspection with Hardhat Traces
Hardhat’s tracing layer can reveal the full execution path of a revert.
npx hardhat test --traceYou’ll see nested calls, gas usage per function, and exactly where the failure occurred. This is invaluable for multi-contract interactions like on-chain governance or liquidity management.
Example output:
CALL treasury.withdraw
 └─ CALL token.transfer -> reverted with reason: 'Insufficient balance'1.5 Custom Error Decoding for Verified Contracts
If a Somnia contract is verified on the explorer, you can fetch its ABI dynamically to decode errors programmatically:
import axios from 'axios';
const abiURL = `https://explorer.somnia.network/api?module=contract&action=getabi&address=${address}`;
const { data } = await axios.get(abiURL);
const iface = new ethers.utils.Interface(JSON.parse(data.result));Then use iface.parseError(error.data) to decode reverts directly from on-chain logs or transactions.
2. Common Error Patterns on Somnia
Even experienced developers encounter recurring issues. Below are the most common EVM-level errors observed when deploying or testing on Somnia Testnet (Shannon) and Mainnet.
execution reverted
Fallback revert with no message
Add explicit revert messages or decode ABI data
out of gas
Gas exhausted mid-call
Use estimateGas() or increase gas limit
invalid opcode
Calling a non-existent function
Validate ABI and deployed bytecode
nonce too low
Pending transaction not mined yet
Wait for confirmation or reset nonce
replacement underpriced
Gas bump too small
Raise gas price by 10–20%
static call violation
State-changing call via eth_call
Use .sendTransaction() instead
2.1 Example: Catching a Custom Error in Somnia Treasury Contract
error Unauthorized(address caller);
function mint(address to, uint amount) external {
  if (msg.sender != owner) revert Unauthorized(msg.sender);
  _mint(to, amount);
}Decoding in JS:
try {
  await treasury.connect(randomUser).mint(addr, 100);
} catch (e: any) {
  const iface = new ethers.utils.Interface(['error Unauthorized(address caller)']);
  const decoded = iface.parseError(e.data);
  console.log('Unauthorized address:', decoded.args[0]);
}2.2 Handling Complex Contract Interactions
When interacting with multi-layered DeFi protocols or bridging modules on Somnia, reverts can originate several calls deep. Use Hardhat’s trace or Foundry’s -vvvv verbosity to see the full stack.
Foundry example:
forge test -vvvvThis reveals each opcode execution, event emission, and revert reason.
2.3 Invalid ABI or Proxy Conflicts
Many Somnia projects use upgradeable proxies. Reverts from a proxy may originate in the implementation contract. If you get a generic execution reverted, verify you’re using the correct implementation ABI:
const implAddr = await provider.getStorageAt(proxyAddress, '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc');
const iface = new ethers.utils.Interface(implementationABI);3. Transaction Simulation
Simulating transactions allows developers to predict revert causes, estimate gas usage, and test behaviors without risking real SOMI or STT.
3.1 Fork Somnia Networks Locally
Create a local fork of Somnia Mainnet or Shannon Testnet:
npx hardhat node --fork https://api.infra.mainnet.somnia.networkOr in configuration:
networks: {
  hardhat: {
    forking: {
      url: process.env.SOMNIA_RPC_TESTNET,
      blockNumber: 123456, //example
    }
  }
}This mirrors on-chain state locally, so you can safely replay any transaction.
3.2 Using callStatic for Dry-Run Simulation
callStatic runs a transaction without broadcasting or altering state.
try {
  const result = await treasury.callStatic.withdraw(1000);
  console.log('Call successful:', result);
} catch (error: any) {
  console.log('Simulation failed with reason:', error.reason);
}3.3 Using eth_call Manually
For raw RPC simulation:
const tx = {
  to: contract.address,
  data: contract.interface.encodeFunctionData('stake', [amount])
};
const result = await provider.call(tx);
console.log('Returned data:', result);If the call reverts, inspect error.data to decode it with the ABI.
3.4 Impersonating Accounts for Privileged Actions
Simulate admin or contract-controlled operations:
await network.provider.request({
  method: 'hardhat_impersonateAccount',
  params: ['0xAdminAddress']
});
const admin = await ethers.getSigner('0xAdminAddress');
await treasury.connect(admin).setFee(5);Stop impersonation when finished:
await network.provider.request({
  method: 'hardhat_stopImpersonatingAccount',
  params: ['0xAdminAddress']
});3.5 Snapshot and Rollback Control
Snapshots let you test different outcomes quickly:
const snapshot = await network.provider.send('evm_snapshot', []);
await treasury.mint(100);
await network.provider.send('evm_revert', [snapshot]);This resets the blockchain to its previous state instantly.
3.6 Simulating On-Chain Transactions
If you have a failed transaction hash from Somnia Mainnet:
const tx = await provider.getTransaction('0x123...');
await provider.call({ to: tx.to!, data: tx.data });This reproduces the failure locally and lets you inspect the revert reason directly in Hardhat.
3.7 Advanced Fork Testing with Foundry
anvil --fork-url https://dream-rpc.somnia.network --fork-block-number 3456789
forge test -vvvvYou can use cheatcodes like:
vm.startPrank(admin);
contract.withdraw(1000);
vm.stopPrank();3.8 Gas Profiling and Cost Analysis
Somnia gas costs can differ from Ethereum due to consensus differences. Always estimate gas usage per function:
const gas = await contract.estimateGas.executeTrade(orderId);
console.log('Estimated gas:', gas.toString());Compare against Shannon and Mainnet to identify anomalies.
3.9 Full Transaction Lifecycle Test
Last updated
