Skip to content

A streamlined smart contract development and testing project using Foundry and Kurtosis CDK, focused on local L1/L2 setups, automated test flows, and deployment scripting.

License

Notifications You must be signed in to change notification settings

itev4n7/foundry-kurtosis-task

Repository files navigation

πŸ”— L2 Local Testing & Deployment Framework

Tools used: Kurtosis CDK Β· Docker Β· Foundry Β· Node.js Β· Ethers.js Β· Jest Β· Axios


πŸ› οΈ Prerequisites

Ensure your machine meets the following:

  • βœ… At least 16 GB of RAM
  • βœ… Node.js installed (>= v20.*)
  • βœ… Foundry installed
  • βœ… Docker installed (>= v4.27.0)
  • βœ… jq installed
  • βœ… Kurtosis CLI installed
  • βœ… pnpm installed

Table of Contents


βš™οΈ Setting Up Local L1 & L2 Using Kurtosis CDK (that based on cdk-erigon)

Steps based on kurtosis-cdk

Clone Kurtosis CDK Repo (tags/v0.4.4)

git clone https://github.com/0xPolygon/kurtosis-cdk.git
cd kurtosis-cdk

πŸ§ͺ To have a static port for local RPC URL http://127.0.0.1:61712 (better for testing), run Kurtosis with a custom args file:

kurtosis run --enclave cdk github.com/0xPolygon/kurtosis-cdk --args-file ./.github/tests/nightly/static-ports/custom-static-ports.yml

πŸ—οΈ Once completed, you'll see the User Services running successfully (it takes around 5-8 minutes to spin up L1 and L2)

User Services

πŸ›‘ To Shutdown the Kurtosis Node

kurtosis clean --all

πŸ› οΈ MacOS (Apple Silicon) Troubleshooting

❗ Problem 1: AMD64 Image Compatibility

Kurtosis uses amd64 images, which may not run on M chips

βœ… Solution

  1. Install Rossetta:

    softwareupdate --install-rosetta --agree-to-license
  2. Enable Rosetta support in Docker:

    • Go to Docker Settings β†’ General
    • Enable: Use Rosetta for x86_64/amd64 emulation on Apple Silicon
    • Click Apply & Restart
  3. Manually pull the necessary Docker images:

    docker pull --platform=linux/amd64 hermeznetwork/zkevm-prover:v8.0.0-RC16-fork.12
    docker pull --platform=linux/amd64 leovct/e2e:78df008-cdk

❗ Problem 2: Default L2 Contract Deployment Process Stuck

If the deployment process gets stuck, it is likely due to Docker’s default memory limit being too low (8GB).
Increase Docker memory allocation to at least 16GB or 24GB (32GB recommended) to fix this issue.

βœ… How to increase Docker memory:

  • Go to Docker Settings
  • Open the Resources tab
  • Increase the Memory limit
  • Ensure the CPU limit is at least 4
  • Click Apply & Restart

πŸ”„ Once Docker is ready, run the Kurtosis command again.

kurtosis run --enclave cdk github.com/0xPolygon/kurtosis-cdk --args-file ./.github/tests/nightly/static-ports/custom-static-ports.yml

ℹ️ Additional: Getting Erigon RPC URL

