Using Foundry Foundry is a fast, Rust-based toolkit for developing, testing, and deploying EVM-compatible smart contracts. It includes:
forge — for building, testing, and deploying contracts
cast — for interacting with deployed contracts
anvil — for running a local Ethereum-compatible node
Foundry is well-suited for advanced developers who prefer Solidity-based tests and fast iteration.
Official Foundry repository: https://github.com/foundry-rs/foundry
Before you begin, ensure you have:
A Unix-like terminal (Linux, macOS, or WSL on Windows)
A funded wallet private key (testnet or mainnet SHM)
Access to a Shardeum RPC endpoint
Install Foundry using the official installer:
Before you begin, ensure you have:
A Unix-like terminal (Linux, macOS, or WSL on Windows)
A funded wallet private key (testnet or mainnet SHM)
Access to a Shardeum RPC endpoint
Install Foundry using the official installer:
curl -L https://foundry.paradigm.xyz | bash
Restart your terminal (or reload your shell), then run:
Restart your terminal (or reload your shell), then run:
foundryup
Verify installation:
```shell
forge --version
Verify installation:
If this installation method does not work,
or you would like to install Foundry a different way, see other installation methods in Foundry's README:
Initialize a new Foundry project:
Initialize a new Foundry project:
forge init shardeum-foundry
cd shardeum-foundry
forge init shardeum-foundry
cd shardeum-foundry
This creates the following structure:
This creates the following structure:
src/ — smart contracts
test/ — Solidity test contracts
foundry.toml — project configuration
src/ — smart contracts
test/ — Solidity test contracts
foundry.toml — project configuration
Create a new file at src/Contract.sol:
Create a new file at src/Contract.sol:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.7 ;
error sameStorageValue ();
error notOwner ();
error msgValueZero ();
contract SimpleStorage {
uint public storedData;
uint public ownerUnixTimeContract;
uint public storedData;
uint public ownerUnixTimeContract;
address public immutable owner;
constructor () {
owner = msg.sender ;
}
event setOpenDataEvent ( address indexed user , uint newValue );
event setOpenDataEvent ( address indexed user , uint newValue );
event setOwnerDataEvent ( uint newOwnerUnixTime );
event donateToOwnerEvent ();
function set ( uint x ) public {
if (storedData == x) revert sameStorageValue ();
if (storedData == x) revert sameStorageValue ();
storedData = x;
emit setOpenDataEvent ( msg.sender , x);
emit setOpenDataEvent ( msg.sender , x);
}
function setOwnerData () public {
if ( msg.sender != owner) revert notOwner ();
if ( msg.sender != owner) revert notOwner ();
ownerUnixTimeContract = block .timestamp;
emit setOwnerDataEvent ( block .timestamp);
}
function donateToOwner () public payable {
if ( msg .value == 0 ) revert msgValueZero ();
if ( msg .value == 0 ) revert msgValueZero ();
payable (owner). transfer ( address ( this ).balance);
emit donateToOwnerEvent ();
}
}
Create test/Contract.t.sol:
Create test/Contract.t.sol:
// SPDX-License-Identifier: Unlicense
pragma solidity 0.8.7 ;
import "forge-std/Test.sol" ;
import "src/Contract.sol" ;
contract TestContract is Test {
receive () external payable {}
fallback () external payable {}
receive () external payable {}
fallback () external payable {}
event setOpenDataEvent ( address indexed user , uint newValue );
event setOwnerDataEvent ( uint newOwnerUnixTime );
event donateToOwnerEvent ();
SimpleStorage storageContract;
SimpleStorage storageContract;
function setUp () public {
storageContract = new SimpleStorage ();
storageContract = new SimpleStorage ();
}
function testInitialState () public {
assertEq (storageContract. storedData (), 0 );
assertEq (storageContract. ownerUnixTimeContract (), 0 );
assertEq (storageContract. owner (), address ( this ));
function testInitialState () public {
assertEq (storageContract. storedData (), 0 );
assertEq (storageContract. ownerUnixTimeContract (), 0 );
assertEq (storageContract. owner (), address ( this ));
}
function testSetValue () public {
vm. expectEmit ( true , false , false , true );
emit setOpenDataEvent ( address ( this ), 1 );
storageContract. set ( 1 );
assertEq (storageContract. storedData (), 1 );
function testSetValue () public {
vm. expectEmit ( true , false , false , true );
emit setOpenDataEvent ( address ( this ), 1 );
storageContract. set ( 1 );
assertEq (storageContract. storedData (), 1 );
}
function testSetSameValueReverts () public {
vm. expectRevert (sameStorageValue.selector);
storageContract. set ( 0 );
function testSetSameValueReverts () public {
vm. expectRevert (sameStorageValue.selector);
storageContract. set ( 0 );
}
function testOwnerOnlyFunction () public {
vm. warp ( 10 );
storageContract. setOwnerData ();
assertEq (storageContract. ownerUnixTimeContract (), 10 );
function testOwnerOnlyFunction () public {
vm. warp ( 10 );
storageContract. setOwnerData ();
assertEq (storageContract. ownerUnixTimeContract (), 10 );
}
function testDonateRevertsOnZeroValue () public {
vm. expectRevert (msgValueZero.selector);
storageContract. donateToOwner ();
function testDonateRevertsOnZeroValue () public {
vm. expectRevert (msgValueZero.selector);
storageContract. donateToOwner ();
}
}
Run all tests:
Run all tests:
Run a specific test with verbose output:
Run a specific test with verbose output:
forge test -vvvv --match-test testSetValue
Create a .env file in the project root (or export variables in your shell):
SHARDEUM_RPC=https://api-mezame.shardeum.org
PRIVATE_KEY=YOUR_PRIVATE_KEY
Security note :
Never commit private keys to version control
Add .env to .gitignore
Foundry uses EIP-1559 transactions by default. Shardeum currently requires legacy-style gas parameters, so deployments should include the --legacy flag.
forge test -vvvv --match-test testSetValue
### Set Environment Variables
Create a `.env` file in the project root (or export variables in your shell):
SHARDEUM_RPC=https://api-mezame.shardeum.org
PRIVATE_KEY=YOUR_PRIVATE_KEY
**Security note**:
- Never commit private keys to version control
- Add `.env` to `.gitignore`
### Deploy to Shardeum Using Foundry
Foundry uses EIP-1559 transactions by default. Shardeum currently requires legacy-style gas parameters, so deployments should include the --legacy flag.
#### Deploy to Testnet (Mezame)
```shell
forge create \
--legacy \
--rpc-url $SHARDEUM_RPC \
--private-key $PRIVATE_KEY \
src/Contract.sol:SimpleStorage
forge create \
--legacy \
--rpc-url $SHARDEUM_RPC \
--private-key $PRIVATE_KEY \
src/Contract.sol:SimpleStorage
export SHARDEUM_RPC = https://api.shardeum.org
forge create \
--legacy \
--rpc-url $SHARDEUM_RPC \
--private-key $PRIVATE_KEY \
src/Contract.sol:SimpleStorage
If deployment succeeds, Foundry will output the deployed contract address.
If the terminal output is minimal, search for transactions from your wallet address on the appropriate Shardeum explorer.
After deployment, copy the contract address and search for it on:
You should be able to view:
contract creation transaction
deployed bytecode
interactions with the contract
Read stored value:
export SHARDEUM_RPC=https://api.shardeum.org
forge create
--legacy
--rpc-url $SHARDEUM_RPC
--private-key $PRIVATE_KEY
src/Contract.sol:SimpleStorage
If deployment succeeds, Foundry will output the deployed contract address.
If the terminal output is minimal, search for transactions from your wallet address on the appropriate Shardeum explorer.
### Verify Deployment on the Explorer
After deployment, copy the contract address and search for it on:
- [Shardeum Testnet Explorer](https://explorer-mezame.shardeum.org/)
- [Shardeum Mainnet Explorer](https://explorer.shardeum.org/)
You should be able to view:
- contract creation transaction
- deployed bytecode
- interactions with the contract
### (Optional) Interact Using Cast
Read stored value:
```shell
cast call \
--rpc-url $SHARDEUM_RPC \
<CONTRACT_ADDRESS> \
"storedData()(uint256)"
Set a new value:
cast call \
--rpc-url $SHARDEUM_RPC \
< CONTRACT_ADDRES S > \
"storedData()(uint256)"
Set a new value:
cast send \
--rpc-url $SHARDEUM_RPC \
--private-key $PRIVATE_KEY \
< CONTRACT_ADDRES S > \
"set(uint256)" \
42
Foundry is ideal for fast, Solidity-native testing and deployment
Use forge test for local validation before deploying
Deploy to Shardeum using forge create --legacy
Always confirm the correct RPC endpoint and network before deployment
cast send \
--rpc-url $SHARDEUM_RPC \
--private-key $PRIVATE_KEY \
< CONTRACT_ADDRES S > \
"set(uint256)" \
42
Foundry is ideal for fast, Solidity-native testing and deployment
Use forge test for local validation before deploying
Deploy to Shardeum using forge create --legacy
Always confirm the correct RPC endpoint and network before deployment