Skip to content

Foundry (EVM example) ​

Deploy, verify and test a Solidity contract with forge. Reference: smart contract cookbook.

Copied from Futureverse’s futureversecom/trn-examples, deleted with its GitHub organisation, at commit 44c9e16 of 2024-10-31. Apache-2.0: license. Before you run it, read Running the examples.

Offline destinations

Some places this page points to did not answer when last checked:

  • github.com/futureversecom — organisation deleted (HTTP 404) (checked 2026-10-10)
  • porcini.rootnet.app — TLS certificate does not cover the host (checked 2026-10-10), named in code

The node source survives as the mirror https://github.com/marco-fv/trn-seed-public, and the organisation’s code examples are on this site as Code examples; its other repositories have no public copy.

This project showcases the deployment and verification of a basic smart contract on the TRN Porcini network using Foundry's forge CLI. It includes a sample contract, a corresponding test, and a deployment script.

IMPORTANT

Ensure the following ENV vars are available before running the examples

Examples ​

bash
# change your working directory to this example first
cd examples/substrate/use-forge

# export all required environments
export CALLER_PRIVATE_KEY=

pnpm forge:install

# deploy contract using `forge create`
pnpm forge:create

# deploy contract using `forge script`
pnpm forge:script

Procedure: ​

There are two methods to deploy contracts using Foundry:

  1. Using forge create

    Users can utilize the forge create command to deploy and verify a contract by specifying all the necessary parameters in the command. Here is an example:

    forge create \
        --rpc-url $PORCINI_RPC_URL \
        --private-key $DEPLOYER_PRIVATE_KEY \0
        --legacy \
        --optimize \
        --optimizer-runs 200 \
        --use <SOLIDITY_VERSION> \
        --verify \
        --verifier sourcify \
        path/to/<CONTRACT_NAME>.sol:<CONTRACT_NAME> \
        --constructor-args {if necessary}

    Note: You can check out the shell script example included in this repo: examples/evm/use-forge/script/use-forge-create.sh

    In this example, we set the --rpc-url to TRN Porcini and specify the --private-key using environment variables (see #5 of Prerequisites section).

    Pay special attention to the --legacy flag, as it is necessary for successful contract deployment on the TRN Porcini network. Failure to include this flag may result in errors (refer to the troubleshooting section for more information).

  2. Using forge script

    Users can also create a deployment script to automate the deployment process without having to provide the parameters each time. For detailed examples and information, refer to this repository (offline, checked 2026-10-10).

    However, note that there are known issues (explained further in the following section) when using forge script. If any errors occur during deployment, it is recommended to revert back to using forge create.

Testing: ​

This project includes a test script written in Solidity (see examples/evm/use-forge/test/Counter.t.sol file). To run the test, execute the following command in your terminal:

forge test

Troubleshooting: ​

  • Deserialization Error: invalid type: null, expected f64 at line 1 column 240.

    This error occurs when the --legacy flag is not used during contract deployment. Including this flag will resolve the issue.

  • Failed to get EIP-1559 fees

    This error also arises when the --legacy flag is missing. Adding the flag will resolve the problem.

  • Transaction Failure: 0x9cfe621bac130205088168a30470f0cbfdacb22ebb7ce5869a348a7552b7e8b8

    When this error occurs, the transaction has been executed on the chain but returns with an error. You can check the error code in the event broadcasted on the blockchain explorer, as shown in the example screenshot below.

    Possible errors: OutOfGas.

    Explorer event ethereum.Executed for an EVM transaction: its from and to addresses and transaction hash, exitReason Error, and the error OutOfGas

    One possible reason for this error is that the estimated gas is insufficient for the transaction. Increasing the `--gas-estimate-multiplier`` to 200 or higher may resolve the issue.

  • (code: -32603, message: submit transaction to pool failed: InvalidTransaction(InvalidTransaction::Custom(3)), data: None)

    The InvalidTransaction::Custom(3) means GasLimitTooLow in frontier. A higher gas limit is required to avoid this error.

    Note: While using forge script, sometimes raising gas limit still cannot solve this error. In this case a workaround is to switch to forge create

Source files ​

contracts/Counter.sol ​

solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

contract Counter {
    int256 count;

    constructor(int256 _count) {
        count = _count;
    }

    function increment() public {
        count += 1;
    }

    function decrement() public {
        count -= 1;
    }

    function getCount() public view returns (int256) {
        return count;
    }
}

script/Counter.s.sol ​

solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

import "../contracts/Counter.sol";

import "forge-std/console.sol";
import "forge-std/Script.sol";

contract CounterScript is Script {
  function setUp() public {}

  function run() public {
    uint256 deployerPrivateKey = vm.envUint("CALLER_PRIVATE_KEY");

    vm.startBroadcast(deployerPrivateKey);
    new Counter(0);
    vm.stopBroadcast();
  }
}

script/use-forge-create.sh ​

bash
#!/usr/bin/env bash

forge create \
  --rpc-url "https://porcini.rootnet.app" \
  --private-key $CALLER_PRIVATE_KEY \
  --legacy \
  --verify \
  --verifier sourcify contracts/Counter.sol:Counter \
  --constructor-args 0

script/use-forge-script.sh ​

bash
#!/usr/bin/env bash

forge script \
  ./script/Counter.s.sol:CounterScript \
  --rpc-url "https://porcini.rootnet.app" \
  --private-key $CALLER_PRIVATE_KEY \
  --legacy \
  --broadcast \
  --verify \
  --verifier sourcify \
  -vvvv

test/Counter.t.sol ​

solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

import "forge-std/Test.sol";
import "../contracts/Counter.sol";

contract CounterTest is Test {
    Counter counter;

    function setUp() public {
        counter = new Counter(0);
    }

    function testGetCount() public {
        int value = counter.getCount();
        assertEq(value, 0);
        emit log_int(value);
    }

    function testIncrement() public {
        counter.increment();
        int value = counter.getCount();
        assertEq(value, 1);
        emit log_int(value);
    }

    function testDecrement() public {
        counter.decrement();
        int value = counter.getCount();
        assertEq(value, -1);
        emit log_int(value);
    }
}

foundry.toml ​

toml
[profile.default]
src = 'contracts'
test = 'test'
out = 'forge-artifacts'
libs = ['lib']
solc = "0.8.17"

optimizer = true
optimizer-runs = 200
verbosity = 2

[rpc_endpoints]
porcini = "https://porcini.rootnet.app"

package.json ​

json
{
  "scripts": {
    "test": "forge test",
    "clean": "forge clean",
    "forge:install": "forge install foundry-rs/forge-std --no-commit --shallow",
    "forge:create": "./script/use-forge-create.sh",
    "forge:script": "./script/use-forge-script.sh",
    "forge:test": "forge test"
  }
}

remappings.txt ​

text
ds-test/=lib/forge-std/lib/ds-test/src/
forge-std/=lib/forge-std/src/
Source: futureversecom/trn-examples, examples/evm/use-forge (deleted) · commit 44c9e16, 2024-10-31 · Apache-2.0, license · part of Code examples

Curated independently. Source-attributed reference for The Root Network. Not affiliated with Futureverse / TRN Labs.