Skip to content
This repository was archived by the owner on Jan 9, 2026. It is now read-only.
Open
Changes from all commits
Commits
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
74 changes: 46 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ To configure your development environment to use Foundry with Chainweb EVM:
```

3. Create a `remappings.txt` file in the root of your project.

```bash
touch remappings.txt
```

4. Add the following to the `remappings.txt` file to override the default dependency mapping:

Expand Down Expand Up @@ -151,9 +155,11 @@ You can set the following configuration parameters in the `chainweb.config.json`
| `chainIdOffset` | `number` | Base Ethereum chain identifier to use as the starting point for Chainweb EVM chain identifiers. For example, if you set the base `chainIdOffset` to 1000, `numberOfChains` to three, and the `chainwebChainIdOffset` index to zero, the resulting chain identifiers would be 1000, 1001, and 1002. |
| `externalHostUrl` | `string` | Base URL for external Chainweb network access. |

You should note that the `chainid` (blockchain network Id) used in Chainweb EVM tests and scripts for Foundry projects is computed using the `chainIdOffset` defined in the project `chainweb.config.json` file as a base and incremented for each chain where a test or script runs.
You should note that the `chainid` used in Chainweb EVM tests and scripts for Foundry projects is computed by using the `chainIdOffset` for the target network as defined in the project `chainweb.config.json` file.
The `chainIdOffset` value is used as a base for the network chain identifier and incremented for each chain where a test or script runs.

The Chainweb chain Id (for instance 20, 21, 22, etc) is computed using the `chainwebChainIdOffset` defined in the project `chainweb.config.json` file as a base and incremented for each chain where a test or script runs.
The Chainweb chain identifier is computed by using the `chainwebChainIdOffset` for each target network as defined in the project `chainweb.config.json` file. a
The `chainwebChainIdOffset` value is used as a base for the Chainweb chain identifier and incremented for each chain where a test or script runs.

**Important**: The `chainweb.config.json` configuration file is only used when running scripts with `ChainwebScript`.
When running tests with `ChainwebTest`, the configuration parameters are passed directly to the constructor and the configuration file is ignored.
Expand All @@ -167,15 +173,15 @@ For example:
```bash
contract CounterTest is ChainwebTest(2, 0) {
function userSetUp() public override {
// Custom setup can be done here if needed
// Custom setup logic can be added here if needed
console.log("Setting up your test here");
}
}
```

## Tests

To write tests that run on Chainweb EVM and support the multi-chain network, you should extend the `ChainwebTest` contract that's defined in the `lib/foundry-chainweb/src/Chainweb.sol` file instead of using the default Foundry `Test` base contract.
To write tests that run on Chainweb EVM and support the multi-chain network, you should import the `ChainwebTest` contract that's defined in the `lib/foundry-chainweb/src/Chainweb.sol` file instead of using the default Foundry `Test` base contract.

The `ChainwebTest` code extends the Foundry `Test` contract by adding a `chainweb` property that provides two additional methods—the `getChainIds` and `switchChain` methods—to support the multi-chain network.

Expand All @@ -193,7 +199,7 @@ The `ChainwebTest` constructor takes the following parameters to set up multi-ch

### Adding multi-chain tests

The following example demonstrates using `ChainwebTest` with the `switchChain` method to test the `setNumber` function from the `Counter` contract on four chains:
The following example demonstrates using `ChainwebTest` with the `switchChain` method to test the `setNumber` and `increment` functions from the `Counter` contract on four chains:

```Solidity
// SPDX-License-Identifier: UNLICENSED
Expand Down Expand Up @@ -233,7 +239,6 @@ contract CounterTest is ChainwebTest(4, 0) {
}
}
}

