Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
1763818
Add simple foundry project.
elle-j Apr 29, 2026
5685cd9
Add CI workflow using foundry.
elle-j Apr 29, 2026
8e1efda
Remove foundryup script workaround since a fix has been merged.
elle-j Apr 29, 2026
4bb3d1b
Fix caching.
elle-j Apr 29, 2026
e633a23
Add explicit optimization settings.
elle-j Apr 29, 2026
f2abd88
Fix install path.
elle-j Apr 29, 2026
04e18a8
Fix build dependencies.
elle-j Apr 29, 2026
760ec25
Add wasm32 target for building foundry-polkadot.
elle-j Apr 29, 2026
ff4199b
Log forge inspect comands.
elle-j Apr 30, 2026
6607dda
Merge branch 'main' into lj/ci-foundry
elle-j Apr 30, 2026
db16593
Replace Counter example project with ERC20.
elle-j Apr 30, 2026
cbb5662
Update cached path and key.
elle-j Apr 30, 2026
94c0815
Replace --root with working-directory.
elle-j May 1, 2026
261d6b4
Use return values in transfer test.
elle-j May 1, 2026
7524d16
Remove caching of foundry/bin due to forge 'Illegal instruction' on c…
elle-j May 1, 2026
d4bb46a
Merge branch 'main' into lj/ci-foundry
elle-j May 4, 2026
b76187c
Use solc 0.8.35.
elle-j May 4, 2026
5e02cb4
Add explicit --use-resolc as inspect option.
elle-j May 4, 2026
15aa49e
Merge branch 'main' into lj/ci-foundry
elle-j May 4, 2026
a973046
Merge branch 'main' into lj/ci-foundry
elle-j May 12, 2026
aaaa01c
Reuse the build from differential tests.
elle-j May 12, 2026
34b7b3b
Move inline script into sh file.
elle-j May 12, 2026
8eec971
Add workflow comment.
elle-j May 12, 2026
86e1b18
Fix broken pipe in script.
elle-j May 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions .github/workflows/foundry.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Tests compilation and E2E integration with foundry-polkadot, including basic
# runtime behavior, but not differential runtime correctness.

name: Foundry Integration

on:
workflow_call:

env:
CARGO_TERM_COLOR: always
# Workflow-internal foundry envs are prefixed with `CI_` to not conflict with the
# `FOUNDRY_*` namespace, which foundry itself will interpret for its configuration.
CI_FOUNDRY_PROJECT_ROOT: tooling-projects/foundry/erc20
# This is currently the commit that adds support for resolc ">=0.6.0, <2.0.0".
# The latest release as of Apr. 28, 2026 supports ">=0.6.0, <0.7.0".
# Once a release includes the updated supported versions, this can be changed to a
# tag in order to download the prebuilt binary.
CI_FOUNDRY_POLKADOT_COMMIT: b3173d0584382687cc96b2af072d8cb2addf23d3
Comment on lines +16 to +18
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They plan a new release mid-May.


jobs:
use-foundry:
runs-on: ubuntu-24.04
steps:
- name: Checkout revive
uses: actions/checkout@v4

- name: Set Up Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
# without this it will override our rust flags
rustflags: ""
# TODO: Remove once the foundry install step replaces `--commit` with `--install`.
# Needed for building foundry-polkadot.
target: wasm32-unknown-unknown
components: rust-src

- name: Download resolc Binary
uses: actions/download-artifact@v4
with:
name: resolc-x86_64-unknown-linux-musl
path: resolc-bin

- name: Export resolc Path
run: |
chmod +x resolc-bin/resolc-x86_64-unknown-linux-musl
echo "RESOLC_PATH=$(pwd)/resolc-bin/resolc-x86_64-unknown-linux-musl" >> $GITHUB_ENV

# TODO: Remove once the foundry install step replaces `--commit` with `--install`.
- name: Install Build Dependencies
run: |
sudo apt-get update
sudo apt-get install -y clang libclang-dev build-essential protobuf-compiler

# NOTE: ~/.foundry/bin is not cached (which would be very useful when using `--commit`)
# due to forge sometimes crashing with `Illegal instruction` when using the cached
# binaries, seemingly due to the CPU being different when building forge vs running it.
- name: Install foundryup-polkadot and the Foundry Toolchain
run: |
# TODO: Replace `--commit` with `--install` once a release with this commit exists: https://github.com/paritytech/foundry-polkadot/commit/b3173d0584382687cc96b2af072d8cb2addf23d3

# Pin install path with `FOUNDRY_DIR` to prevent the pre-existing `XDG_CONFIG_HOME` from being used.
export FOUNDRY_DIR="$HOME/.foundry"
curl -fsSL https://raw.githubusercontent.com/paritytech/foundry-polkadot/refs/heads/master/foundryup/install | bash
"$FOUNDRY_DIR/bin/foundryup-polkadot" --commit "$CI_FOUNDRY_POLKADOT_COMMIT"

- name: Add Foundry to PATH
run: echo "$HOME/.foundry/bin" >> "$GITHUB_PATH"

- name: Check Tool Versions
run: |
foundryup-polkadot --version
forge --version

