Blockchain Developer Examples

Externalized from the agent definition per the few-shot-examples rule (#1587).

Blockchain Developer — Worked Examples

Externalized from the agent definition per the few-shot-examples rule (#1587).

Your Process

1. ERC-20 Token Implementation

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import {ERC20Votes} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {Nonces} from "@openzeppelin/contracts/utils/Nonces.sol";

/// @title GovernanceToken
/// @notice ERC-20 with gasless permits (EIP-2612) and on-chain voting delegation
contract GovernanceToken is ERC20, ERC20Permit, ERC20Votes, Ownable2Step {
    uint256 public constant MAX_SUPPLY = 1_000_000_000e18; // 1 billion tokens

    error ExceedsMaxSupply(uint256 requested, uint256 available);

    constructor(address initialOwner)
        ERC20("Governance Token", "GOV")
        ERC20Permit("Governance Token")
        Ownable(initialOwner)
    {}

    /// @notice Mint tokens; owner-only, capped at MAX_SUPPLY
    function mint(address to, uint256 amount) external onlyOwner {
        uint256 available = MAX_SUPPLY - totalSupply();
        if (amount > available) revert ExceedsMaxSupply(amount, available);
        _mint(to, amount);
    }

    // Required overrides for ERC20Votes
    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);
    }
}

2. ERC-721 NFT with Royalties and Allowlist

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {ERC721A} from "erc721a/contracts/ERC721A.sol";
import {ERC2981} from "@openzeppelin/contracts/token/common/ERC2981.sol";
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

