Installing Foundry

On your command line, run:

curl -L https://raw.githubusercontent.com/matter-labs/foundry-zksync/main/install-foundry-zksync | bash
You can find more info on the official ZKsync Foundry website.

Create a New Project

forge init
forge build --zksync

This will create a new simple Foundry project with a Counter contract.

Set Configs

Use the following configs on your foundry.toml

[etherscan]
testnet = { key = "${ETHERSCAN_API_KEY}", url = "${TESTNET_VERIFIER_URL}", chain = 531050104 } 
sophon = { key = "${ETHERSCAN_API_KEY}", url = "${MAINNET_VERIFIER_URL}", chain = 50104 } 

[rpc_endpoints]
testnet = "${TESTNET_RPC_URL}"
sophon = "${MAINNET_RPC_URL}"

Create an .env file and set these env vars:

PRIVATE_KEY= # your deployer private key
ETHERSCAN_API_KEY= # your etherscan API key
PAYMASTER_ADDRESS= # Sophon's paymaster address

# sophon testnet
TESTNET_RPC_URL=https://rpc.testnet.sophon.xyz
TESTNET_VERIFIER_URL=https://api-explorer-verify.testnet.sophon.xyz/contract_verification
TESTNET_CHAIN_ID="531050104"

# sophon
MAINNET_RPC_URL=https://rpc.sophon.xyz
MAINNET_VERIFIER_URL=https://verification-explorer.sophon.xyz/contract_verification
MAINNET_CHAIN_ID="50104"

Counter Contract Deployment

Basic Deployment

source .env && forge create ./src/Counter.sol:Counter --rpc-url testnet --private-key $PRIVATE_KEY --zksync

Deployment and Verification

Same as Basic Deployment but you append the --verify flag

This will only verify on Sophscan
source .env && forge create ./src/Counter.sol:Counter --rpc-url testnet --private-key $PRIVATE_KEY --zksync --verify

Deployment Using Paymaster

Same as Basic Deployment but you append the --zk-paymaster-address flag

The paymaster allows you to perform gasless transactions, so there’s no need to have a $SOPH balance
source .env && forge create ./src/Counter.sol:Counter --rpc-url testnet --private-key $PRIVATE_KEY --zksync --zk-paymaster-address $PAYMASTER_ADDRESS --zk-paymaster-input $(cast calldata "general(bytes)" "0x")

Deployment With Paymaster and Verification (All-in-One)

source .env && forge create ./src/Counter.sol:Counter --rpc-url testnet --private-key $PRIVATE_KEY --zksync --zk-paymaster-address $PAYMASTER_ADDRESS --zk-paymaster-input $(cast calldata "general(bytes)" "0x") --verify

Deploy Contracts Using Foundry Scripts

Deployment Using Script

source .env && forge script ./script/Counter.s.sol --rpc-url testnet --private-key $PRIVATE_KEY --zksync --broadcast

Deployment Using Script and Verification

Same as Deployment Using Script but you append the --verify flag

This will only verify on Sophscan
source .env && forge create ./src/Counter.sol:Counter --rpc-url testnet --private-key $PRIVATE_KEY --zksync --verify

Deployment Using Script With Paymaster

Same as Deployment Using Script but you append the --zk-paymaster-address flag

This will only verify on Sophscan

To use the paymaster within a script, you have 2 options: using a cheatcode or making a low-level call:

Using a Foundry Cheatcode

  • Install forge-zksync-std library

  • Use the vmExt.zkUsePaymaster cheatcode

  • For your convenience, you can copy-paste the following script:

    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.13;
    
    import {Script, console} from "forge-std/Script.sol";
    import {Counter} from "../src/Counter.sol";
    import {TestExt} from "lib/forge-zksync-std/src/TestExt.sol";
    
    contract CounterScript is Script, TestExt {
        Counter public counter;
    
        function setUp() public {}
    
        function run() public {
            vm.startBroadcast();
    
            // Encode paymaster input
            bytes memory paymaster_encoded_input = abi.encodeWithSelector(
                bytes4(keccak256("general(bytes)")),
                bytes("0x")
            );
            vmExt.zkUsePaymaster(vm.envAddress("PAYMASTER_ADDRESS"), paymaster_encoded_input);
            counter = new Counter();
    
            vm.stopBroadcast();
        }
    }
    

You can now run the following command

source .env && forge script ./script/Counter.s.sol --rpc-url testnet --private-key $PRIVATE_KEY --zksync --broadcast

Using a Low-Level Call (in Case You Can’t Install the Cheatcode Library)

For your convenience, you can copy-paste the following script:

Here we use address(vm).call
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Script, console} from "forge-std/Script.sol";
import {Counter} from "../src/Counter.sol";
import {TestExt} from "lib/forge-zksync-std/src/TestExt.sol";

contract CounterScript is Script, TestExt {
    Counter public counter;

    function setUp() public {}

    function run() public {
        vm.startBroadcast();

        // Encode paymaster input
        bytes memory paymaster_encoded_input = abi.encodeWithSelector(
            bytes4(keccak256("general(bytes)")),
            bytes("0x")
        );
        (bool success, ) = address(vm).call(
            abi.encodeWithSignature(
                "zkUsePaymaster(address,bytes)",
                vm.envAddress("PAYMASTER_ADDRESS"),
                paymaster_encoded_input
            )
        );
        require(success, "zkUsePaymaster() call failed");
        counter = new Counter();

        vm.stopBroadcast();
    }
}

You can now run the following command:

source .env && forge script ./script/Counter.s.sol --rpc-url testnet --private-key $PRIVATE_KEY --zksync --broadcast

Deployment Using Script With Paymaster and Verification

Same as Deployment Using Script With Paymaster but you append the --verify flag

This will only verify on Sophscan
source .env && forge script ./script/Counter.s.sol --rpc-url testnet --private-key $PRIVATE_KEY --zksync --broadcast --verify

Contract Verification

If you want to verify an already deployed contract, you can do so on Sophscan (an Etherscan-like explorer) and/or on Sophon’s Explorer.

On Sophscan

source .env && forge verify-contract COUNTER_CONTRACT_ADDRESS ./src/Counter.sol:Counter:Counter --watch --rpc-url testnet --zksync

On Sophon’s Explorer

forge verify-contract COUNTER_CONTRACT_ADDRESS ./src/Counter.sol:Counter --watch --verifier zksync --verifier-url $TESTNET_VERIFIER_URL --zksync

Using Libraries

If your contract relies on external libraries, you need to build it with the linked libraries during deployment. For example:

forge build --zksync --libraries ./contracts/util/SignatureChecker.sol:SignatureChecker:0xb0A2cf27Bf984bd0c56dCFb37C9DA0F2c5028844

This ensures the library is properly linked during the build and deploy process, enabling verification to succeed.

Contracts With Constructor Params

If your contract receives constructor params, you can use the --constructor-args flag:

Modify your Counter contract to receive a constructor param:

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

contract Counter {
    uint256 public number;

    constructor(uint256 _number) {
        number = _number;
    }

    function setNumber(uint256 newNumber) public {
        number = newNumber;
    }

    function increment() public {
        number++;
    }
}

You can now run:

forge create ./src/CounterWithConstructorParams.sol:Counter --rpc-url testnet --private-key $PRIVATE_KEY --zksync --constructor-args "$(cast abi-encode "constructor(uint256)" "123")"
To verify or use the paymaster, you can use the flags --verify and/or ----zk-paymaster-address as explained above.