Introduction
Smart contract development is a fast-moving field. Whether you're writing Solidity for Ethereum, Rust for Solana or Polkadot, or Vyper for security-critical DeFi protocols, the same challenge arises: translating high-level requirements into secure, gas-efficient code. Over the past year, I've refined a set of prompts that help me move from idea to audited contract faster. Each prompt is battle-tested in real projects—auditing, building, and debugging. Below are nine prompts that cover the most common tasks, from architecture to gas optimization.
1. Generate a Solidity smart contract skeleton with OpenZeppelin
Prompt: "Create a Solidity smart contract for an ERC-20 token with mint, burn, and pause functionality using OpenZeppelin v4.9. Include a constructor that sets the owner and an initial supply. Add events for Mint, Burn, and Pause."
Why it works: OpenZeppelin provides audited base contracts. This prompt forces you to specify versions, features, and events—avoiding common pitfalls like missing initializer or wrong import path.
Example usage:
// 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/security/Pausable.sol";
contract MyToken is ERC20, Ownable, Pausable {
event Mint(address indexed to, uint256 amount);
event Burn(address indexed from, uint256 amount);
event Pause(address indexed pauser);
event Unpause(address indexed unpauser);
constructor(uint256 initialSupply) ERC20("MyToken", "MTK") {
_mint(msg.sender, initialSupply);
}
function mint(address to, uint256 amount) external onlyOwner whenNotPaused {
_mint(to, amount);
emit Mint(to, amount);
}
function burn(uint256 amount) external whenNotPaused {
_burn(msg.sender, amount);
emit Burn(msg.sender, amount);
}
function pause() external onlyOwner {
_pause();
emit Pause(msg.sender);
}
function unpause() external onlyOwner {
_unpause();
emit Unpause(msg.sender);
}
}
Source: OpenZeppelin Contracts Documentation
2. Refactor a Solidity function for gas efficiency
Prompt: "Optimize this Solidity function for gas: it takes an array of addresses and marks them as whitelisted. Currently uses a loop with storage writes. Suggest changes to reduce gas cost."
Why it works: Gas is money on Ethereum. This prompt pushes you to think about storage vs memory, batch operations, and unchecked blocks.
Example:
// Original: high gas due to storage writes in loop
function whitelistBatch(address[] calldata users) external onlyOwner {
for (uint i = 0; i < users.length; i++) {
whitelisted[users[i]] = true;
}
}
// Optimized: use unchecked and cache length
function whitelistBatchOptimized(address[] calldata users) external onlyOwner {
uint length = users.length;
for (uint i = 0; i < length; ) {
whitelisted[users[i]] = true;
unchecked { i++; }
}
}
Source: Ethereum Gas Optimization Guide
3. Write a Vyper contract for a simple escrow
Prompt: "Write a Vyper contract for an escrow that holds ETH until both parties agree to release. Use two parties: buyer and seller. Include a refund function if buyer cancels within 24 hours."
Why it works: Vyper prioritizes security and auditability. This prompt tests your understanding of time locks, payable functions, and state management.
Example:
# @version 0.3.10
buyer: public(address)
seller: public(address)
escrow_amount: public(uint256)
created_at: public(uint256)
released: public(bool)
@external
def __init__(_seller: address):
self.buyer = msg.sender
self.seller = _seller
self.created_at = block.timestamp
@external
@payable
def deposit():
assert msg.value > 0
assert self.escrow_amount == 0
self.escrow_amount = msg.value
@external
def release():
assert msg.sender == self.buyer or msg.sender == self.seller
assert not self.released
self.released = True
send(self.seller, self.escrow_amount)
@external
def refund():
assert msg.sender == self.buyer
assert block.timestamp <= self.created_at + 86400 # 24 hours
assert not self.released
self.released = True
send(self.buyer, self.escrow_amount)
Source: Vyper Documentation
4. Explain a reentrancy vulnerability with a Solidity example
Prompt: "Show a Solidity code snippet vulnerable to reentrancy and then provide a fix using the checks-effects-interactions pattern. Explain why the attack works."
Why it works: Reentrancy is the most common critical bug. Understanding it is non-negotiable for any blockchain developer.
Example:
// Vulnerable
function withdraw(uint256 amount) external {
require(balances[msg.sender] >= amount);
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
balances[msg.sender] -= amount;
}
// Fixed
function withdraw(uint256 amount) external {
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount;
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
}
Source: SWC-107 Reentrancy
5. Write a Rust smart contract for Solana (SPL token)
Prompt: "Write a Solana program in Rust that creates an SPL token and mints an initial supply to a user. Use the Anchor framework. Include error handling for invalid authority."
Why it works: Solana uses Rust and the Anchor framework for security. This prompt covers account deserialization, CPI calls, and program-derived addresses.
Example:
use anchor_lang::prelude::*;
use anchor_spl::token::{self, Mint, Token, TokenAccount};
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
pub mod my_token {
use super::*;
pub fn initialize(ctx: Context<Initialize>, decimals: u8) -> Result<()> {
let cpi_accounts = token::MintTo {
mint: ctx.accounts.mint.to_account_info(),
to: ctx.accounts.token_account.to_account_info(),
authority: ctx.accounts.authority.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
token::mint_to(cpi_ctx, 1_000_000 * 10u64.pow(decimals as u32))?;
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, payer = user, mint::decimals = decimals, mint::authority = authority)]
pub mint: Account<'info, Mint>,
#[account(init, payer = user, token::mint = mint, token::authority = authority)]
pub token_account: Account<'info, TokenAccount>,
#[account(mut)]
pub user: Signer<'info>,
pub authority: Signer<'info>,
pub token_program: Program<'info, Token>,
pub system_program: Program<'info, System>,
pub rent: Sysvar<'info, Rent>,
}
Source: Anchor Documentation
6. Create a Solidity contract that emits structured events for DeFi
Prompt: "Design a Solidity contract for a simple lending pool that tracks deposits and borrows. Emit events with indexed parameters so off-chain services can efficiently query user activity."
Why it works: Events are critical for indexing and analytics. This prompt teaches best practices for event design.
Example:
event Deposit(address indexed user, uint256 amount, uint256 timestamp);
event Borrow(address indexed user, uint256 amount, uint256 debt, uint256 timestamp);
event Repay(address indexed user, uint256 amount, uint256 timestamp);
function deposit(uint256 amount) external {
// ... logic
emit Deposit(msg.sender, amount, block.timestamp);
}
Source: Solidity Events Documentation
7. Debug a Vyper function that reverts unexpectedly
Prompt: "I have a Vyper function that calls an external contract and reverts with 'out of gas'. The function uses a loop. How can I debug this? Provide a code snippet that limits gas per iteration."
Why it works: Gas estimation is tricky in Vyper due to static analysis limitations. This prompt teaches defensive programming.
Example:
@external
def process_batch(addresses: DynArray[address, 100]):
for addr in addresses:
# use a fixed gas limit per call
raw_call(addr, b"", gas=50000)
Source: Vyper Gas Control
8. Write a Solidity upgradeable contract using UUPS pattern
Prompt: "Write a Solidity contract that uses the UUPS upgradeable pattern with OpenZeppelin. Include a constructor that initializes the owner and a version variable. Show the proxy contract structure."
Why it works: Upgradeable contracts are standard in production. This prompt tests understanding of storage collisions and initialization.
Example:
// Implementation
contract MyContractV1 is Initializable, UUPSUpgradeable, OwnableUpgradeable {
uint256 public version;
function initialize() public initializer {
__Ownable_init();
__UUPSUpgradeable_init();
version = 1;
}
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
}
Source: OpenZeppelin Upgrades
9. Generate a Rust function to verify Ed25519 signatures on Solana
Prompt: "Write a Rust function using the Solana SDK that verifies an Ed25519 signature from a user. The function should accept a public key, message, and signature, and return a boolean."
Why it works: Signature verification is essential for off-chain authorization. This prompt covers sysvar instructions and cryptographic primitives.
Example:
use solana_program::{
ed25519_program, instruction::Instruction, pubkey::Pubkey,
};
pub fn verify_signature(
pubkey: &Pubkey,
message: &[u8],
signature: &[u8; 64],
) -> Result<(), ProgramError> {
let instruction = Instruction {
program_id: ed25519_program::ID,
accounts: vec![],
data: [&[1], pubkey.as_ref(), message, signature].concat(),
};
// ... invoke and check
Ok(())
}
Source: Solana Ed25519 Program
Conclusion
These nine prompts cover the essential tasks of a blockchain developer: contract creation, security analysis, gas optimization, and cross-language patterns. The key is to adapt each prompt to your specific problem—add constraints like "use OpenZeppelin v4.9" or "optimize for storage writes." Start with the prompt that matches your current task, and modify it as you go. For further learning, explore the official documentation of Solidity, Vyper, and Anchor—they are the most reliable sources for production code.
Comments