```

### Executing multi-chain tests
Expand Down Expand Up @@ -385,15 +390,15 @@ contract CounterScript is ChainwebScript {
The script automatically reads configuration settings from the `chainweb.config.json` file and uses the `CHAINWEB` environment variable, if specified, to determine which configuration settings to apply.
If you don't specify the `CHAINWEB` environment variable, the script uses the `anvil` environment by default.

For example, you can deploy the `Counter` contract with the default configuration that uses the `anvil` node environment and the private key for a test account by running the `Counter.s.sol` script like this:
For example, you can deploy the `Counter` contract with the default configuration that uses the `anvil` node as the target network and the private key for a test account by running the `Counter.s.sol` script like this:

```bash
forge script --multi script/Counter.s.sol:CounterScript \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
--broadcast
```

You can run the deployment script using the `testnet` configuration from the `chainweb.config.json` file as the target environment by running a command similar to the following:
You can run the deployment script using the `testnet` configuration from the `chainweb.config.json` file as the target network by running a command similar to the following:

```bash
CHAINWEB=testnet forge script --multi script/Counter.s.sol:CounterScript \
Expand Down Expand Up @@ -421,7 +426,7 @@ The following sample `chainweb.config.json` file demonstrates the configuration
}
```

This deployment script deploys the contract on four Chainweb EVM chains forked from anvil with output similar to the following:
This deployment script deploys the contract on five Chainweb EVM chains forked from anvil with output similar to the following:

```bash
[⠊] Compiling...
Expand Down Expand Up @@ -591,8 +596,7 @@ Paid: 0.000092815000092815 ETH (92815 gas * 1.000000001 gwei)
✅ Sequence #4 on 62603 | Total Paid: 0.000130903263641675 ETH (136306 gas * avg 0.93788673 gwei)

✅ Sequence #5 on 62604 | Total Paid: 0.000130903263641675 ETH (136306 gas * avg 0.93788673 gwei)



==========================

ONCHAIN EXECUTION COMPLETE & SUCCESSFUL.
Expand All @@ -602,7 +606,7 @@ Transactions saved to: /Users/hswope/git-repos/kadena/foundry-chainweb/examples/
Sensitive details saved to: /Users/hswope/git-repos/kadena/foundry-chainweb/examples/Counter/cache/multi/Counter.s.sol-latest/run.json
```

In the sample output for deploying the contract on multiple chains, you'll notice that the Foundry chain identifiers are computed using the default value from the `chainweb.config.json` `chainIdOffset` as a base value and incremented for each Chainweb EVM chain.
In the sample output for deploying the contract on multiple chains, you'll notice that the Foundry chain identifiers are computed using the default value from the `chainweb.config.json` file, with `chainIdOffset` as a base value and incremented for each Chainweb EVM chain.
As a result of the computation, Chainweb EVM chain 20 (index 0) maps to the Foundry chain identifier 62600, Chainweb EVM chain 21 (index 1) maps to the Foundry chain identifier 62601, and so on.

## Verifying contracts
Expand Down Expand Up @@ -630,7 +634,7 @@ For example:
--constructor-args $(cast abi-encode "constructor(string,string,uint256,uint256)" "ForgeUSD" "FUSD" 18 1000000000000000000000)
```

You can run the following command to see all the options:
You can run the following command to see all of the command-line options:

```bash
forge verify-contract --help
Expand All @@ -654,11 +658,11 @@ uint256 public constant DUMMY = 1;

Increase the value used for the constant in each contract you deploy to verify contracts that are deployed on multiple chains.

Contract verification is not possible against anvil, as there is no block explorer for anvil.
Contract verification is not possible against anvil, because there is no block explorer for anvil.

## Deterministic deployment

The `/examples/Counter` folder in the [foundry-chainweb](https://github.com/kadena-io/foundry-chainweb) repository includes a sample script that demonstrates how to deploy a contract that has the same address on every chain.
The `/examples/Counter` folder in the [foundry-chainweb](https://github.com/kadena-io/foundry-chainweb) repository includes a sample script that demonstrates how to deploy a contract that has the same address on every chain.

The `CounterCreate2.s.sol` script demonstrates deterministic deployment across all Chainweb EVM chains using the CREATE2 opcode.
The CREATE2 opcode ensures that the same contract is deployed using a predetermined address that is exactly the same on every chain, making cross-chain interactions predictable and easier to manage.
Expand All @@ -680,7 +684,7 @@ To simulate the deployment using the sample script:
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
```

To deploy the Counter contract using the sample deployment script, add the `--broadcast` command-line option.
To deploy the `Counter` contract using the sample deployment script, add the `--broadcast` command-line option.

For example:

Expand All @@ -699,28 +703,32 @@ The sample script demonstrates the following deployment steps:

## Methods

#### `chainweb.switchChain`
### `chainweb.switchChain`

Switch to a different chain in the Chainweb network.

| Parameter | Type | Description |
| --------- | --------- | ----------------------------------------- |
| `chainId` | `uint256` | The target Chainweb chain ID to switch to |
| `chainId` | `uint256` | The target Chainweb chain identifier to switch to. |

#### `chainweb.getChainIds`
### `chainweb.getChainIds`

Retrieve a list of available Chainweb chain IDs.

| Output | Type | Description |
| ---------- | ----------- | -------------------------------------------- |
| `chainIds` | `uint256[]` | An array containing valid Chainweb chain IDs |

