Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Generates Solidity smart contracts with security best practices (ERC-20, ERC-721, ERC-1155, custom). Use when user asks to "create smart contract", "solidity contract", "erc20 token", "nft contract", or "web3 contract".
.claude/skills/microck-smart-contract-generator/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 134% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 77% | 0% |
| case-06 | ✓→✓ | = Same ✓ | 84% | 0% |
| case-07 | ✓→✓ | = Same ✓ | 94% | 0% |
| case-08 | ✓→✓ | = Same ✓ | 116% | 0% |
Generates secure Solidity smart contracts following OpenZeppelin standards and best practices.
Ask user which type:
solidity// 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); } }
solidity// 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); } }
solidity// 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); } }
Reentrancy Protection:
solidityimport "@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:
solidityimport "@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:
solidity// ❌ 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); }
solidity// 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;
Hardhat:
javascript// 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); }); });
javascript// 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; });
hardhat.config.js:
javascriptrequire("@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, }, };
solidity/** * @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); }
bash# 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
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 12,717 | 13,844 | +9% | 1 | 1 | 0% | 2,666 | 6,230 | +134% | 0 | 0 | — |
case-02 | fail→fail | 29,695 | 16,593 | -44% | 1 | 1 | 0% | 3,564 | 6,786 | +90% | 0 | 0 | — |
case-03 | pass→pass | 20,350 | 16,850 | -17% | 1 | 1 | 0% | 3,740 | 6,604 | +77% | 0 | 0 | — |
case-04 | fail→fail | 11,485 | 9,949 | -13% | 1 | 1 | 0% | 2,125 | 5,146 | +142% | 0 | 0 | — |
case-05 | fail→fail | 12,221 | 13,737 | +12% | 1 | 1 | 0% | 2,386 | 6,020 | +152% | 0 | 0 | — |
case-06 | pass→pass | 22,184 | 21,823 | -2% | 1 | 1 | 0% | 3,914 | 7,192 | +84% | 0 | 0 | — |
case-07 | pass→pass | 14,708 | 10,083 | -31% | 1 | 1 | 0% | 2,639 | 5,131 | +94% | 0 | 0 | — |
case-08 | pass→pass | 10,894 | 8,561 | -21% | 1 | 1 | 0% | 2,288 | 4,938 | +116% | 0 | 0 | — |
case-09 | pass→pass | 12,907 | 10,697 | -17% | 1 | 1 | 0% | 2,372 | 5,616 | +137% | 0 | 0 | — |
case-10 | pass→pass | 12,973 | 11,369 | -12% | 1 | 1 | 0% | 2,423 | 5,819 | +140% | 0 | 0 | — |
case-11 | pass→pass | 11,492 | 11,636 | +1% | 1 | 1 | 0% | 2,476 | 5,484 | +121% | 0 | 0 | — |
case-21 | pass→pass | 12,193 | 10,056 | -18% | 1 | 1 | 0% | 2,276 | 5,167 | +127% | 0 | 0 | — |
case-12 | pass→pass | 7,507 | 17,249 | +130% | 1 | 1 | 0% | 1,628 | 5,897 | +262% | 0 | 0 | — |
case-13 | pass→pass | 10,625 | 13,033 | +23% | 1 | 1 | 0% | 2,572 | 5,965 | +132% | 0 | 0 | — |
case-14 | pass→pass | 12,675 | 9,067 | -28% | 1 | 1 | 0% | 2,225 | 5,016 | +125% | 0 | 0 | — |
case-15 | pass→pass | 19,584 | 19,561 | -0% | 1 | 1 | 0% | 3,410 | 6,409 | +88% | 0 | 0 | — |
case-22 | pass→pass | 10,378 | 9,966 | -4% | 1 | 1 | 0% | 2,127 | 5,384 | +153% | 0 | 0 | — |
case-16 | pass→pass | 5,381 | 4,944 | -8% | 1 | 1 | 0% | 1,022 | 4,016 | +293% | 0 | 0 | — |
case-17 | pass→pass | 8,488 | 9,584 | +13% | 1 | 1 | 0% | 1,564 | 5,017 | +221% | 0 | 0 | — |
case-18 | pass→pass | 15,871 | 10,265 | -35% | 1 | 1 | 0% | 3,413 | 5,649 | +66% | 0 | 0 | — |
case-19 | pass→pass | 9,646 | 8,843 | -8% | 1 | 1 | 0% | 2,059 | 5,145 | +150% | 0 | 0 | — |
case-20 | pass→pass | 14,681 | 11,480 | -22% | 1 | 1 | 0% | 3,309 | 5,781 | +75% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +5 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.