Deploying EVM Contracts with Foundry

Deploy an EVM smart contract using Foundry for compilation and a TypeScript script integrating the Fordefi Web3 Provider for signing transactions with your Fordefi EVM Vault.

The deployment process involves:

  • Foundry for contract compilation (forge build)
  • TypeScript for the deployment script
  • Fordefi Web3 Provider for transaction handling
  • A Fordefi vault as the signer

Prerequisites

Ensure the following are installed:

  1. Node.js and npm
  2. TypeScript:
    npm install -g typescript
  3. Foundry. (Installation Guide).
  4. Fordefi API Signer and User:

Installation

Install all dependencies:

npm install --save-dev typescript ts-node @types/node
npm install ethers @fordefi/web3-provider dotenv

Environment variables

Create a .env file:

FORDEFI_API_USER_TOKEN=your_token_here

Store the Fordefi API Signer private key in ./fordefi_secret/private.pem.

TypeScript configuration

Create tsconfig.json:

{
  "compilerOptions": {
    "target": "es2020",
    "module": "commonjs",
    "esModuleInterop": true,
    "strict": true,
    "resolveJsonModule": true
  }
}

Deploy the contract

Create script/deploy.ts:

import fs from "fs";
import path from "path";
import dotenv from "dotenv";
dotenv.config();
import { FordefiWeb3Provider, FordefiProviderConfig } from "@fordefi/web3-provider";
import { ethers } from "ethers";


// 1. Configure your Fordefi secrets
const FORDEFI_API_USER_TOKEN = process.env.FORDEFI_API_USER_TOKEN ?? 
  (() => { throw new Error("FORDEFI_API_USER_TOKEN is not set"); })();
const privateKeyFilePath = "./fordefi_secret/private.pem";
const PEM_PRIVATE_KEY = fs.readFileSync(privateKeyFilePath, "utf8") ??
  (() => { throw new Error("PEM_PRIVATE_KEY is not set"); })();


// 2. Chain ID configuration
//    Example: deploying on Polygon Mainnet (chainId=137)
const chainId = 137;


// 3. Construct FordefiWeb3Provider config
const config: FordefiProviderConfig = {
  chainId,
  address: "0x...", // Your Fordefi EVM Vault address
  apiUserToken: FORDEFI_API_USER_TOKEN,
  apiPayloadSignKey: PEM_PRIVATE_KEY,
  rpcUrl: "https://polygon-rpc.com/" // Fallback RPC
};

async function main() {
  // A) Create the Fordefi provider
  const fordefiProvider = new FordefiWeb3Provider(config);

  // B) Wrap the fordefiProvider with Ethers.js and get the signer
  const provider = new ethers.BrowserProvider(fordefiProvider);
  const signer = await provider.getSigner();

  // C) Load the Foundry artifact from `out/Counter.sol/Counter.json`
  const lockArtifactPath = path.join(__dirname, "..", "out", "Counter.sol", "Counter.json");
  const lockArtifact = JSON.parse(fs.readFileSync(lockArtifactPath, "utf8"));

  // D) Get Foundry bytecode from `artifact.bytecode.object`,
  const abi = lockArtifact.abi;
  let bytecode = lockArtifact.bytecode;
  if (bytecode && bytecode.object) {
    bytecode = bytecode.object;
  }

  // E) Deploy with Ethers
  const factory = new ethers.ContractFactory(abi, bytecode, signer);
  console.log("Deploying contract...");
  const lock = await factory.deploy();

  console.log("Contract deployed to:", await lock.getAddress());
}

main()
  .then(() => process.exit(0))
  .catch((err) => {
    console.error("Error deploying contract:", err);
    process.exit(1);
  });

Compile your contract:

forge build

Deploy your contract:

npx ts-node script/deploy.ts

You're done!