Skip to main content

Forge Contract Testing

Live

Smart contract tests use Foundry Forge, wrapped by bun scripts that manage profiles, environment, and exclusions.

How To Approach Tests

Forge tests verify Solidity contract behavior in isolation and against live chain state. The guiding philosophy is: never use raw forge commands — always go through the bun script wrappers that set the correct profile, environment variables, and exclusion flags.

Foundry Profiles

The foundry.toml in packages/contracts/ defines multiple profiles, each targeting a different use case:

Profilevia_irPurpose
defaultfalse (selective)Local iteration; fastest builds via sparse_mode = true
testtrueUnit tests; separate out-test artifacts avoid stale cache
citrueGitHub Actions; 10,000 fuzz runs for thorough coverage
forktrueFork tests against live chains; 32 fuzz runs, 30M gas limit
productiontrueDeploy builds; optimizer_runs = 1 for smallest bytecode

Certain contracts always require via_ir due to stack depth limits. The default profile uses compilation_restrictions to selectively enable it for Action.sol, Deployment.sol, Karma.sol, and mock contracts.

Test Organization

All tests extend shared base contracts that provide common setup:

  • Unit tests (test/unit/) -- isolated contract logic with mock dependencies
  • Integration tests (test/integration/) -- multi-contract interaction with shared fixtures
  • E2E tests (test/E2E*.t.sol) -- full protocol workflow from deploy through attestation
  • Fuzz tests (test/Fuzz*.t.sol) -- property-based testing with configurable run count

Completing Test Coverage

ABI Encoding Convention

Resolvers use tuple decode, not struct decode. EAS stores attestation data as flat tuples (encoded by the client with encodeAbiParameters), so all resolver contracts must decode accordingly:

// Correct: tuple decode
(string memory title, address contributor) = abi.decode(data, (string, address));

// Wrong: struct decode (will revert on real attestation data)
WorkData memory work = abi.decode(data, (WorkData));

Mock Contracts

Mock contracts must implement all called functions, not just the ones under test. Import enums from the concrete contract, not the interface. All test addresses must be checksummed.

Fork Test Patterns

Fork tests run against real chain state via RPC. They load RPC URLs through vm.envString() rather than [rpc_endpoints] in foundry.toml (which would cause all tests to fail if env vars are missing).

Fork tests use ForkTestBase._deployFullStackOnFork() which calls _setupHatsTreeOnFork() to bootstrap the Hats Protocol tree using real chain constants from HatsLib.sol.

Running Tests

Unit Tests

Always use bun scripts, never raw forge commands:

# Unit tests (excludes E2E and fork tests)
cd packages/contracts && bun run test

# Fast subset (excludes integration-heavy tests)
cd packages/contracts && bun run test:fast

# Match specific test files
cd packages/contracts && bun run test:match test/unit/MyContract.t.sol

The bun run test script sets FOUNDRY_PROFILE=test and applies --no-match-contract 'E2E' --no-match-path 'test/fork/**' automatically.

Fork Tests

# Run all fork tests (requires RPC URLs in .env)
cd packages/contracts && bun run test:fork

# Chain-specific E2E fork tests
cd packages/contracts && bun run test:e2e:sepolia
cd packages/contracts && bun run test:e2e:arbitrum
cd packages/contracts && bun run test:e2e:celo

Build System

The adaptive build system (bun build) defaults to fast mode (~2s cached) and only triggers a full build when necessary. For CI and deployments, use bun build:full which compiles with --skip test --skip script.

bun build                 # Adaptive (fast by default)
bun build:fast # Force fast mode
bun build:full # Full build (CI/deploy)
bun build:target -- src/registries/Action.sol # Single target

CI Integration

The contracts-tests.yml workflow runs on pushes and PRs touching packages/contracts/. It installs Foundry via foundry-rs/foundry-toolchain@v1, runs bun run test for unit tests, and has a separate fork job with a 90-minute timeout for chain-specific E2E tests.

Resources

  • Foundry Book -- Official Foundry documentation
  • Forge Test Reference -- Test command reference
  • Contract source: packages/contracts/src/
  • Test source: packages/contracts/test/
  • Build config: packages/contracts/foundry.toml
  • CI workflow: .github/workflows/contracts-tests.yml

Next: Playwright E2E Testing

Learn how to write end-to-end tests for the client PWA and admin dashboard.

Playwright E2E Testing