Solidity
Version
Running on Solidity v0.8.34 via Foundry and Forge Std v1.15.0 (pre-installed in lib/forge-std/).
Supported languages
Solidity
Testing framework
Foundry (forge test) with forge-std/Test.sol. Tests are written in Solidity, not JavaScript.
Project layout
challenge/
├── foundry.toml # compiler + evm_version config
├── Main.s.sol # runCode entrypoint (Foundry script)
├── src/ # your contracts
│ └── HelloWorld.sol
├── tests/ # *.t.sol test files
│ └── HelloWorld.t.sol
├── lib/forge-std/ # pre-installed, import-ready
└── node_modules/ # user-installed npm packages (OpenZeppelin, etc.)Special reminders and implementation details
Contract (src/HelloWorld.sol)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.34;
contract HelloWorld {
function helloWorld() public pure returns (string memory) {
return "Hello World!";
}
}Test (tests/HelloWorld.t.sol)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.34;
import "forge-std/Test.sol";
import "../src/HelloWorld.sol";
contract HelloWorldTest is Test {
HelloWorld helloWorld;
function setUp() public {
helloWorld = new HelloWorld();
}
/// Should return "Hello World!"
function testReturnsHelloWorld() public view {
assertEq(helloWorld.helloWorld(), "Hello World!");
}
}Notes on tests:
- File name must end with
.t.sol. - Test contract must extend
Testfromforge-std/Test.sol. - Test function names must start with
test. UsetestFuzz*for fuzzed inputs,testFail*for tests that are expected to revert. - A one-line comment (
// …,/// …, or NatSpec/// @notice …//// @dev …) directly above each test function becomes the human-readable label shown in the results panel. Without one, the raw function name is displayed.
Run Code script (Main.s.sol at project root)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.34;
import "forge-std/Script.sol";
import "forge-std/console.sol";
import "./src/HelloWorld.sol";
contract Main is Script {
function run() external {
HelloWorld instance = new HelloWorld();
console.log(instance.helloWorld());
}
}forge script runs run() against an in-memory EVM; any console.log output is captured to the Run Code console panel. No local node (anvil) is required for simple read/deploy interactions.
Debugging
console.log
Import forge-std/console.sol and call console.log(...) from anywhere — including pure and view functions. Logs appear in the Run Code output panel (during forge script) and in the Tests panel (during forge test).
import "forge-std/console.sol";
function helloWorld() public pure returns (string memory) {
console.log("helloWorld() was called");
return "Hello World!";
}Supported types: string, uint, int, bool, address, bytes, bytes32. Use console.logInt / console.logBytes when overload resolution is ambiguous.
Cheatcodes
From forge-std/Test.sol:
vm.prank(addr)— next call is made asaddr.vm.warp(ts)/vm.roll(bn)— time-travel / block number override.vm.expectRevert()/vm.expectRevert("reason")— assert the next call reverts.vm.expectEmit()— assert an event will be emitted.vm.deal(addr, amount)— set ETH balance.
Assertions
assertEq,assertTrue,assertFalseassertGt,assertGe,assertLt,assertLeassertApproxEqAbs(a, b, tolerance)/assertApproxEqRel(a, b, maxDelta)
Package management
Solidity challenges support npm-based packages. The Dependencies panel searches the npm registry; install something like @openzeppelin/contracts, pick a version, and it is staged into node_modules/. foundry.toml declares libs = ["lib", "node_modules"] with auto_detect_remappings = true, so forge scans both paths and generates remappings like @openzeppelin/contracts/=node_modules/@openzeppelin/contracts/ automatically at build time.
import "@openzeppelin/contracts/utils/Strings.sol";
contract HelloWorld {
using Strings for uint256;
function helloWorld() public pure returns (string memory) {
return string.concat("Hello World! ", uint256(42).toString());
}
}No remappings.txt needed — forge walks node_modules/ and generates remappings automatically at build time.
Included libraries
- forge-std —
Test,Script,console,Vm(cheatcodes) - Foundry toolchain —
forge,anvil,cast,chisel