/// @title NFTCollection
/// @notice Gas-efficient NFT collection using ERC721A with merkle allowlist and EIP-2981 royalties
contract NFTCollection is ERC721A, ERC2981, Ownable2Step, ReentrancyGuard {
    using Strings for uint256;

    uint256 public constant MAX_SUPPLY = 10_000;
    uint256 public constant ALLOWLIST_PRICE = 0.05 ether;
    uint256 public constant PUBLIC_PRICE = 0.08 ether;
    uint256 public constant MAX_PER_WALLET = 5;

    bytes32 public merkleRoot;
    string private _baseTokenURI;
    string private _unrevealedURI;

    bool public revealed;
    bool public allowlistActive;
    bool public publicActive;

    mapping(address => uint256) public allowlistMinted;

    event Revealed(string baseURI);
    event MerkleRootUpdated(bytes32 newRoot);

    error InvalidProof();
    error AllowlistNotActive();
    error PublicNotActive();
    error ExceedsMaxSupply();
    error ExceedsWalletLimit();
    error InsufficientPayment(uint256 sent, uint256 required);
    error WithdrawFailed();

    constructor(
        address initialOwner,
        bytes32 _merkleRoot,
        string memory unrevealedURI,
        address royaltyReceiver
    ) ERC721A("NFT Collection", "NFTC") Ownable(initialOwner) {
        merkleRoot = _merkleRoot;
        _unrevealedURI = unrevealedURI;
        _setDefaultRoyalty(royaltyReceiver, 500); // 5% royalty
    }

    /// @notice Mint for allowlisted addresses
    function allowlistMint(uint256 quantity, bytes32[] calldata proof)
        external
        payable
        nonReentrant
    {
        if (!allowlistActive) revert AllowlistNotActive();
        if (_totalMinted() + quantity > MAX_SUPPLY) revert ExceedsMaxSupply();
        if (allowlistMinted[msg.sender] + quantity > MAX_PER_WALLET) revert ExceedsWalletLimit();

        bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
        if (!MerkleProof.verifyCalldata(proof, merkleRoot, leaf)) revert InvalidProof();

        uint256 cost = ALLOWLIST_PRICE * quantity;
        if (msg.value < cost) revert InsufficientPayment(msg.value, cost);

        allowlistMinted[msg.sender] += quantity;
        _mint(msg.sender, quantity);

        // Refund overpayment
        if (msg.value > cost) {
            (bool ok,) = msg.sender.call{value: msg.value - cost}("");
            require(ok);
        }
    }

    /// @notice Public mint
    function publicMint(uint256 quantity) external payable nonReentrant {
        if (!publicActive) revert PublicNotActive();
        if (_totalMinted() + quantity > MAX_SUPPLY) revert ExceedsMaxSupply();

        uint256 cost = PUBLIC_PRICE * quantity;
        if (msg.value < cost) revert InsufficientPayment(msg.value, cost);

        _mint(msg.sender, quantity);

        if (msg.value > cost) {
            (bool ok,) = msg.sender.call{value: msg.value - cost}("");
            require(ok);
        }
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override
        returns (string memory)
    {
        if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
        if (!revealed) return _unrevealedURI;
        return string(abi.encodePacked(_baseTokenURI, tokenId.toString(), ".json"));
    }

    // Admin functions
    function reveal(string calldata baseURI) external onlyOwner {
        _baseTokenURI = baseURI;
        revealed = true;
        emit Revealed(baseURI);
    }

    function setMerkleRoot(bytes32 newRoot) external onlyOwner {
        merkleRoot = newRoot;
        emit MerkleRootUpdated(newRoot);
    }

    function toggleAllowlist() external onlyOwner { allowlistActive = !allowlistActive; }
    function togglePublic() external onlyOwner { publicActive = !publicActive; }

    function withdraw() external onlyOwner nonReentrant {
        (bool ok,) = owner().call{value: address(this).balance}("");
        if (!ok) revert WithdrawFailed();
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721A, ERC2981)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}

3. Foundry Test Suite with Fuzzing

// test/NFTCollection.t.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {Test, console2} from "forge-std/Test.sol";
import {NFTCollection} from "../src/NFTCollection.sol";
import {Merkle} from "murky/Merkle.sol";

contract NFTCollectionTest is Test {
    NFTCollection nft;
    Merkle merkle;
    address owner = makeAddr("owner");
    address alice = makeAddr("alice");
    address bob = makeAddr("bob");
    address royaltyReceiver = makeAddr("royalty");

    bytes32 merkleRoot;
    bytes32[] aliceProof;

    function setUp() public {
        merkle = new Merkle();

        // Build allowlist merkle tree
        bytes32[] memory leaves = new bytes32[](2);
        leaves[0] = keccak256(abi.encodePacked(alice));
        leaves[1] = keccak256(abi.encodePacked(bob));
        merkleRoot = merkle.getRoot(leaves);
        aliceProof = merkle.getProof(leaves, 0);

        vm.prank(owner);
        nft = new NFTCollection(owner, merkleRoot, "ipfs://unrevealed", royaltyReceiver);

        // Fund test addresses
        vm.deal(alice, 10 ether);
        vm.deal(bob, 10 ether);
    }

    function test_AllowlistMint() public {
        vm.prank(owner);
        nft.toggleAllowlist();

        vm.prank(alice);
        nft.allowlistMint{value: 0.05 ether}(1, aliceProof);

        assertEq(nft.balanceOf(alice), 1);
        assertEq(nft.allowlistMinted(alice), 1);
    }

    function test_RevertWhen_InvalidMerkleProof() public {
        vm.prank(owner);
        nft.toggleAllowlist();

        address attacker = makeAddr("attacker");
        vm.deal(attacker, 1 ether);

        vm.prank(attacker);
        vm.expectRevert(NFTCollection.InvalidProof.selector);
        nft.allowlistMint{value: 0.05 ether}(1, aliceProof);
    }

    function test_RevertWhen_AllowlistNotActive() public {
        vm.prank(alice);
        vm.expectRevert(NFTCollection.AllowlistNotActive.selector);
        nft.allowlistMint{value: 0.05 ether}(1, aliceProof);
    }

    function test_RevealChangesTokenURI() public {
        vm.prank(owner);
        nft.toggleAllowlist();
        vm.prank(alice);
        nft.allowlistMint{value: 0.05 ether}(1, aliceProof);

        assertEq(nft.tokenURI(0), "ipfs://unrevealed");

        vm.prank(owner);
        nft.reveal("ipfs://QmRevealedHash/");

        assertEq(nft.tokenURI(0), "ipfs://QmRevealedHash/0.json");
    }

    // Fuzz: any valid quantity within limits should mint successfully
    function testFuzz_PublicMintQuantity(uint8 quantity) public {
        quantity = uint8(bound(quantity, 1, 5));
        uint256 cost = 0.08 ether * quantity;
        vm.deal(alice, cost);

        vm.prank(owner);
        nft.togglePublic();

        vm.prank(alice);
        nft.publicMint{value: cost}(quantity);

        assertEq(nft.balanceOf(alice), quantity);
    }

    // Invariant: total supply never exceeds MAX_SUPPLY
    function invariant_TotalSupplyNeverExceedsMax() public view {
        assertLe(nft.totalSupply(), nft.MAX_SUPPLY());
    }
}
// Hardhat test suite (for teams preferring JavaScript toolchain)
// test/GovernanceToken.test.js
const { expect } = require("chai");
const { ethers } = require("hardhat");
const { loadFixture } = require("@nomicfoundation/hardhat-toolbox/network-helpers");

describe("GovernanceToken", function () {
  async function deployFixture() {
    const [owner, alice, bob] = await ethers.getSigners();
    const Token = await ethers.getContractFactory("GovernanceToken");
    const token = await Token.deploy(owner.address);
    return { token, owner, alice, bob };
  }

  it("mints up to MAX_SUPPLY", async function () {
    const { token, owner, alice } = await loadFixture(deployFixture);
    const MAX_SUPPLY = await token.MAX_SUPPLY();

    await token.connect(owner).mint(alice.address, MAX_SUPPLY);
    expect(await token.totalSupply()).to.equal(MAX_SUPPLY);
  });

  it("reverts when minting beyond MAX_SUPPLY", async function () {
    const { token, owner, alice } = await loadFixture(deployFixture);
    const MAX_SUPPLY = await token.MAX_SUPPLY();

    await token.connect(owner).mint(alice.address, MAX_SUPPLY);

    await expect(
      token.connect(owner).mint(alice.address, 1n)
    ).to.be.revertedWithCustomError(token, "ExceedsMaxSupply");
  });

  it("supports EIP-2612 permit", async function () {
    const { token, owner, alice, bob } = await loadFixture(deployFixture);
    await token.connect(owner).mint(alice.address, ethers.parseEther("1000"));

    const amount = ethers.parseEther("100");
    const deadline = Math.floor(Date.now() / 1000) + 3600;
    const nonce = await token.nonces(alice.address);

    const domain = {
      name: "Governance Token",
      version: "1",
      chainId: (await ethers.provider.getNetwork()).chainId,
      verifyingContract: await token.getAddress(),
    };

    const types = {
      Permit: [
        { name: "owner", type: "address" },
        { name: "spender", type: "address" },
        { name: "value", type: "uint256" },
        { name: "nonce", type: "uint256" },
        { name: "deadline", type: "uint256" },
      ],
    };

    const signature = await alice.signTypedData(domain, types, {
      owner: alice.address,
      spender: bob.address,
      value: amount,
      nonce,
      deadline,
    });
    const { v, r, s } = ethers.Signature.from(signature);

    await token.permit(alice.address, bob.address, amount, deadline, v, r, s);
    expect(await token.allowance(alice.address, bob.address)).to.equal(amount);
  });
});

4. Security Auditing Patterns

Reentrancy — Checks-Effects-Interactions

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

contract SecureVault is ReentrancyGuard {
    mapping(address => uint256) public balances;

    // VULNERABLE pattern — DO NOT USE
    function withdraw_UNSAFE(uint256 amount) external {
        require(balances[msg.sender] >= amount, "Insufficient balance");
        // External call happens BEFORE state update — attacker can re-enter
        (bool ok,) = msg.sender.call{value: amount}("");
        require(ok, "Transfer failed");
        balances[msg.sender] -= amount; // Too late: attacker already re-entered here
    }

    // SECURE pattern: CEI + ReentrancyGuard
    function withdraw(uint256 amount) external nonReentrant {
        if (balances[msg.sender] < amount) revert InsufficientBalance(amount);
        // Check (already done) → Effect → Interaction
        balances[msg.sender] -= amount;  // State updated FIRST
        (bool ok,) = msg.sender.call{value: amount}("");
        require(ok, "Transfer failed");
    }

    // Pull-over-push: safer than pushing funds to arbitrary addresses
    function claimReward() external nonReentrant {
        uint256 reward = pendingRewards[msg.sender];
        if (reward == 0) revert NothingToClaim();
        pendingRewards[msg.sender] = 0;  // Zero before transfer
        (bool ok,) = msg.sender.call{value: reward}("");
        require(ok);
    }

    mapping(address => uint256) public pendingRewards;

    error InsufficientBalance(uint256 requested);
    error NothingToClaim();
}

Front-running and MEV Mitigation

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/// @notice Commit-reveal scheme prevents front-running on sensitive operations
contract CommitRevealAuction {
    struct Commitment {
        bytes32 hash;
        uint256 deposit;
        uint256 revealDeadline;
    }

    mapping(address => Commitment) public commitments;
    uint256 public constant COMMIT_PERIOD = 1 days;
    uint256 public constant REVEAL_PERIOD = 1 days;
    uint256 public immutable auctionStart;

    error TooEarlyToReveal();
    error CommitmentMismatch();
    error RevealWindowClosed();

    constructor() { auctionStart = block.timestamp; }

    /// @notice Step 1: commit a hash of (bid, salt) — no one can see your bid
    function commit(bytes32 commitHash) external payable {
        require(block.timestamp < auctionStart + COMMIT_PERIOD, "Commit period over");
        commitments[msg.sender] = Commitment({
            hash: commitHash,
            deposit: msg.value,
            revealDeadline: auctionStart + COMMIT_PERIOD + REVEAL_PERIOD,
        });
    }

    /// @notice Step 2: reveal bid with salt — verified against commitment
    function reveal(uint256 bidAmount, bytes32 salt) external {
        Commitment memory c = commitments[msg.sender];
        if (block.timestamp < auctionStart + COMMIT_PERIOD) revert TooEarlyToReveal();
        if (block.timestamp > c.revealDeadline) revert RevealWindowClosed();

        bytes32 expected = keccak256(abi.encodePacked(msg.sender, bidAmount, salt));
        if (expected != c.hash) revert CommitmentMismatch();

        // Process verified bid
        _processBid(msg.sender, bidAmount);
    }

    function _processBid(address bidder, uint256 amount) internal { /* ... */ }
}

/// @notice Slippage protection for DEX interactions prevents sandwich attacks
interface ISwapRouter {
    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 amountIn;
        uint256 amountOutMinimum;  // Slippage guard
        uint160 sqrtPriceLimitX96;
    }
    function exactInputSingle(ExactInputSingleParams calldata) external payable returns (uint256);
}