If you need the Erigon RPC URL (static URL is usually http://127.0.0.1:61712), run:

kurtosis port print cdk cdk-erigon-rpc-001 rpc

🧡 End-to-End Deployment and Testing Script

Before running tests, install required dependencies for the repo:

pnpm i

Run to create .env from example file:

cp .env.example .env

πŸ› οΈ To run the full deployment and testing flow (after Kurtosis node is running):

The most recommended way to run all tests and deployment processes in one go.

  • ▢️ Simple reporting mode:

    ./scripts/deploy-and-verify.sh
  • 🐞 Debug reporting mode (see full logs per stage):

    ./scripts/deploy-and-verify.sh --debug

πŸ“ Script Report Output:

  • ℹ️ After running the deploy-and-verify.sh shell script, a script-report.txt file will be generated.
    It contains detailed logs of every stage during the script run.

πŸ§ͺ Manual & Dedicated Stage-by-Stage Test Flows:

πŸ”— Stage 1: Raw Precompile Invocation

πŸ”§ Manual Check: Precompile Contract (0x02)

We will test if the 0x02 precompiled contract correctly computes the SHA-256 hash of the input.

  • Input message: "Hello, Erigon"
  • Hex representation: 0x48656c6c6f2c20457269676f6e
  • Expected SHA-256 hash result: 0x3c5f683f4ecfdf824570081dda764483bb4f259a71a802b348f914b0451c5fab

Manual test using cast:

cast call 0x0000000000000000000000000000000000000002 0x48656c6c6f2c20457269676f6e --rpc-url kurtosis

πŸ€– Automated Precompile Test (Preferred)

The project includes an automated test flow using both eth_call via RPC and ethers.js.

Run test that use RPC API:

pnpm run test-precompile-rpc

Run test that use ethers.js:

pnpm run test-precompile

πŸ”— Stage 2: Deploy Smart Contract Using Forge (Contract Wrapper Deployment)

🧱 Step 1: Deploy Contract to L2

Run the following command to deploy the smart contract:

forge script script/StorageHash.s.sol --rpc-url kurtosis --legacy --json --broadcast

πŸ“¦ Step 2: Retrieve Deployment Output

After deployment, in terminal output you will find JSON like this:

{ "chain": 10101, "status": "success", "tx_hash": "0xf455d4680403f45ca6cc1ec5c9752b14d73a63deb896981729f49e45b27a5dd5", "contract_address": "0x998B1ACBF8E46fB93476aD4482a4490326BC1EDb", "block_number": 9040, "gas_used": 260222, "gas_price": 1000000000 }

For further testing you will need:

  • βœ… tx_hash – transaction hash
  • βœ… contract_address – deployed contract address

You can also find this information in broadcast/StorageHash.s.sol/10101/run-latest.json

πŸ§ͺ Step 3: Test Deployed Contract

  • πŸ” Option A: Run Automated Test
    pnpm run test-post-deploy
  • πŸ§‘β€πŸ’» Option B: Manual Verification Using curl
    To check transaction receipt run:
    curl -X POST http://127.0.0.1:61712 \
    -H "Content-Type: application/json" \
    -d '{"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":["<tx_hash>"],"id":1}'
    To check deployed contract bytecode:
    curl -X POST http://127.0.0.1:61712 \
    -H "Content-Type: application/json" \
    -d '{"jsonrpc":"2.0","method":"eth_getCode","params":["<contract_address>", "latest"],"id":2}'
    🧰 (Alternative) To check contract code using cast
    cast code <contract_address> --rpc-url kurtosis

πŸ”— Stage 3: Deployed Contract Interaction on Local Erigon (Contract Invocation)

πŸ” Manual flow:

  • Check current hashedValue (default bytes32 equals to 0x...0), run following:

    cast call <contract_address> "hashedValue()(bytes32)" --rpc-url kurtosis
  • πŸ”’ Call contract function with number to hash it's value and store inside contract:

    cast send <contract_address> "hashNumber(uint256)()" 44 --rpc-url kurtosis --legacy --interactive

    ⚠️ Note:
    This command uses the --interactive flag, so you'll be prompted to paste the private key manually for security reasons.
    Since this is a local testing key, it's safe to reveal it:
    0x12d7de8621a77640c9241b2595ba78ce443d05e94090365ab3bb5e19df82c625

  • πŸ” Verify that the value was hashed and stored in contract:

    cast call <contract_address> "hashedValue()(bytes32)" --rpc-url kurtosis

βš™οΈ Automated way (recommended):

pnpm run test-contract-invocation

➑️ This script tests contract interaction via ethers.js.


🧰 Additionally, Unit Tests on Foundry for Smart Contracts

  • To run the tests:

    forge test

Powered by 🧠 brain and handsπŸ§‘β€πŸ’»!

About

A streamlined smart contract development and testing project using Foundry and Kurtosis CDK, focused on local L1/L2 setups, automated test flows, and deployment scripting.

Resources

License

Stars

Watchers

Forks