Smart Contract Template Generator
Generates secure Solidity smart contracts following OpenZeppelin standards and best practices.
When to Use
-
"Create an ERC-20 token"
-
"Generate NFT contract"
-
"Smart contract template"
-
"Solidity contract with security"
-
"Create DAO contract"
Instructions
- Determine Contract Type
Ask user which type:
-
ERC-20 (Fungible Token)
-
ERC-721 (NFT - Non-Fungible Token)
-
ERC-1155 (Multi-Token)
-
ERC-4626 (Tokenized Vault)
-
Custom contract
- Generate Contracts
ERC-20 Token
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
contract MyToken is ERC20, ERC20Burnable, ERC20Pausable, Ownable, ERC20Permit { constructor(address initialOwner) ERC20("MyToken", "MTK") Ownable(initialOwner) ERC20Permit("MyToken") { _mint(msg.sender, 1000000 * 10 ** decimals()); }
function pause() public onlyOwner {
_pause();
}
function unpause() public onlyOwner {
_unpause();
}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
// Required override
function _update(address from, address to, uint256 value)
internal
override(ERC20, ERC20Pausable)
{
super._update(from, to, value);
}
}
ERC-721 NFT
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Pausable.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
contract MyNFT is ERC721, ERC721Enumerable, ERC721URIStorage, ERC721Pausable, Ownable, ERC721Burnable { uint256 private _nextTokenId; uint256 public constant MAX_SUPPLY = 10000; uint256 public constant MINT_PRICE = 0.05 ether;
constructor(address initialOwner)
ERC721("MyNFT", "MNFT")
Ownable(initialOwner)
{}
function pause() public onlyOwner {
_pause();
}
function unpause() public onlyOwner {
_unpause();
}
function safeMint(address to, string memory uri) public payable {
require(_nextTokenId < MAX_SUPPLY, "Max supply reached");
require(msg.value >= MINT_PRICE, "Insufficient payment");
uint256 tokenId = _nextTokenId++;
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}
function withdraw() public onlyOwner {
uint256 balance = address(this).balance;
payable(owner()).transfer(balance);
}
// Required overrides
function _update(address to, uint256 tokenId, address auth)
internal
override(ERC721, ERC721Enumerable, ERC721Pausable)
returns (address)
{
return super._update(to, tokenId, auth);
}
function _increaseBalance(address account, uint128 value)
internal
override(ERC721, ERC721Enumerable)
{
super._increaseBalance(account, value);
}
function tokenURI(uint256 tokenId)
public
view
override(ERC721, ERC721URIStorage)
returns (string memory)
{
return super.tokenURI(tokenId);
}
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721, ERC721Enumerable, ERC721URIStorage)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
}
ERC-1155 Multi-Token
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Pausable.sol"; import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol";
contract MyMultiToken is ERC1155, Ownable, ERC1155Pausable, ERC1155Supply { constructor(address initialOwner) ERC1155("https://api.example.com/token/{id}.json") Ownable(initialOwner) {}
function setURI(string memory newuri) public onlyOwner {
_setURI(newuri);
}
function pause() public onlyOwner {
_pause();
}
function unpause() public onlyOwner {
_unpause();
}
function mint(address account, uint256 id, uint256 amount, bytes memory data)
public
onlyOwner
{
_mint(account, id, amount, data);
}
function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data)
public
onlyOwner
{
_mintBatch(to, ids, amounts, data);
}
// Required overrides
function _update(address from, address to, uint256[] memory ids, uint256[] memory values)
internal
override(ERC1155, ERC1155Pausable, ERC1155Supply)
{
super._update(from, to, ids, values);
}
}
- Security Patterns
Reentrancy Protection:
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract SecureContract is ReentrancyGuard { function withdraw() public nonReentrant { uint amount = balances[msg.sender]; balances[msg.sender] = 0; (bool success, ) = msg.sender.call{value: amount}(""); require(success, "Transfer failed"); } }
Access Control:
import "@openzeppelin/contracts/access/AccessControl.sol";
contract MyContract is AccessControl { bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(MINTER_ROLE, msg.sender);
}
function mint(address to) public onlyRole(MINTER_ROLE) {
// Minting logic
}
}
Pull Over Push:
// ❌ BAD: Push pattern (vulnerable) function distribute() public { for (uint i = 0; i < recipients.length; i++) { recipients[i].transfer(amounts[i]); } }
// ✅ GOOD: Pull pattern (secure) mapping(address => uint) public pendingWithdrawals;
function withdraw() public { uint amount = pendingWithdrawals[msg.sender]; pendingWithdrawals[msg.sender] = 0; payable(msg.sender).transfer(amount); }
- Gas Optimization
// Use uint256 instead of smaller uints (saves gas) uint256 public count; // ✅
// Cache array length for (uint256 i = 0; i < array.length; i++) // ❌ uint256 length = array.length; for (uint256 i = 0; i < length; i++) // ✅
// Use unchecked for gas savings (when safe) unchecked { counter++; }
// Immutable for constants uint256 public immutable MAX_SUPPLY;
- Testing Setup
Hardhat:
// test/MyToken.test.js const { expect } = require("chai"); const { ethers } = require("hardhat");
describe("MyToken", function () { let token; let owner; let addr1;
beforeEach(async function () { [owner, addr1] = await ethers.getSigners();
const MyToken = await ethers.getContractFactory("MyToken");
token = await MyToken.deploy(owner.address);
});
it("Should assign total supply to owner", async function () { const ownerBalance = await token.balanceOf(owner.address); expect(await token.totalSupply()).to.equal(ownerBalance); });
it("Should transfer tokens", async function () { await token.transfer(addr1.address, 50); expect(await token.balanceOf(addr1.address)).to.equal(50); }); });
- Deployment Script
// scripts/deploy.js const hre = require("hardhat");
async function main() { const [deployer] = await hre.ethers.getSigners();
console.log("Deploying with account:", deployer.address);
const MyToken = await hre.ethers.getContractFactory("MyToken"); const token = await MyToken.deploy(deployer.address);
await token.waitForDeployment();
console.log("Token deployed to:", await token.getAddress());
// Verify on Etherscan if (network.name !== "hardhat") { await hre.run("verify:verify", { address: await token.getAddress(), constructorArguments: [deployer.address], }); } }
main().catch((error) => { console.error(error); process.exitCode = 1; });
- Configuration Files
hardhat.config.js:
require("@nomicfoundation/hardhat-toolbox"); require("dotenv").config();
module.exports = { solidity: { version: "0.8.20", settings: { optimizer: { enabled: true, runs: 200, }, }, }, networks: { sepolia: { url: process.env.SEPOLIA_RPC_URL, accounts: [process.env.PRIVATE_KEY], }, mainnet: { url: process.env.MAINNET_RPC_URL, accounts: [process.env.PRIVATE_KEY], }, }, etherscan: { apiKey: process.env.ETHERSCAN_API_KEY, }, };
- Best Practices
-
Use latest Solidity version
-
Import from OpenZeppelin
-
Add comprehensive tests (>90% coverage)
-
Use Slither for static analysis
-
Get audited before mainnet
-
Use multi-sig for ownership
-
Implement pause mechanism
-
Follow checks-effects-interactions pattern
-
Document all functions with NatSpec
-
Version control and CI/CD
- Audit Checklist
-
Reentrancy protection
-
Integer overflow/underflow (use 0.8.0+)
-
Access control properly implemented
-
No unchecked external calls
-
Gas limits considered
-
Front-running mitigation
-
Timestamp dependence avoided
-
Randomness source secure
-
Upgrade mechanism (if proxy)
-
Emergency pause function
- Documentation Template
/**
- @title MyToken
- @dev Implementation of ERC-20 token with additional features
- @custom:security-contact security@example.com */
/**
- @notice Mints new tokens
- @dev Only callable by owner
- @param to Address to receive tokens
- @param amount Amount of tokens to mint */ function mint(address to, uint256 amount) public onlyOwner { _mint(to, amount); }
Installation
Initialize project
npm init -y npm install --save-dev hardhat @openzeppelin/contracts
Initialize Hardhat
npx hardhat init
Install dependencies
npm install --save-dev @nomicfoundation/hardhat-toolbox
Run tests
npx hardhat test
Deploy
npx hardhat run scripts/deploy.js --network sepolia