function swapWithSlippageProtection(
    address tokenIn,
    address tokenOut,
    uint256 amountIn,
    uint256 maxSlippageBps  // e.g., 50 = 0.5%
) internal returns (uint256 amountOut) {
    uint256 quotedOut = _getQuote(tokenIn, tokenOut, amountIn);
    uint256 minAmountOut = quotedOut * (10_000 - maxSlippageBps) / 10_000;

    amountOut = ISwapRouter(SWAP_ROUTER).exactInputSingle(
        ISwapRouter.ExactInputSingleParams({
            tokenIn: tokenIn,
            tokenOut: tokenOut,
            fee: 3000,
            recipient: address(this),
            amountIn: amountIn,
            amountOutMinimum: minAmountOut,  // Reverts if sandwich drops price too far
            sqrtPriceLimitX96: 0,
        })
    );
}

5. Rust Smart Contract on Solana (Anchor)

// programs/staking/src/lib.rs
// Solana staking program using Anchor framework

use anchor_lang::prelude::*;
use anchor_spl::token::{self, Token, TokenAccount, Transfer};

declare_id!("Stake11111111111111111111111111111111111111");

#[program]
pub mod staking {
    use super::*;

    pub fn initialize_pool(
        ctx: Context<InitializePool>,
        reward_rate_per_second: u64,
    ) -> Result<()> {
        let pool = &mut ctx.accounts.pool;
        pool.authority = ctx.accounts.authority.key();
        pool.stake_mint = ctx.accounts.stake_mint.key();
        pool.reward_mint = ctx.accounts.reward_mint.key();
        pool.reward_rate = reward_rate_per_second;
        pool.total_staked = 0;
        pool.last_update_time = Clock::get()?.unix_timestamp as u64;
        pool.reward_per_token_stored = 0;
        Ok(())
    }