- name: Fetch Project Dependencies
working-directory: ${{ env.CI_FOUNDRY_PROJECT_ROOT }}
run: |
mkdir -p lib
git clone --depth 1 --branch v1.15.0 https://github.com/foundry-rs/forge-std.git lib/forge-std
git clone --depth 1 --branch v5.3.0 https://github.com/OpenZeppelin/openzeppelin-contracts.git lib/openzeppelin-contracts
Comment on lines +78 to +79
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use vars to define foundry and openzeppelin version


- name: Compile Project
working-directory: ${{ env.CI_FOUNDRY_PROJECT_ROOT }}
run: forge build --use-resolc "$RESOLC_PATH" --optimize -Oz

- name: Verify Output
working-directory: ${{ env.CI_FOUNDRY_PROJECT_ROOT }}
run: bash verify-compiler-output.sh "$RESOLC_PATH"

- name: Test Project
working-directory: ${{ env.CI_FOUNDRY_PROJECT_ROOT }}
run: forge test --polkadot=pvm --use-resolc "$RESOLC_PATH" --optimize -Oz -vvvv
4 changes: 4 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,7 @@ jobs:
# Only run this if `test` successfully completes.
needs: test
uses: ./.github/workflows/differential-tests.yml

run-foundry-tests:
needs: run-differential-tests
uses: ./.github/workflows/foundry.yml
25 changes: 25 additions & 0 deletions tooling-projects/foundry/erc20/foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[profile.default]
src = "src"
test = "test"
libs = ["lib"]
solc = "0.8.35"
remappings = [
"forge-std/=lib/forge-std/src/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
]
extra_output = [
"abi",
"metadata",
"devdoc",
"userdoc",
"storageLayout",
"ir",
"irOptimized",
"evm.bytecode",
"evm.deployedBytecode",
"evm.assembly",
"evm.methodIdentifiers",
]

[profile.default.polkadot]
resolc_compile = true
17 changes: 17 additions & 0 deletions tooling-projects/foundry/erc20/src/MyToken.sol
Comment thread
elle-j marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.22;

import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";

contract MyToken is ERC20, Ownable {
constructor(address initialOwner)
ERC20("MyToken", "MTK")
Ownable(initialOwner)
{}

function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
}
61 changes: 61 additions & 0 deletions tooling-projects/foundry/erc20/test/MyToken.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import { Test } from "forge-std/Test.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { MyToken } from "../src/MyToken.sol";

contract MyTokenTest is Test {
MyToken internal token;
address internal owner = address(this);
address internal alice = address(0xA11CE);
address internal bob = address(0xB0B);

function setUp() public {
token = new MyToken(owner);
}

function test_NameAndSymbol() public view {
assertEq(token.name(), "MyToken");
assertEq(token.symbol(), "MTK");
}

function test_Owner() public view {
assertEq(token.owner(), owner);
}

function test_OwnerCanMint() public {
token.mint(alice, 1000);
assertEq(token.balanceOf(alice), 1000);
}

function test_NonOwnerCannotMint() public {
vm.prank(alice);
vm.expectRevert(
abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, alice)
);
token.mint(alice, 1000);
}

function test_TotalSupplyIncreases() public {
uint256 before = token.totalSupply();
token.mint(alice, 500);
assertEq(token.totalSupply() - before, 500);
}

function test_Transfer() public {
token.mint(owner, 1000);
assertTrue(token.transfer(alice, 100));
assertEq(token.balanceOf(alice), 100);
assertEq(token.balanceOf(owner), 900);
}

function test_TransferFrom() public {
token.mint(owner, 1000);
token.approve(alice, 50);
vm.prank(alice);
assertTrue(token.transferFrom(owner, bob, 50));
assertEq(token.balanceOf(bob), 50);
assertEq(token.allowance(owner, alice), 0);
}
}
33 changes: 33 additions & 0 deletions tooling-projects/foundry/erc20/verify-compiler-output.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env bash

# Asserts that the compiled project contains the expected compiler output.
# Requires `forge` in PATH. Run from the project's root.
#
# Usage: verify-compiler-output.sh <resolc-path>

set -euxo pipefail

if [[ $# -ne 1 ]]; then
echo "usage: $(basename "$0") <resolc-path>" >&2
exit 2
fi

resolc=$1

inspect() {
forge inspect --use-resolc "$resolc" MyToken "$@"
}

inspect bytecode | grep '^0x50564d' > /dev/null
inspect deployedBytecode | grep '^0x50564d' > /dev/null
inspect irOptimized | grep . > /dev/null
inspect ir | grep . > /dev/null
inspect assembly | grep . > /dev/null
inspect abi --json | jq -e 'length > 0' > /dev/null
inspect methodIdentifiers --json | jq -e 'length > 0' > /dev/null
inspect storageLayout --json | jq -e '.storage | length > 0' > /dev/null
inspect metadata --json | jq -e 'length > 0' > /dev/null
inspect devdoc --json | jq -e 'length > 0' > /dev/null
inspect userdoc --json | jq -e 'length > 0' > /dev/null

echo "all checks passed"
Loading