Live Preview
Template Structure
Smart Contract Template Library — Production-Tested Solidity Patterns
These templates represent the baseline architecture we start every project from. They are not copy-paste ready — every production contract requires specification, testing, and independent audit.
Format
Document
Sections
2
Format
Document
Status
Ready to customize
Template 1: Standard ERC-20 Token
Use for: Protocol governance tokens, utility tokens with on-chain voting. Required audit scope: Supply cap enforcement, vote checkpoint correctness, permit signature security.
Template 2: NFT Minting Contract (ERC-721A)
Template Guide
How to use this template
Template Overview
These templates represent the baseline architecture we start every project from. They are not copy paste ready — every production contract requires specification, testing, and independent audit.
Template 1: Standard ERC-20 Token
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
contract GovernanceToken is ERC20, Ownable, ERC20Permit, ERC20Votes {
uint256 public constant MAX_SUPPLY = 100_000_000 * 10**18;
constructor(address initialOwner)
ERC20("GovernanceToken", "GOV")
Ownable(initialOwner)
ERC20Permit("GovernanceToken")
{}
function mint(address to, uint256 amount) external onlyOwner {
require(totalSupply() + amount <= MAX_SUPPLY, "Exceeds max supply");
_mint(to, amount);
}
function _update(address from, address to, uint256 value)
internal override(ERC20, ERC20Votes) {
super._update(from, to, value);
}
function nonces(address owner)
public view override(ERC20Permit, Nonces) returns (uint256) {
return super.nonces(owner);
}
}Use for: Protocol governance tokens, utility tokens with on-chain voting. Required audit scope: Supply cap enforcement, vote checkpoint correctness, permit signature security.
Template 2: NFT Minting Contract (ERC-721A)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "erc721a/contracts/ERC721A.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
contract NFTCollection is ERC721A, Ownable {
uint256 public constant MAX_SUPPLY = 10000;
uint256 public constant ALLOWLIST_PRICE = 0.06 ether;
uint256 public constant PUBLIC_PRICE = 0.08 ether;
uint256 public constant MAX_PER_WALLET = 5;
bytes32 public merkleRoot;
string private baseURI;
bool public revealed;
string private hiddenURI;
enum Phase { CLOSED, ALLOWLIST, PUBLIC }
Phase public currentPhase;
constructor(address initialOwner) ERC721A("Collection", "COL") Ownable(initialOwner) {}
function allowlistMint(
uint256 quantity,
bytes32[] calldata proof
) external payable {
require(currentPhase == Phase.ALLOWLIST, "Allowlist not active");
require(msg.value >= ALLOWLIST_PRICE * quantity, "Insufficient payment");
require(totalSupply() + quantity <= MAX_SUPPLY, "Exceeds max supply");
require(_numberMinted(msg.sender) + quantity <= MAX_PER_WALLET, "Exceeds per-wallet limit");
bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
require(MerkleProof.verify(proof, merkleRoot, leaf), "Not on allowlist");
_mint(msg.sender, quantity);
}
function publicMint(uint256 quantity) external payable {
require(currentPhase == Phase.PUBLIC, "Public mint not active");
require(msg.value >= PUBLIC_PRICE * quantity, "Insufficient payment");
require(totalSupply() + quantity <= MAX_SUPPLY, "Exceeds max supply");
require(_numberMinted(msg.sender) + quantity <= MAX_PER_WALLET, "Exceeds per-wallet limit");
_mint(msg.sender, quantity);
}
function setPhase(Phase phase) external onlyOwner { currentPhase = phase; }
function setMerkleRoot(bytes32 root) external onlyOwner { merkleRoot = root; }
function reveal(string calldata uri) external onlyOwner {
revealed = true;
baseURI = uri;
}
function _baseURI() internal view override returns (string memory) {
return revealed ? baseURI : hiddenURI;
}
function withdraw() external onlyOwner {
(bool success, ) = payable(owner()).call{value: address(this).balance}("");
require(success, "Withdrawal failed");
}
}