    pub fn stake(ctx: Context<Stake>, amount: u64) -> Result<()> {
        require!(amount > 0, StakingError::ZeroAmount);

        let pool = &mut ctx.accounts.pool;
        let user_stake = &mut ctx.accounts.user_stake;
        let now = Clock::get()?.unix_timestamp as u64;

        // Update global reward accumulator
        pool.update_reward_per_token(now);

        // Settle pending rewards for this user before changing their stake
        let pending = user_stake.pending_rewards(pool.reward_per_token_stored);
        user_stake.rewards_earned = user_stake.rewards_earned
            .checked_add(pending)
            .ok_or(StakingError::ArithmeticOverflow)?;
        user_stake.reward_debt = pool.reward_per_token_stored;

        // Transfer tokens from user to pool vault
        let transfer_ctx = CpiContext::new(
            ctx.accounts.token_program.to_account_info(),
            Transfer {
                from: ctx.accounts.user_token_account.to_account_info(),
                to: ctx.accounts.vault.to_account_info(),
                authority: ctx.accounts.user.to_account_info(),
            },
        );
        token::transfer(transfer_ctx, amount)?;

        // Update state
        user_stake.amount = user_stake.amount
            .checked_add(amount)
            .ok_or(StakingError::ArithmeticOverflow)?;
        pool.total_staked = pool.total_staked
            .checked_add(amount)
            .ok_or(StakingError::ArithmeticOverflow)?;

        emit!(StakeEvent {
            user: ctx.accounts.user.key(),
            amount,
            total_staked: pool.total_staked,
        });

        Ok(())
    }

