> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sophon.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Foundry

## Installing Foundry

On your command line, run:

```
curl -L https://raw.githubusercontent.com/matter-labs/foundry-zksync/main/install-foundry-zksync | bash
```

<Tip>
  You can find more info on the official [ZKsync
  Foundry](https://foundry-book.zksync.io/getting-started/installation) website.
</Tip>

## Create a New Project

```
forge init
forge build --zksync
```

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

## Set Configs

<Note>
  A full baseline Foundry example can be found on our GitHub
  [here](https://github.com/sophon-org/hello-foundry-template).
</Note>

Set your `foundry.toml` and `.env` as follows:

<CodeGroup>
  ```.toml foundry.toml theme={null}
  # The Default Profile
  [profile.default]
  solc_version = "0.8.24"
  zksolc_version = "1.5.11"
  src = "src"
  out = "out"
  libs = ["lib"]
  remappings = [
      "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
  ]

  [etherscan]
  sepoliaTestnet = { key = "${ETHERSCAN_API_KEY}", url = "${SEPOLIA_VERIFIER_URL}", chain = 11155111 }
  testnet = { key = "${ETHERSCAN_API_KEY}", url = "${SOPHON_TESTNET_VERIFIER_URL}", chain = 531050104 }
  sophon = { key = "${ETHERSCAN_API_KEY}", url = "${SOPHON_VERIFIER_URL}", chain = 50104 }
  ethereum = { key = "${ETHERSCAN_API_KEY}", url = "${VERIFIER_URL}", chain = 1 }

  [rpc_endpoints]
  sepoliaTestnet = "${SEPOLIA_RPC_URL}"
  testnet = "${SOPHON_TESTNET_RPC_URL}"
  sophon = "${SOPHON_RPC_URL}"
  ethereum = "${RPC_URL}"

  # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
  ```

  ```.env .env theme={null}

  PRIVATE_KEY="" # YOUR_PRIVATE_KEY your developer private key here
  ETHERSCAN_API_KEY="YOUR_ETHERSCAN_API_KEY" # your Sophscan API gey obtained from sophscan
  PAYMASTER_ADDRESS=0x98546B226dbbA8230cf620635a1e4ab01F6A99B2 # the paymaster address you want to use

  # ethereum sepolia
  SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/sp2BM_VFMURcKWMWg8HDVSBgvnm75_NS
  SEPOLIA_VERIFIER_URL=https://api.etherscan.io/api
  SEPOLIA_CHAIN_ID="11155111"

  # ethereum
  RPC_URL=https://ethereum-rpc.publicnode.com
  VERIFIER_URL=https://api.etherscan.io/api
  CHAIN_ID="1"

  # sophon testnet
  SOPHON_TESTNET_RPC_URL=https://rpc.testnet.sophon.xyz
  SOPHSCAN_TESTNET_VERIFIER_URL="https://api-sepolia.etherscan.io/api"
  SOPHON_TESTNET_VERIFIER_URL=https://api-explorer-verify.testnet.sophon.xyz/contract_verification
  SOPHON_TESTNET_CHAIN_ID="531050104"

  # sophon
  SOPHON_RPC_URL=https://rpc.sophon.xyz
  SOPHSCAN_MAINNET_VERIFIER_URL="https://api.sophscan.xyz/api"
  SOPHON_VERIFIER_URL=https://verification-explorer.sophon.xyz/contract_verification
  SOPHON_CHAIN_ID="50104"
  ```
</CodeGroup>

<Tip>
  In order to obtain a Sophscan API Key, you need to create an account and get an API Key
  [here](https://docs.sophscan.xyz/getting-started/viewing-api-usage-statistics).
</Tip>

## Counter Contract Deployment

### Basic Deployment

<Warning>
  If you run into deployment or interaction issues, always check for possible short-circuit errors
  caused by environment variables not being properly exposed to the foundry script. You can do so by
  running `source .env` before running the script or check the variable in your env by running `echo
      $VARIABLE_NAME`.
</Warning>

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

### Deployment and Verification

Same as [Basic Deployment](#basic-deployment) but you append the `--verify` flag and use the `--verifier zksync` flag to specify the verifier to use the Sophon Explorer or `--verifier etherscan` to use Sophscan.

<Note>This will only verify on **Sophscan**</Note>

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

### Deployment Using Paymaster

Same as [Basic Deployment](#basic-deployment) but you append the `--zk-paymaster-address` flag

<Note>
  The paymaster allows you to perform gasless transactions, so there's no need to have a SOPH
  balance
</Note>

```
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 \
--verifier zksync --verifier-url $TESTNET_VERIFIER_URL
```

## 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](#deployment-using-script) but you append the `--verify` flag

<Note>This will only verify on **Sophscan**</Note>

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

### Deployment Using Script With Paymaster

Same as [Deployment Using Script](#deployment-using-script) but you append the `--zk-paymaster-address` flag

<Note>This will only verify on **Sophscan**</Note>

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](https://foundry-book.zksync.io/zksync-specifics/forge-zksync-std)
* Use the [vmExt.zkUsePaymaster](https://foundry-book.zksync.io/zksync-specifics/cheatcodes/zk-use-paymaster) cheatcode
* For your convenience, you can copy-paste the following script:

  ```solidity theme={null}
  // 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:

<Info>Here we use `address(vm).call`</Info>

```solidity theme={null}
// 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](#deployment-using-script-with-paymaster) but you append the `--verify` flag

<Note>This will only verify on **Sophscan**</Note>

```
source .env && forge script ./script/Counter.s.sol \
--rpc-url testnet --private-key $PRIVATE_KEY --zksync --broadcast --verify \
--verifier etherscan --verifier-url $SOPHSCAN_TESTNET_VERIFIER_URL
```

## Contract Verification

If you want to verify an already deployed contract, you can do so on [Sophscan](https://sophscan.xyz) (an Etherscan-like explorer) and/or on [Sophon's Explorer](https://explorer.sophon.xyz).

**On Sophscan**

```
source .env && forge verify-contract COUNTER_CONTRACT_ADDRESS \
./src/Counter.sol:Counter:Counter --watch --rpc-url testnet --zksync \
--verifier zksync --verifier-url $SOPHSCAN_TESTNET_VERIFIER_URL
```

**On Sophon's Explorer**

```
forge verify-contract COUNTER_CONTRACT_ADDRESS ./src/Counter.sol:Counter \
--watch --verifier zksync --verifier-url $SOPHON_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:

<Warning>
  If you deploy a library via the EVM Equivalence Module, you can't link it to a zkEVM contract.
  We advise to keep your libraries inline, in possible. Check how to compile non-inlinable libraries [here](https://docs.zksync.io/zksync-era/tooling/hardhat/guides/compiling-libraries).
</Warning>

```
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:

```solidity theme={null}
// 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:

```shell theme={null}
forge create ./src/CounterWithConstructorParams.sol:Counter \
--rpc-url testnet --private-key $PRIVATE_KEY --zksync \
--constructor-args "$(cast abi-encode "constructor(uint256)" "123")"
```

<Tip>
  To verify or use the paymaster, you can use the flags `--verify` and/or `----zk-paymaster-address`
  as explained above.
</Tip>

## Check if a contract is whitelisted by the paymaster

Before making calls to a newly deployed contract using a paymaster, it's important to verify that the contract has been added to the paymaster's whitelist. Our automated contract whitelisting service typically takes 2-3 seconds to whitelist new contracts under normal network conditions. While optional, checking the whitelist status before proceeding makes deployment scripts more robust by ensuring contracts are ready to interact with the paymaster.

The code below provides a reference implementation for checking the whitelist status:

```solidity theme={null}
// 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";

interface IRestrictionContract {
    function contractWhitelist(address) external view returns (bool);
}

contract CounterScript is Script, TestExt {
    Counter public counter;

    function setUp() public {}

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

        address restrictionContractAddress = vm.envAddress("RESTRICTION_CONTRACT");
        address paymasterAddress = vm.envAddress("PAYMASTER_ADDRESS");
        uint256 waitTime = 5_000; // wait time in milliseconds
        uint256 maxAttempts = 5;
        bool isWhitelisted = false;
        uint256 attempts = 0;

        if (restrictionContractAddress != address(0)) {
            IRestrictionContract restrictionContract = IRestrictionContract(restrictionContractAddress);
            while (!isWhitelisted && attempts < maxAttempts) {
                try restrictionContract.contractWhitelist(address(this)) returns (bool whitelisted) {
                    isWhitelisted = whitelisted;
                    if (isWhitelisted) {
                        console.log("Contract is whitelisted on restriction contract!");
                        break;
                    } else {
                        attempts++;
                        if (attempts < maxAttempts) {
                            console.log("Contract not yet whitelisted. Waiting before retrying...");
                            vm.sleep(waitTime);
                        } else {
                            console.log("Contract still not whitelisted after max attempts. Proceeding anyway.");
                        }
                    }
                } catch {
                    console.log("Error checking whitelist status. Retrying...");
                    attempts++;
                    if (attempts < maxAttempts) {
                        vm.sleep(waitTime);
                    }
                }
            }
        }

        // 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)",
                paymasterAddress,
                paymaster_encoded_input
            )
        );
        require(success, "zkUsePaymaster() call failed");

        counter = new Counter();

        vm.stopBroadcast();
    }
}
```
