A comprehensive guide to diagnosing and resolving common issues in MARK Protocol development, testing, and deployment.
- Development Setup Issues
- Smart Contract Issues
- Frontend Development Issues
- Testing Issues
- Deployment Issues
- CI/CD Workflow Issues
- Network & RPC Issues
Symptoms:
bash: pnpm: command not found
Causes:
- pnpm not installed
- corepack not enabled
- PATH not updated
Solutions:
-
Enable corepack (recommended):
# Enable corepack globally corepack enable # Prepare pnpm version from package.json corepack prepare # Verify pnpm --version # Should show 9.0.2+
-
Install pnpm via mise (project standard):
# mise manages pnpm version per .mise.toml mise install pnpm # Verify which pnpm pnpm --version # Should show 9.0.2
-
Update PATH:
# Add to ~/.bashrc, ~/.zshrc, or ~/.profile export PATH="$HOME/.npm/_npx:$PATH" # Reload shell source ~/.bashrc # or source ~/.zshrc
Symptoms:
⚠️ This project requires Node.js v24
You are using v18.x.x
Causes:
- Node.js version doesn't match
.mise.toml - mise or another Node version manager is not installed
Solutions:
-
Use mise (recommended):
# Install mise if not present brew install mise # Trust and install tools from .mise.toml mise trust mise install # Verify node --version # Should show v24.x.x
-
Use another version manager:
# Example with fnm brew install fnm fnm install 24 fnm use 24 -
Manual installation:
- Download from https://nodejs.org
- Install Node.js v24
Symptoms:
forge: command not found
Solutions:
# Install Foundry (macOS/Linux/WSL)
curl -L https://foundry.paradigm.xyz | bash
# Update PATH
source $HOME/.bashrc # or ~/.zshrc
# Install Foundry components
foundryup
# Verify
forge --version
cast --versionWindows users:
# Use WSL2 or install from https://github.com/foundry-rs/foundry/releasesSymptoms:
sh: /usr/local/bin/circom: Bad CPU type in executable
/usr/local/bin/circom: line 1: Not: command not found
Causes:
- A stale or wrong-architecture
circombinary is earlier in PATH - A failed download wrote an error page to
/usr/local/bin/circom - The local shell and downloaded binary architectures do not match
Solutions:
-
Inspect active candidates:
type -a circom file "$(command -v circom)" circom --version
-
Install a known-good local binary with Cargo:
cargo install --git https://github.com/iden3/circom.git --tag v2.2.3 --locked
-
Remove or replace the broken system binary:
mv /usr/local/bin/circom /usr/local/bin/circom.bak.$(date +%Y%m%d%H%M%S) ln -s "$HOME/.cargo/bin/circom" /usr/local/bin/circom
-
Verify circuit tests:
command -v circom circom --version pnpm -s circuits:test
Symptoms:
sup: command not found
Solutions:
# Install super-cli via pnpm (dev tooling)
pnpm add -g @eth-optimism/super-cli
# Or use the bootstrap script which installs all tools:
./scripts/bootstrap.sh
# Verify
sup --versionSymptoms:
- Linting not running before commit
- Tests not running before push
Causes:
- Git hooks not installed
.git/hooksdirectory corrupted
Solutions:
# Reinstall dependencies (usually sets up hooks)
pnpm i
# If still not working, manually set up hooks
# (Hook setup depends on your husky/lint-staged config)
# Force rebuild
rm -rf node_modules pnpm-lock.yaml
pnpm iSymptoms:
CI Error: architecture-guard
ERROR: Forbidden import found in src/bridge/MARKBridgeAdapter.sol
Importing from: src/settlement/ISettlement.sol
Settlement should not be imported by bridge domain
Causes:
- Bridge contract imported settlement module
- Settlement contract imported bridge module
- Violates strict domain boundaries
Solutions:
-
Check the forbidden import:
grep -n "import.*settlement\|import.*bridge" src/bridge/MARKBridgeAdapter.sol grep -n "import.*settlement\|import.*bridge" src/settlement/MARKSettlementModule.sol
-
Remove the forbidden import:
// ❌ DON'T DO THIS (bridge importing settlement) import { ISettlement } from "../settlement/ISettlement.sol"; // ✅ DO THIS INSTEAD (use shared interface) import { ISharedSettlement } from "../interfaces/ISharedSettlement.sol";
-
Create shared interface if needed:
// src/interfaces/ISharedSettlement.sol pragma solidity ^0.8.0; interface ISharedSettlement { function execute(bytes32 id, bytes calldata proof) external; }
-
Run guard to verify:
cd contracts && make architecture-guard # Should pass: ✅ Architecture guard passed
Symptoms:
CI Error: layering-guard
ERROR: Test file importing script in src/script/Deploy.sol
Tests should mock or import only from src/
Causes:
- Test file imports from deployment scripts
- Script file imports from tests
- Violates layering boundaries
Solutions:
-
Identify the problematic import:
grep -n "import.*script" contracts/test/**/*.sol grep -n "import.*test" contracts/script/**/*.sol
-
Refactor to remove cross-layer dependency:
// ❌ DON'T DO THIS (test importing script) import { Deploy } from "../../script/Deploy.s.sol"; // ✅ DO THIS (test imports contract directly) import { RYLA } from "../../src/token/RYLA.sol";
-
Extract shared code to
src/if needed:# Move common utilities to src/ mv contracts/script/Helpers.sol contracts/src/lib/Helpers.sol # Update imports in both test and script files
-
Run guard to verify:
cd contracts && make layering-guard # Should pass: ✅ Layering guard passed
Symptoms:
Slither output:
HIGH: Arbitrary Send ERC20 in MARKBridgeAdapter.transfer()
Risk: Caller can transfer arbitrary tokens
Causes:
- Actual security issue (fix it!)
- False positive (document exclusion)
Solutions:
-
If legitimate issue (do this first):
// ❌ Before: Vulnerable function bridgeTransfer(address token, uint amount) external { IERC20(token).transfer(recipient, amount); // Arbitrary token! } // ✅ After: Safe function bridgeTransfer(uint amount) external { require(msg.sender == owner); // Only owner can bridge RYLA.transfer(recipient, amount); // Only RYLA token }
-
If false positive, document exclusion:
# contracts/Makefile slither-core: slither "$$target" \ --exclude-dependencies \ --exclude "naming-convention,arbitrary-send-erc20" \ # ← Add here
Document why in your code:
/// @notice Transfer is safe because: /// - Only whitelisted tokens allowed (see tokenWhitelist) /// - Transfer is guarded by onlyOwner modifier /// - Slither exclusion: arbitrary-send-erc20 (documented safe) function transfer(address token, uint amount) external onlyOwner { IERC20(token).transfer(recipient, amount); }
-
Update Slither exclusions in Makefile:
cd contracts && make slither-core # Review findings # Update Makefile if excluding make slither-core # Re-verify
Symptoms:
Compiler error: contracts/src/token/RYLA.sol:5:1: DeclarationError
Import "openzeppelin-contracts/token/ERC20/ERC20.sol" not found
Causes:
- Missing dependencies
- Remapping misconfigured
- Submodule not initialized
Solutions:
-
Initialize submodules:
git submodule init git submodule update --recursive
-
Check remappings:
cat contracts/foundry.toml | grep -A5 "remappings" # Should show: # @openzeppelin/ => lib/createx/lib/openzeppelin-contracts/ # @interop-lib/ => lib/interop-lib/src/
-
Verify dependencies exist:
ls -la contracts/lib/ # Should show: forge-std, interop-lib, createx -
Reinstall dependencies:
cd contracts rm -rf lib cache forge install -
Try building again:
cd contracts && forge build
Symptoms:
forge test execution reverted: OutOfGas
Function: testSettlementWithLargeProof
Causes:
- Test uses too much gas
- Fuzzing with many iterations
- Complex contract interaction
Solutions:
-
Reduce fuzzing runs (in test file):
// Add at top of test file: /// forge-config: default.fuzz.runs = 10 (instead of default 256) function testFuzzSettlement(bytes32 id) public { // Test logic }
-
Optimize test setup:
// ❌ Before: Redundant setup function testManySettlements() public { for (uint i = 0; i < 1000; i++) { settlement.execute(id, proof); // 1000 executions = high gas } } // ✅ After: Test key scenario function testSettlement() public { settlement.execute(id, proof); } function testMultipleSettlements() public { settlement.execute(id1, proof1); settlement.execute(id2, proof2); // Test only 2-3 scenarios, not 1000 }
-
Check gas limits in Makefile:
grep -n "gas\|FOUNDRY" contracts/Makefile -
Run with gas reporting:
cd contracts && forge test --gas-report # See which functions consume most gas
Symptoms:
Error: listen EADDRINUSE: address already in use :::5173
Causes:
- Another process using port 5173
- Previous dev server still running
- Port configured differently
Solutions:
-
Kill process on port 5173:
# macOS/Linux lsof -i :5173 # Find process kill -9 <PID> # Kill it # Or use direct command pkill -f "vite\|dev:frontend"
-
Use different port:
pnpm dev:frontend -- --port 5174
-
Restart dev server:
# Stop all processes pkill -f "vite\|anvil\|supersim" # Start fresh pnpm dev
Symptoms:
- VS Code shows red squiggles
pnpm typecheckpasses locally- CI passes but IDE unhappy
Causes:
- IDE cache stale
- TypeScript version mismatch
- tsconfig.json not reloaded
Solutions:
-
Reload TypeScript server in VS Code:
- Press:
Cmd/Ctrl + Shift + P - Type: "TypeScript: Restart TS Server"
- Press Enter
- Press:
-
Clear IDE cache:
# Close VS Code rm -rf .vscode/ rm -rf node_modules/.vite pnpm i # Reopen VS Code
-
Verify TypeScript version:
pnpm ls typescript # Should show 5.7.2+ -
Force typecheck:
pnpm typecheck --strict
Symptoms:
Browser console: Uncaught ReferenceError: MARK is not defined
or
Failed to resolve module: @eth-optimism/viem
Causes:
- Dependency not installed
- Import path wrong
- Build cache stale
Solutions:
-
Reinstall dependencies:
rm -rf node_modules pnpm-lock.yaml pnpm i
-
Check import path:
// ❌ Wrong import { useAccount } from "wagmi"; // Missing path // ✅ Correct import { useAccount } from "wagmi"; import { supersimL2A } from "@eth-optimism/viem/chains";
-
Clear browser cache:
# Hard refresh in browser Cmd/Ctrl + Shift + R (or Cmd/Ctrl + Shift + Delete, then clear cache)
-
Restart dev server:
pkill -f "vite" pnpm dev:frontend
Symptoms:
forge test
# ... waiting forever ...
Test suite hangs without completing
Causes:
- Infinite loop in test
- Test stuck waiting for transaction
- Network call timeout
- Foundry offline mode issue
Solutions:
-
Kill hanging process:
# In another terminal pkill -f forge -
Run single test to isolate:
cd contracts forge test --match-test "testSpecificFunction" -vv
-
Check for infinite loops in problematic test:
// ❌ Bad: Infinite loop function testLoop() public { while(true) { // INFINITE! settlement.execute(...); } } // ✅ Good: Bounded loop function testLoop() public { for (uint i = 0; i < 10; i++) { // 10 iterations max settlement.execute(...); } }
-
Try offline mode:
cd contracts FOUNDRY_OFFLINE=true forge test
-
Increase timeout:
timeout 300 forge test # 5 minute timeout
Symptoms:
forge test error: Assertion failed
Message: Insufficient balance
Causes:
- Test didn't fund account properly
- Token transfer didn't work
- Balance calculation wrong
Solutions:
-
Add explicit balance setup:
function setUp() public { // Mint tokens to test accounts vm.prank(owner); token.mint(user, 1000e18); // 1000 tokens // Verify balance assertEq(token.balanceOf(user), 1000e18); }
-
Use deal() (simpler):
// Set ETH balance vm.deal(user, 10 ether); assertEq(user.balance, 10 ether); // Set ERC20 balance (requires contract) deal(address(token), user, 1000e18); assertEq(token.balanceOf(user), 1000e18);
-
Debug balance in test:
function testDebugBalance() public { emit log_uint(token.balanceOf(user)); // Print to console assertEq(token.balanceOf(user), expectedAmount); } // Run with: forge test -vv
Symptoms:
Error: Insufficient balance. Required: 0.5 ETH, Have: 0.01 ETH
Solutions:
# Check balance
cast balance $DEPLOYER_ADDRESS --rpc-url $RPC
# Fund account (from another address)
cast send $DEPLOYER_ADDRESS --value 1ether \
--rpc-url $RPC \
--private-key $FUNDING_KEY
# Verify funded
cast balance $DEPLOYER_ADDRESS --rpc-url $RPCSymptoms:
Error: Transaction not mined after 300 seconds
TX Hash: 0x1234...
Causes:
- Network congestion
- Gas price too low
- RPC node issues
Solutions:
-
Check transaction status:
cast receipt 0x1234... --rpc-url $RPC # If output is empty, transaction dropped
-
Increase gas price:
# Check current gas cast gas-price --rpc-url $RPC # Redeploy with higher gas GASPRICE=50gwei pnpm sup deploy create2 ...
-
Verify RPC is healthy:
# Test RPC curl -X POST $RPC \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
-
Wait longer:
# Testnet congestion? Wait 5-10 minutes and retry sleep 300 pnpm sup deploy ...
Symptoms:
- Workflow shows "In progress" for >1 hour
- No error message
Solutions:
-
Cancel workflow:
- Go to: https://github.com/trade/mark/actions
- Find workflow
- Click: "Cancel workflow"
-
Check logs:
- Click workflow name
- View "Logs" section
- Look for last message before hang
-
Restart workflow:
- Go to workflow run
- Click: "Re-run failed jobs"
-
Check GitHub status:
- https://www.githubstatus.com/
- If GitHub is down, wait for recovery
Symptoms:
Error: Environment variable DEPLOYER_KEY not set
Causes:
- Secret not added to repository
- Secret name misspelled
- Secret scoped to wrong environment
Solutions:
-
Add secret to repository:
- Go to: Settings → Secrets and variables → Actions
- Click: "New repository secret"
- Name:
DEPLOYER_KEY - Value: (paste actual private key)
- Click: "Add secret"
-
Verify secret name in workflow:
# In .github/workflows/deploy.yml - name: Deploy env: DEPLOYER_KEY: ${{ secrets.DEPLOYER_KEY }} # Must match exact name run: pnpm sup deploy create2
-
For environment-specific secrets:
environment: production # Or staging # Secrets from production environment used
-
Re-run workflow after adding secret:
- Go to workflow run
- Click: "Re-run failed jobs"
Symptoms:
GitHub Actions: Slither Core Contracts → FAILED
Local: cd contracts && make slither-core → PASSED
Causes:
- Slither version different
- Environment differences
- Cache issues
Solutions:
-
Update Slither locally:
pip install --upgrade slither-analyzer slither --version
-
Run Slither like CI does:
cd contracts && make slither-core # Should match GitHub output
-
Check Slither remappings:
# Verify remappings in workflow slither src/token/RYLA.sol \ --solc-remaps "@interop-lib/=lib/interop-lib/src/ @openzeppelin/=lib/createx/lib/openzeppelin-contracts/"
Symptoms:
Error: connect ECONNREFUSED 127.0.0.1:9545
or
Error: Gateway timeout
Causes:
- Local network (anvil/supersim) not running
- RPC URL wrong
- Network congestion
Solutions:
-
For local network:
# Check if supersim is running lsof -i :9545 # Should show anvil process # If not, start it pnpm dev:supersim
-
For public RPC:
# Verify RPC URL echo $MAINNET_RPC # Example: https://mainnet.infura.io/v3/YOUR_API_KEY # Test RPC curl -X POST $MAINNET_RPC \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
-
Retry with timeout:
# Use cast with timeout cast call 0x123... "balanceOf(address)" $ACCOUNT \ --rpc-url $RPC \ --timeout 30s # 30 second timeout
Symptoms:
Error: Contract address is 0x0000000000000000000000000000000000000000
or
Error: Contract not found at 0x1234...
Causes:
- Deployment failed silently
- Contract address misconfigured
- Wrong network selected
Solutions:
-
Check deployment status:
# Look at broadcast files ls -la contracts/broadcast/ cat contracts/broadcast/Deploy.s.sol/*/run-latest.json | jq '.transactions'
-
Verify contract deployed:
# Check if code exists at address cast code 0x1234... --rpc-url $RPC # Should show bytecode (not 0x)
-
Check network:
# Verify you're on correct network cast chain-id --rpc-url $RPC # Mainnet: 1 # OP Sepolia: 11155420 # Sepolia: 11155111
-
Redeploy if needed:
# Get fresh deployment cd contracts && make ci-full # Verify locally first pnpm sup deploy create2 ... # Deploy
- Check existing issues: https://github.com/trade/mark/issues
- Review pull requests: https://github.com/trade/mark/pulls
- Read documentation:
CONTRIBUTING.md,DEPLOYMENT.md,BRANCHING.md - Ask in GitHub Discussions (if enabled)
Version: 1.0
Last Updated: 2026-06-02