    pub fn claim_rewards(ctx: Context<ClaimRewards>) -> Result<()> {
        let pool = &mut ctx.accounts.pool;
        let user_stake = &mut ctx.accounts.user_stake;
        let now = Clock::get()?.unix_timestamp as u64;

        pool.update_reward_per_token(now);

        let pending = user_stake.pending_rewards(pool.reward_per_token_stored);
        let total_claimable = user_stake.rewards_earned
            .checked_add(pending)
            .ok_or(StakingError::ArithmeticOverflow)?;

        require!(total_claimable > 0, StakingError::NoRewardsToClaim);

        user_stake.rewards_earned = 0;
        user_stake.reward_debt = pool.reward_per_token_stored;

        // Transfer rewards from pool authority PDA
        let seeds = &[b"pool", pool.stake_mint.as_ref(), &[pool.bump]];
        let signer = &[&seeds[..]];
        let transfer_ctx = CpiContext::new_with_signer(
            ctx.accounts.token_program.to_account_info(),
            Transfer {
                from: ctx.accounts.reward_vault.to_account_info(),
                to: ctx.accounts.user_reward_account.to_account_info(),
                authority: pool.to_account_info(),
            },
            signer,
        );
        token::transfer(transfer_ctx, total_claimable)?;

        Ok(())
    }
}

#[account]
pub struct Pool {
    pub authority: Pubkey,
    pub stake_mint: Pubkey,
    pub reward_mint: Pubkey,
    pub reward_rate: u64,
    pub total_staked: u64,
    pub last_update_time: u64,
    pub reward_per_token_stored: u128,
    pub bump: u8,
}

impl Pool {
    pub fn update_reward_per_token(&mut self, now: u64) {
        if self.total_staked == 0 {
            self.last_update_time = now;
            return;
        }
        let elapsed = now.saturating_sub(self.last_update_time);
        let new_rewards = (self.reward_rate as u128)
            .saturating_mul(elapsed as u128)
            .saturating_mul(1_000_000_000)  // Precision scaling factor
            / self.total_staked as u128;
        self.reward_per_token_stored = self.reward_per_token_stored.saturating_add(new_rewards);
        self.last_update_time = now;
    }
}

#[account]
pub struct UserStake {
    pub pool: Pubkey,
    pub owner: Pubkey,
    pub amount: u64,
    pub reward_debt: u128,
    pub rewards_earned: u64,
}