---
| `chainIds` | `uint256[]` | An array containing valid Chainweb chain identifiers. |

### ChainID
It's possible to condifigure `chainId` in `founder.toml`. However, Chainweb EVM has a different concept of chainId. The Chainweb Chain Id of a Chainweb EVM chain is a number like 20, 21, 22, 23, 24 for our testnet. This Chainweb Chain Id should be configured in your project's `chainweb.config.json `as `chainwebChainIdOffset`. The value of `chainwebChainIdOffset` would be 20 for Chainweb EVM testnet. In the example `chainweb.config.json `, 20 is also configured as the `chainwebChainIdOffset` for anvil. For internal anvil instances that get spun up for tests, you will see Chainweb chain Ids staring with 0.
You should note that **Chainweb chain identifiers** 0 through 19 are reserved for chains that support Pact, so Kadena public networks use the `chainwebChainIdOffset` to set the starting point for Chainweb EVM chain identifiers.
For example, the Chainweb EVM testnet consists of five chains with the Chainweb chain identifiers 20, 21, 22, 23, and 24.
This value is configured in the `chainweb.config.json` file for your project.
In the sample `chainweb.config.json` file, the `anvil` deployment target is also configured to use 20 as the starting point for Chainweb EVM chain identifiers.
However, if you spin up internal `anvil` instances for testing, you'll see Chainweb chain identifiers starting with 0.

The `chainIdOffset` is the starting point for the network chain Id (akin to 1 for Ethereum mainnet). For Chainweb EVM testnet, the starting value is 5920, as this is the network chain Id of Chainweb EVM testnet chain 20. Chainweb EVM testnet chain 21 has network chain Id 5921, and so on.
The `chainIdOffset` in the `chainweb.config.json` file is the starting point for the **Ethereum network chain identifier**, similar to 1 for the Ethereum mainnet.
For Chainweb EVM testnet chains, the starting value for the Ethereum network chain identifier is 5920.
The network chain identifier for the Chainweb EVM testnet chain 20 is 5920.
Chainweb EVM testnet chain 21 has network chain identifier 5921, and so on.

## How nonces are maintained in multi-fork scenarios

Expand All @@ -741,7 +749,17 @@ If you specify the msg.sender account by setting the `--sender` or `--private-ke

You can also use the `vm.startBroadcast` function to explicitly set the sender for the next call. The nonce for that sender is maintained per fork.

# Known Issues
When running the `forge test --gas-report` command in the root directory of the repo, the `test_Nonce` test case will fail. This is because when the `--gas-report` option is used, Foundry does not properly isolate the forks. The nonces are global instead of per fork.
# Known issues

If you run the `forge test` command with the `--gas-report` command-line option in the root directory of the `foundry-chainweb` repo, the `test_Nonce` test case fails.
This failure occurs because Foundry does not isolate the forks with separate nonces when the `--gas-report` option is used.
The nonces are global instead of per fork when you use the `--gas-report` command-line option.

When running the `forge test --gas-report` command, the `test_setupChainsForScript` may not work. This is because when the `--gas-report` option is used, contract state on forks is not isolated. The chainId precompile is deployed and it's state variable is set to, for instance 0, for the first chain. It is then set to 1 for the second chain. However, the state in the contract on the first chain fork is overwritten to 1 instead of writing that state to a separate fork. It's not clear why this happens, but logging has shown that it happens. Code changes that seeminlgy have nothing to do with this issue can make the test case work or not work in a seemingly random pattern. The cheatcode `vm.revokePersistent` is used in the `deployChainWebChainIdContract` function to ensure that the contract addresses are isolated when using `--gas-report`. However, it can't be guaranteed that this will always work.
In addition, the `test_setupChainsForScript` test case might fail when running the `forge test` command with the `--gas-report` command-line option.
This failure occurs because the contract state on the forks is not isolated.
For example, when the chainId precompile is deployed, its state variable is set to 0 for the first chain.
The state variable is then set to 1 for the second chain.
However, the state in the contract on the first chain fork is **overwritten** to 1 instead of writing that state to a separate fork.
The behavior can be observed by logging activity, but the root cause of the behavior isn't known at this time.
You can attempt to work around this behavior by using the `vm.revokePersistent` cheat code in the `deployChainWebChainIdContract` function to isolate contract addresses are when using the `--gas-report` command-line option.
However, the work around doesn't guarantee that the `test_setupChainsForScript` test case will succeed in all cases.