Monad blockchain development tutor and builder. Triggers on "build", "create", "dApp", "smart contract", "Solidity", "DeFi", "Monad", "web3", "MON", or any blockchain development task. Covers Foundry-first workflow, Scaffold-Monad, parallel execution EVM, and Monad-specific deployment patterns.
86
Quality
79%
Does it follow best practices?
Impact
100%
1.56xAverage score across 3 eval scenarios
Advisory
Suggest reviewing before use
Optimize this skill with Tessl
npx tessl skill review --optimize ./data/skills-md/0x70626a/monad-wingman/monad-wingman/SKILL.mdComprehensive Monad blockchain development guide for AI agents. Covers smart contract development on Monad (parallel execution EVM-compatible L1), DeFi protocols, security best practices, deployment workflows, and the SpeedRun Ethereum curriculum adapted for Monad.
Before doing ANYTHING, internalize these Monad-specific rules:
https://testnet-rpc.monad.xyzhttps://rpc.monad.xyzprague — set in foundry.toml as evm_version = "prague"--legacy flag — Monad does NOT support EIP-1559 type 2 transactionsforge script — NEVER use forge create~/.monad-wallet with chmod 600--ledger, --trezor, or keystore files — NEVER --private-key for mainnet0x0100 — enables passkey/WebAuthn signingcurl -X POST https://agents.devnads.com/v1/faucet \
-H "Content-Type: application/json" \
-d '{"chainId": 10143, "address": "0xYOUR_ADDRESS"}'curl -X POST https://agents.devnads.com/v1/verify \
-H "Content-Type: application/json" \
-d '{"chainId": 10143, "address": "0xCONTRACT", "source": "...", "compilerVersion": "0.8.28"}'Monad development supports two workflows. Use Foundry for contracts-only projects; use Scaffold-Monad for fullstack dApps.
Step 1: Initialize Foundry Project
forge init my-monad-project
cd my-monad-projectStep 2: Configure foundry.toml for Monad
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
evm_version = "prague"
solc_version = "0.8.28"
[rpc_endpoints]
monad_testnet = "https://testnet-rpc.monad.xyz"
monad_mainnet = "https://rpc.monad.xyz"Step 3: Write and Test Contracts
forge testStep 4: Deploy to Monad Testnet
forge script script/Deploy.s.sol:DeployScript \
--rpc-url https://testnet-rpc.monad.xyz \
--private-key $PRIVATE_KEY \
--broadcast \
--legacyNEVER use forge create. ALWAYS use forge script with --legacy.
Step 5: Verify Contract
curl -X POST https://agents.devnads.com/v1/verify \
-H "Content-Type: application/json" \
-d '{"chainId": 10143, "address": "0xDEPLOYED", "source": "...", "compilerVersion": "0.8.28"}'Step 1: Create Project
# Foundry variant (recommended)
git clone https://github.com/monad-developers/scaffold-monad-foundry.git my-dapp
cd my-dappStep 2: Configure for Monad
Edit packages/nextjs/scaffold.config.ts:
import { monadTestnet } from "viem/chains";
const scaffoldConfig = {
targetNetworks: [monadTestnet],
pollingInterval: 3000, // 3 seconds (default 30000 is too slow)
};IMPORTANT: Import monadTestnet from viem/chains. Do NOT define a custom chain object.
Step 3: Configure Foundry for Monad
Edit packages/foundry/foundry.toml:
evm_version = "prague"
solc_version = "0.8.28"Step 4: Install and Start
cd <project-name>
yarn install
yarn startStep 5: Deploy
yarn deploy --network monadTestnetStep 6: Fund Wallet via Faucet
curl -X POST https://agents.devnads.com/v1/faucet \
-H "Content-Type: application/json" \
-d '{"chainId": 10143, "address": "0xYOUR_BURNER_WALLET"}'Step 7: Test the Frontend
http://localhost:3000anvil --fork-url https://rpc.monad.xyz --block-time 1Or for testnet:
anvil --fork-url https://testnet-rpc.monad.xyz --block-time 1forge create (use forge script with --legacy instead)--legacy)--private-key for mainnet deployments (use --ledger/--trezor/keystore)import { monadTestnet } from 'viem/chains')// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "forge-std/Script.sol";
import "../src/MyContract.sol";
contract DeployScript is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
MyContract myContract = new MyContract();
vm.stopBroadcast();
console.log("MyContract deployed to:", address(myContract));
}
}# Testnet (default)
forge script script/Deploy.s.sol:DeployScript \
--rpc-url https://testnet-rpc.monad.xyz \
--private-key $PRIVATE_KEY \
--broadcast \
--legacy
# Mainnet (use hardware wallet!)
forge script script/Deploy.s.sol:DeployScript \
--rpc-url https://rpc.monad.xyz \
--ledger \
--broadcast \
--legacyNOTHING IS AUTOMATIC ON ANY EVM CHAIN, INCLUDING MONAD.
Smart contracts cannot execute themselves. There is no cron job, no scheduler, no background process. For EVERY function that "needs to happen":
Always ask: "Who calls this function? Why would they pay gas?"
If you cannot answer this, your function will not get called.
Monad note: Gas on Monad is cheap due to parallel execution throughput, but the incentive principle still applies. Gas is charged on gas_limit, not usage — so set limits carefully.
// LIQUIDATIONS: Caller gets bonus collateral
function liquidate(address user) external {
require(getHealthFactor(user) < 1e18, "Healthy");
uint256 bonus = collateral * 5 / 100; // 5% bonus
collateralToken.transfer(msg.sender, collateral + bonus);
}
// YIELD HARVESTING: Caller gets % of harvest
function harvest() external {
uint256 yield = protocol.claimRewards();
uint256 callerReward = yield / 100; // 1%
token.transfer(msg.sender, callerReward);
}
// CLAIMS: User wants their own tokens
function claimRewards() external {
uint256 reward = pendingRewards[msg.sender];
pendingRewards[msg.sender] = 0;
token.transfer(msg.sender, reward);
}USDC = 6 decimals, not 18!
// BAD: Assumes 18 decimals - transfers 1 TRILLION USDC!
uint256 oneToken = 1e18;
// GOOD: Check decimals
uint256 oneToken = 10 ** token.decimals();Common decimals:
Contracts cannot pull tokens directly. Two-step process:
// Step 1: User approves
token.approve(spenderContract, amount);
// Step 2: Contract pulls tokens
token.transferFrom(user, address(this), amount);Never use infinite approvals:
// DANGEROUS
token.approve(spender, type(uint256).max);
// SAFE
token.approve(spender, exactAmount);Use basis points (1 bp = 0.01%):
// BAD: This equals 0
uint256 fivePercent = 5 / 100;
// GOOD: Basis points
uint256 FEE_BPS = 500; // 5% = 500 basis points
uint256 fee = (amount * FEE_BPS) / 10000;External calls can call back into your contract:
// SAFE: Checks-Effects-Interactions pattern
function withdraw() external nonReentrant {
uint256 bal = balances[msg.sender];
balances[msg.sender] = 0; // Effect BEFORE interaction
(bool success,) = msg.sender.call{value: bal}("");
require(success);
}Always use OpenZeppelin's ReentrancyGuard. Reentrancy is still possible on Monad despite parallel execution — parallel execution produces the same results as sequential.
Flash loans can manipulate spot prices instantly:
// SAFE: Use Chainlink or Pyth
function getPrice() internal view returns (uint256) {
(, int256 price,, uint256 updatedAt,) = priceFeed.latestRoundData();
require(block.timestamp - updatedAt < 3600, "Stale");
require(price > 0, "Invalid");
return uint256(price);
}On Monad, use Chainlink or Pyth for oracle feeds.
First depositor can steal funds via share manipulation:
// Mitigation: Virtual offset
function convertToShares(uint256 assets) public view returns (uint256) {
return assets.mulDiv(totalSupply() + 1e3, totalAssets() + 1);
}Some tokens (USDT) don't return bool on transfer:
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
using SafeERC20 for IERC20;
token.safeTransfer(to, amount); // Handles non-standard tokensGas limit billing: Monad charges gas on gas_limit, NOT actual gas used. Over-estimating gas wastes real MON. Always benchmark and set tight gas limits in production.
No EIP-1559: Always use --legacy flag. Type 2 transactions will be rejected.
No blob txs: EIP-4844 type 3 transactions are not supported.
No global mempool: Transactions go directly to the block producer. MEV strategies that rely on mempool monitoring do not work the same way.
Parallel execution safety: Monad's parallel execution is deterministic — it produces the same results as sequential execution. Your contracts do NOT need special handling for parallelism. Standard Solidity patterns work as-is.
Historical state: Full nodes do not serve historical state. Do not rely on eth_call at past block numbers.
128KB contract limit: Monad allows contracts up to 128KB (vs Ethereum's 24.5KB). This is generous but not infinite — still optimize if approaching the limit.
packages/
├── foundry/ # Smart contracts
│ ├── contracts/ # Your Solidity files
│ └── script/ # Deploy scripts
└── nextjs/
├── app/ # React pages
└── contracts/ # Generated ABIs + externalContracts.ts// Read contract data
const { data } = useScaffoldReadContract({
contractName: "YourContract",
functionName: "greeting",
});
// Write to contract
const { writeContractAsync } = useScaffoldWriteContract("YourContract");
// Watch events
useScaffoldEventHistory({
contractName: "YourContract",
eventName: "Transfer",
fromBlock: 0n,
});// CORRECT: Import from viem/chains
import { monadTestnet } from "viem/chains";
// WRONG: Do NOT define a custom chain object
const monadTestnet = { id: 10143, ... }; // NEVER DO THISThese challenges use Scaffold-ETH 2 and target Ethereum. They teach EVM fundamentals that are directly applicable to Monad development. Use them for learning, then deploy your projects to Monad.
| Challenge | Concept | Key Lesson |
|---|---|---|
| 0: Simple NFT | ERC-721 | Minting, metadata, tokenURI |
| 1: Staking | Coordination | Deadlines, escrow, thresholds |
| 2: Token Vendor | ERC-20 | Approve pattern, buy/sell |
| 3: Dice Game | Randomness | On-chain randomness is insecure |
| 4: DEX | AMM | x*y=k formula, slippage |
| 5: Oracles | Price Feeds | Chainlink, manipulation resistance |
| 6: Lending | Collateral | Health factor, liquidation incentives |
| 7: Stablecoins | Pegging | CDP, over-collateralization |
| 8: Prediction Markets | Resolution | Outcome determination |
| 9: ZK Voting | Privacy | Zero-knowledge proofs |
| 10: Multisig | Signatures | Threshold approval |
| 11: SVG NFT | On-chain Art | Generative, base64 encoding |
latestRoundData()// Chainlink on Monad
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
function getLatestPrice() public view returns (uint256) {
(, int256 price,, uint256 updatedAt,) = priceFeed.latestRoundData();
require(block.timestamp - updatedAt < 3600, "Stale price");
require(price > 0, "Invalid price");
return uint256(price);
}Before deployment, verify:
evm_version = "prague" in foundry.toml--legacy flagforge script (not forge create)--private-key)~/.monad-wallet with chmod 600When helping developers:
--legacy - Remind about no EIP-1559 supportforge script - Never suggest forge create5342bca
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.