impl UserStake {
    pub fn pending_rewards(&self, reward_per_token: u128) -> u64 {
        let earned = (self.amount as u128)
            .saturating_mul(reward_per_token.saturating_sub(self.reward_debt))
            / 1_000_000_000;
        earned as u64
    }
}

#[error_code]
pub enum StakingError {
    #[msg("Amount must be greater than zero")]
    ZeroAmount,
    #[msg("Arithmetic overflow")]
    ArithmeticOverflow,
    #[msg("No rewards to claim")]
    NoRewardsToClaim,
}

#[event]
pub struct StakeEvent {
    pub user: Pubkey,
    pub amount: u64,
    pub total_staked: u64,
}

6. DeFi Integration and L2 Scaling

DeFi: Uniswap V3 liquidity management

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {INonfungiblePositionManager} from "@uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol";
import {IUniswapV3Pool} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/// @notice Manages a single concentrated liquidity position on Uniswap V3
contract LiquidityManager {
    using SafeERC20 for IERC20;

    INonfungiblePositionManager public immutable positionManager;
    address public immutable token0;
    address public immutable token1;
    uint24 public immutable fee;

    uint256 public positionTokenId;

    event LiquidityAdded(uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
    event FeesCollected(uint256 amount0, uint256 amount1);

    constructor(
        address _positionManager,
        address _token0,
        address _token1,
        uint24 _fee
    ) {
        positionManager = INonfungiblePositionManager(_positionManager);
        token0 = _token0;
        token1 = _token1;
        fee = _fee;
    }

    /// @notice Provide liquidity in a price range around current price
    function addLiquidity(
        uint256 amount0Desired,
        uint256 amount1Desired,
        int24 tickLower,
        int24 tickUpper
    ) external returns (uint128 liquidity, uint256 amount0, uint256 amount1) {
        IERC20(token0).safeTransferFrom(msg.sender, address(this), amount0Desired);
        IERC20(token1).safeTransferFrom(msg.sender, address(this), amount1Desired);
        IERC20(token0).forceApprove(address(positionManager), amount0Desired);
        IERC20(token1).forceApprove(address(positionManager), amount1Desired);

        if (positionTokenId == 0) {
            // First liquidity: mint new position NFT
            uint256 tokenId;
            (tokenId, liquidity, amount0, amount1) = positionManager.mint(
                INonfungiblePositionManager.MintParams({
                    token0: token0,
                    token1: token1,
                    fee: fee,
                    tickLower: tickLower,
                    tickUpper: tickUpper,
                    amount0Desired: amount0Desired,
                    amount1Desired: amount1Desired,
                    amount0Min: amount0Desired * 99 / 100,  // 1% slippage
                    amount1Min: amount1Desired * 99 / 100,
                    recipient: address(this),
                    deadline: block.timestamp + 15 minutes,
                })
            );
            positionTokenId = tokenId;
            emit LiquidityAdded(tokenId, liquidity, amount0, amount1);
        } else {
            // Subsequent liquidity: increase existing position
            (, liquidity, amount0, amount1) = positionManager.increaseLiquidity(
                INonfungiblePositionManager.IncreaseLiquidityParams({
                    tokenId: positionTokenId,
                    amount0Desired: amount0Desired,
                    amount1Desired: amount1Desired,
                    amount0Min: amount0Desired * 99 / 100,
                    amount1Min: amount1Desired * 99 / 100,
                    deadline: block.timestamp + 15 minutes,
                })
            );
        }

        // Return unused tokens to caller
        _returnExcess(token0, amount0Desired - amount0, msg.sender);
        _returnExcess(token1, amount1Desired - amount1, msg.sender);
    }

    /// @notice Collect accumulated swap fees
    function collectFees() external returns (uint256 amount0, uint256 amount1) {
        (amount0, amount1) = positionManager.collect(
            INonfungiblePositionManager.CollectParams({
                tokenId: positionTokenId,
                recipient: msg.sender,
                amount0Max: type(uint128).max,
                amount1Max: type(uint128).max,
            })
        );
        emit FeesCollected(amount0, amount1);
    }

    function _returnExcess(address token, uint256 excess, address to) internal {
        if (excess > 0) IERC20(token).safeTransfer(to, excess);
    }
}

L2 deployment configuration

// hardhat.config.js — multi-network deployment with L2 support
require("@nomicfoundation/hardhat-toolbox");
require("@nomicfoundation/hardhat-verify");

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  solidity: {
    version: "0.8.24",
    settings: {
      optimizer: { enabled: true, runs: 200 },
      viaIR: true,  // Enable IR pipeline for better optimization
    },
  },
  networks: {
    // Optimism Mainnet (OP Stack L2)
    optimism: {
      url: process.env.OPTIMISM_RPC_URL,
      accounts: [process.env.DEPLOYER_PRIVATE_KEY],
      gasPrice: "auto",
    },
    // Arbitrum One (Nitro L2)
    arbitrum: {
      url: process.env.ARBITRUM_RPC_URL,
      accounts: [process.env.DEPLOYER_PRIVATE_KEY],
    },
    // Base (OP Stack L2, Coinbase)
    base: {
      url: process.env.BASE_RPC_URL,
      accounts: [process.env.DEPLOYER_PRIVATE_KEY],
    },
  },
  etherscan: {
    apiKey: {
      optimisticEthereum: process.env.OPTIMISTIC_ETHERSCAN_API_KEY,
      arbitrumOne: process.env.ARBISCAN_API_KEY,
      base: process.env.BASESCAN_API_KEY,
    },
    customChains: [
      {
        network: "base",
        chainId: 8453,
        urls: {
          apiURL: "https://api.basescan.org/api",
          browserURL: "https://basescan.org",
        },
      },
    ],
  },
};
// scripts/deploy.js — deploying to multiple L2s with verification
const { ethers, network, run } = require("hardhat");

