Skip to content

Deployment

Brandon Brooks edited this page Jan 22, 2026 · 1 revision

Deployment Guide

This guide covers deploying ContractKit projects to local and test networks.

Local Deployment (Anvil)

1. Start Local Node

/contractkit:local

Or manually:

anvil

Anvil provides:

  • 10 pre-funded accounts (10,000 ETH each)
  • Instant block mining
  • RPC at http://127.0.0.1:8545

2. Deploy

/contractkit:deploy local

Or manually:

forge script script/Deploy.s.sol --rpc-url http://127.0.0.1:8545 --broadcast --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

3. Verify Deployment

/contractkit:call <address> name()

Testnet Deployment (Sepolia)

Prerequisites

  1. Sepolia ETH - Get from a faucet:

  2. RPC URL - From a provider:

  3. Private Key - Export from your wallet (never share this!)

Environment Setup

Create a .env file (add to .gitignore!):

PRIVATE_KEY=0x...your_private_key...
SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/your-api-key

Load environment:

source .env

Deploy to Sepolia

/contractkit:deploy sepolia

Or manually:

forge script script/Deploy.s.sol \
  --rpc-url $SEPOLIA_RPC_URL \
  --broadcast \
  --private-key $PRIVATE_KEY \
  --verify

Verify on Etherscan

If not auto-verified, manually verify:

forge verify-contract <address> src/Token.sol:Token \
  --chain sepolia \
  --etherscan-api-key $ETHERSCAN_API_KEY

Deployment Scripts

Each template includes deployment scripts in script/:

Deploy.s.sol

Basic deployment script:

contract DeployScript is Script {
    function run() public {
        vm.startBroadcast();

        Token token = new Token("MyToken", "MTK");
        console.log("Token deployed at:", address(token));

        vm.stopBroadcast();
    }
}

Interact.s.sol

Post-deployment interactions:

contract InteractScript is Script {
    function run() public {
        address tokenAddress = vm.envAddress("TOKEN_ADDRESS");
        Token token = Token(tokenAddress);

        vm.startBroadcast();

        // Grant minter role
        token.grantRole(token.MINTER_ROLE(), msg.sender);

        // Mint tokens
        token.mint(msg.sender, 1000 ether);

        vm.stopBroadcast();
    }
}

Run with:

TOKEN_ADDRESS=0x... forge script script/Interact.s.sol --rpc-url $RPC_URL --broadcast --private-key $PRIVATE_KEY

Deployment Checklist

Before deploying to mainnet:

Security

  • Independent security audit completed
  • All tests passing
  • No compiler warnings
  • Reviewed THREAT_MODEL.md
  • Checked SECURITY.md considerations

Configuration

  • Constructor parameters verified
  • Access control roles planned
  • Initial token distribution planned (if applicable)
  • Upgrade path considered (if applicable)

Operations

  • Deployment wallet secured (hardware wallet recommended)
  • Sufficient ETH for gas
  • Etherscan verification planned
  • Monitoring/alerting set up
  • Incident response plan ready

Post-Deployment

  • Contract verified on block explorer
  • Initial roles granted
  • Functionality tested on mainnet
  • Documentation updated with addresses

Broadcast Logs

Foundry saves deployment records in broadcast/:

broadcast/
└── Deploy.s.sol/
    ├── 31337/           # Local (Anvil)
    │   └── run-latest.json
    └── 11155111/        # Sepolia
        └── run-latest.json

Each log contains:

  • Transaction hashes
  • Contract addresses
  • Gas used
  • Timestamps

Network Reference

Network Chain ID RPC
Anvil (local) 31337 http://127.0.0.1:8545
Sepolia 11155111 Provider URL
Mainnet 1 Provider URL

Troubleshooting

"Insufficient funds"

  • Check wallet balance
  • Get testnet ETH from faucet

"Nonce too low"

  • Reset account in MetaMask, or
  • Wait for pending transactions

"Contract verification failed"

  • Ensure exact same compiler version
  • Check constructor arguments match
  • Try flattening: forge flatten src/Contract.sol

"Script failed"

  • Check RPC URL is correct
  • Verify private key format (0x prefix)
  • Ensure network is reachable