> ## 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.

# Hardhat/Ethers

> Learn about how to develop on Sophon using Hardhat

## Getting Started

### Project Setup

We recommend scaffolding your project using the [zksync-cli](https://github.com/matter-labs/zksync-cli).

### Configuration

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

Set up your project with the following configuration:

<CodeGroup>
  ```typescript hardhat.config.ts theme={null}
  import type { HardhatUserConfig } from "hardhat/config";

  import "@matterlabs/hardhat-zksync";
  import * as dotenv from "dotenv";

  // Load environment variables from .env file
  dotenv.config();

  const config: HardhatUserConfig = {
    defaultNetwork: "sophonTestnet",
    networks: {
      hardhat: {
        zksync: true,
      },
      sophonMainnet: {
        url: "https://rpc.sophon.xyz",
        ethNetwork: "mainnet",
        verifyURL: "https://verification-explorer.sophon.xyz/contract_verification",
        browserVerifyURL: "https://explorer.sophon.xyz/",
        enableVerifyURL: true,
        zksync: true,
        accounts: [process.env.WALLET_PRIVATE_KEY as string],
      },
      sophonTestnet: {
        url: "https://rpc.testnet.sophon.xyz",
        ethNetwork: "sepolia",
        verifyURL: "https://api-explorer-verify.testnet.sophon.xyz/contract_verification",
        browserVerifyURL: "https://explorer.testnet.sophon.xyz/",
        enableVerifyURL: true,
        zksync: true,
        accounts: [process.env.WALLET_PRIVATE_KEY as string],
      },
    },
    zksolc: {
      version: "latest",
      settings: {},
    },
    solidity: {
      version: "0.8.27",
    },
    etherscan: {
      enabled: true,
      apiKey: {
        sophonTestnet: process.env.ETHERSCAN_SOPHON_API_KEY as string,
        sophonMainnet: process.env.ETHERSCAN_SOPHON_API_KEY as string,
      },
      customChains: [
        {
          network: "sophonTestnet",
          chainId: 531050104,
          urls: {
            apiURL: "https://api-testnet.sophscan.xyz/api",
            browserURL: "https://testnet.sophscan.xyz",
          },
        },
        {
          network: "sophonMainnet",
          chainId: 50104,
          urls: {
            apiURL: "https://api.sophscan.xyz/api",
            browserURL: "https://sophscan.xyz",
          },
        },
      ],
    },
  };

  export default config;
  ```

  ```env .env theme={null}
  WALLET_PRIVATE_KEY=your_private_key_here
  ETHERSCAN_SOPHON_API_KEY=your_sophscan_api_key_here
  ```
</CodeGroup>

## Contract Deployment

### Basic Deployment

Here's how to deploy a contract without using the paymaster:

```typescript theme={null}
import { utils } from "zksync-ethers";

const deployer = new Deployer(hre, wallet);
const artifact = await deployer.loadArtifact("Your_Contract");
const contract = await deployer.deploy(artifact, constructorArguments || []);
```

### Deployment with Paymaster

During the alpha stage, you'll need to use our Paymaster to sponsor transactions. Here's how to deploy using the paymaster:

```typescript theme={null}
import { Provider, Wallet, utils } from "zksync-ethers";
import { Deployer } from "@matterlabs/hardhat-zksync-deploy";
import { HardhatRuntimeEnvironment } from "hardhat/types";

export default async function (hre: HardhatRuntimeEnvironment) {
  const provider = new Provider(hre.network.config.url);
  const wallet = new Wallet(process.env.WALLET_PRIVATE_KEY!, provider);
  const deployer = new Deployer(hre, wallet);
  const artifact = await deployer.loadArtifact("your_contract");

  const params = utils.getPaymasterParams(
    "0x98546B226dbbA8230cf620635a1e4ab01F6A99B2", // Paymaster address
    {
      type: "General",
      innerInput: new Uint8Array(),
    }
  );

  const contract = await deployer.deploy(
    artifact,
    [], // Constructor arguments
    undefined, // Deployment type (use undefined for regular contract deployment)
    {
      customData: {
        paymasterParams: params,
        gasPerPubdata: utils.DEFAULT_GAS_PER_PUBDATA_LIMIT,
      },
    }
  );
}
```

### Proxy Deployment

To deploy proxy contracts with paymaster support:

```typescript theme={null}
import { utils } from "zksync-ethers";

const deployer = new Deployer(hre, wallet);
const artifact = await deployer.loadArtifact("Your_Contract");

await hre.zkUpgrades.deployProxy(deployer.zkWallet, artifact, [initializerArgs], {
  initializer: "initialize",
  paymasterProxyParams: params,
  paymasterImplParams: params,
});
```

## Contract Interaction

To interact with deployed contracts using the paymaster:

```typescript theme={null}
import { utils } from "zksync-ethers";

const paymasterParams = utils.getPaymasterParams("0x98546B226dbbA8230cf620635a1e4ab01F6A99B2", {
  type: "General",
  innerInput: new Uint8Array(),
});

const tx = await contract.yourFunction(params, {
  customData: {
    gasPerPubdata: utils.DEFAULT_GAS_PER_PUBDATA_LIMIT,
    paymasterParams: paymasterParams,
  },
});
```

## Contract Verification

You can verify contracts on both Sophscan and Sophon's explorer:

```bash theme={null}
npx hardhat verify --network sophonTestnet DEPLOYED_CONTRACT_ADDRESS constructor_arguments
```

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

```typescript theme={null}
const restrictionContractAddress = (hre.network.config as unknown as CustomNetworkConfig)
  ?.restrictionContract;
if (restrictionContractAddress && usePaymaster) {
  const contractAddress = await contract.getAddress();
  logger.info(
    `Checking if ${contractName} (${contractAddress}) is whitelisted on restriction contract...`
  );

  // Create interface for the restriction contract
  const restrictionContractABI = [
    {
      inputs: [
        {
          internalType: "address",
          name: "",
          type: "address",
        },
      ],
      name: "contractWhitelist",
      outputs: [
        {
          internalType: "bool",
          name: "",
          type: "bool",
        },
      ],
      stateMutability: "view",
      type: "function",
    },
  ];

  const provider = hre.ethers.provider;
  const restrictionContract = new hre.ethers.Contract(
    restrictionContractAddress,
    restrictionContractABI,
    provider
  );

  let isWhitelisted = false;
  let attempts = 0;
  const maxAttempts = 5;

  while (!isWhitelisted && attempts < maxAttempts) {
    try {
      isWhitelisted = await restrictionContract.contractWhitelist(contractAddress);

      if (isWhitelisted) {
        logger.info(`${contractName} is now whitelisted on restriction contract!`);
      } else {
        attempts++;
        if (attempts < maxAttempts) {
          logger.info(
            `${contractName} is not yet whitelisted. Waiting ${
              waitTime / 1000
            } seconds before retry (attempt ${attempts}/${maxAttempts})...`
          );
          await new Promise((resolve) => setTimeout(resolve, waitTime));
        } else {
          logger.warn(
            `${contractName} is still not whitelisted after ${maxAttempts} attempts. Proceeding anyway, but paymaster transactions might fail.`
          );
        }
      }
    } catch (error) {
      logger.error(`Error checking whitelist status: ${error}`);
      attempts++;
      if (attempts < maxAttempts) {
        await new Promise((resolve) => setTimeout(resolve, waitTime));
      }
    }
  }
}
```