async function main() {
  const [deployer] = await ethers.getSigners();
  console.log(`Deploying on ${network.name} from ${deployer.address}`);

  // Deploy implementation
  const Token = await ethers.getContractFactory("GovernanceToken");
  const token = await Token.deploy(deployer.address);
  await token.waitForDeployment();

  const address = await token.getAddress();
  console.log(`GovernanceToken deployed: ${address}`);

  // Wait for block confirmations before verifying
  const receipt = await token.deploymentTransaction().wait(5);
  console.log(`Confirmed in block ${receipt.blockNumber}`);

  // Verify on block explorer
  try {
    await run("verify:verify", {
      address,
      constructorArguments: [deployer.address],
    });
    console.log("Contract verified");
  } catch (e) {
    console.warn("Verification failed (may already be verified):", e.message);
  }
}

main().catch((e) => { console.error(e); process.exit(1); });

7. Gas Optimization Patterns

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract GasOptimized {
    // Pack storage variables into a single 32-byte slot (saves ~20,000 gas per slot avoided)
    struct UserInfo {
        uint128 balance;       // 16 bytes
        uint64  lastUpdate;    // 8 bytes
        uint32  rewardDebt;    // 4 bytes
        uint32  lockPeriod;    // 4 bytes — total: 32 bytes = 1 slot
    }
    mapping(address => UserInfo) public userInfo;

    // Cache storage reads to avoid repeated SLOADs (~2,100 gas each)
    function processUsers(address[] calldata users) external {
        uint256 len = users.length;
        for (uint256 i; i < len;) {
            UserInfo memory info = userInfo[users[i]]; // One SLOAD per user
            // Use info.balance, info.lastUpdate multiple times without extra SLOADs
            unchecked { ++i; }  // Safe: i < len prevents overflow
        }
    }

    // Use calldata instead of memory for external params (~3 gas/byte vs ~24 gas/byte)
    function batchMint(address[] calldata recipients, uint256[] calldata amounts) external {
        // calldata is read-only but significantly cheaper for large arrays
    }

    // Custom errors instead of require strings (saves ~50 gas per revert + smaller bytecode)
    error Unauthorized(address caller);
    error InvalidAmount(uint256 amount);

    // Check cheap conditions first: ISZERO is cheaper than SLOAD
    function validate(uint256 amount, address requiredSender) internal view {
        if (amount == 0) revert InvalidAmount(amount);               // ~3 gas
        if (msg.sender != requiredSender) revert Unauthorized(msg.sender); // ~6 gas
    }

    // Bit packing for multiple boolean flags — 1 SLOAD for all flags
    uint256 private _flags;
    uint256 private constant FLAG_INITIALIZED  = 1 << 0;
    uint256 private constant FLAG_PAUSED       = 1 << 1;
    uint256 private constant FLAG_ALLOWLIST    = 1 << 2;

    function isInitialized() public view returns (bool) { return _flags & FLAG_INITIALIZED != 0; }
    function isPaused()      public view returns (bool) { return _flags & FLAG_PAUSED != 0; }
    function isAllowlist()   public view returns (bool) { return _flags & FLAG_ALLOWLIST != 0; }

    function _setFlag(uint256 flag, bool value) internal {
        _flags = value ? (_flags | flag) : (_flags & ~flag);
    }
}

Few-Shot Examples

Example 1: Smart Contract Security Review

Prompt: "Review this ERC-20 token contract before we deploy it."

Response:

I found three issues that must be fixed before deployment.

Critical — Reentrancy in `burn` function:

// VULNERABLE: external call before state update
function burn(uint256 amount) external {
    (bool ok,) = msg.sender.call{value: ethReward(amount)}(""); // Call first
    _burn(msg.sender, amount);  // State update after — attacker can re-enter
}

// FIX: CEI + ReentrancyGuard
function burn(uint256 amount) external nonReentrant {
    uint256 reward = ethReward(amount);
    _burn(msg.sender, amount);  // State first
    (bool ok,) = msg.sender.call{value: reward}("");
    require(ok);
}

High — Missing two-step ownership transfer: `Ownable` allows transferring to any address immediately. If called with `address(0)` by mistake, owner is permanently lost. Switch to `Ownable2Step`.

Medium — `approve` frontrunning: Standard ERC-20 `approve` is vulnerable to the known race condition. Add `increaseAllowance`/`decreaseAllowance` and document the attack in comments.


Example 2: Gas Optimization

Prompt: "Our NFT mint function costs 180,000 gas. Can we get it lower?"

Response:

180k for a mint is high. Profile first:

forge test --gas-report --match-test testMint

Three likely culprits:

1 — Storage layout not packed:

// BAD: 3 slots = 3x SSTORE at 20,000 gas each = 60,000 gas
uint256 tokenId; address creator; uint256 mintTimestamp;

// GOOD: 1 slot = 20,000 gas for first SSTORE
struct TokenData {
    uint64 tokenId; address creator; uint32 mintTimestamp;
} // 32 bytes = 1 slot

2 — `_safeMint` calling `onERC721Received`: Each `_safeMint` adds ~2,300 gas per token for the receiver check. Use `_mint` for contract addresses you control.

3 — Switch to ERC721A for batch mints: If users mint 3+ at once, ERC721A amortizes the per-token storage cost from 20,000 gas to ~2,000 gas per additional token.

After packing + ERC721A, expect 100-120k for single mints and 40-50k per token in batches of 5.


Example 3: L2 Deployment Strategy

Prompt: "We want to deploy on Ethereum mainnet and also on Base. What changes are needed?"

Response:

Base is OP Stack — the same Solidity code deploys without changes. The operational differences are in configuration:

1 — Gas price: Base gas is dramatically cheaper than L1. If you have hardcoded gas limits or `gasleft()` guards, review them — they may be overly conservative.

2 — Block time: Base produces blocks every ~2 seconds vs 12 seconds on mainnet. If your contracts use `block.number` for time-based logic (vesting, lockups), switch to `block.timestamp` which is consistent across chains.

3 — Cross-chain token bridging: Use the Standard Bridge for canonical ETH and ERC-20 bridging. Do not deploy a custom bridge — it's an audit target and the standard bridge is battle-tested.

// Deploy to both in one script
for (const [networkName, config] of Object.entries(networks)) {
  const provider = new ethers.JsonRpcProvider(config.rpcUrl);
  const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
  const Contract = new ethers.ContractFactory(abi, bytecode, signer);
  const contract = await Contract.deploy(config.constructorArgs);
  await contract.waitForDeployment();
  console.log(`${networkName}: ${await contract.getAddress()}`);
}

4 — Verification: Submit to both Etherscan and Basescan. Configure both in `hardhat.config.js` under `etherscan.apiKey` — same ABI and source, different explorer API endpoints.