monad-wingman
npx skills add https://github.com/0x70626a/monad-wingman --skill monad-wingman
Agent 安装分布
Skill 文档
Monad Wingman
Comprehensive 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.
MONAD CRITICAL DIFFERENCES FROM ETHEREUM
Before doing ANYTHING, internalize these Monad-specific rules:
Chain Configuration
- Mainnet chain ID: 143 | Testnet chain ID: 10143
- DEFAULT TO TESTNET unless the user explicitly says mainnet
- Native token: MON (18 decimals) â NOT ETH
- RPC (testnet):
https://testnet-rpc.monad.xyz - RPC (mainnet):
https://rpc.monad.xyz - Block explorers: MonadVision, Monadscan, Socialscan
Solidity & Compilation
- EVM version: MUST be
pragueâ set infoundry.tomlasevm_version = "prague" - Solidity version: 0.8.27+ required, 0.8.28 recommended
- Max contract size: 128KB (vs Ethereum’s 24.5KB) â much more room
Deployment Rules (CRITICAL)
- ALWAYS use
--legacyflag â Monad does NOT support EIP-1559 type 2 transactions - ALWAYS use
forge scriptâ NEVER useforge create - No blob transactions â EIP-4844 type 3 txs are NOT supported
- Gas is charged on gas_limit, not actual usage â set gas limits carefully
- No global mempool â transactions go directly to the block producer
Wallet Persistence
- Store keys at
~/.monad-walletwithchmod 600 - Mainnet key safety: use
--ledger,--trezor, or keystore files â NEVER--private-keyfor mainnet
Monad Architecture
- Parallel execution = optimistic concurrency, produces same results as sequential
- Reserve Balance mechanism â understand this for DeFi
- Historical state NOT available on full nodes â do not rely on archive queries
- secp256r1 (P256) precompile at address
0x0100â enables passkey/WebAuthn signing
Faucet (Testnet)
curl -X POST https://agents.devnads.com/v1/faucet \
-H "Content-Type: application/json" \
-d '{"chainId": 10143, "address": "0xYOUR_ADDRESS"}'
Contract Verification
curl -X POST https://agents.devnads.com/v1/verify \
-H "Content-Type: application/json" \
-d '{"chainId": 10143, "address": "0xCONTRACT", "source": "...", "compilerVersion": "0.8.28"}'
AI AGENT INSTRUCTIONS – READ THIS FIRST
Dual Workflow: Foundry (Primary) + Scaffold-Monad
Monad development supports two workflows. Use Foundry for contracts-only projects; use Scaffold-Monad for fullstack dApps.
Workflow A: Foundry (Primary â Contracts Only)
Step 1: Initialize Foundry Project
forge init my-monad-project
cd my-monad-project
Step 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 test
Step 4: Deploy to Monad Testnet
forge script script/Deploy.s.sol:DeployScript \
--rpc-url https://testnet-rpc.monad.xyz \
--private-key $PRIVATE_KEY \
--broadcast \
--legacy
NEVER 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"}'
Workflow B: Scaffold-Monad (Fullstack dApps)
Step 1: Create Project
# Foundry variant (recommended)
git clone https://github.com/monad-developers/scaffold-monad-foundry.git my-dapp
cd my-dapp
Step 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 start
Step 5: Deploy
yarn deploy --network monadTestnet
Step 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
- Navigate to
http://localhost:3000 - Take a snapshot to get page elements
- Fund the wallet via faucet (MON, not ETH)
- Click through the app to verify functionality
Fork Mode (Testing Against Live Monad State)
anvil --fork-url https://rpc.monad.xyz --block-time 1
Or for testnet:
anvil --fork-url https://testnet-rpc.monad.xyz --block-time 1
DO NOT:
- Use
forge create(useforge scriptwith--legacyinstead) - Send EIP-1559 type 2 transactions (always use
--legacy) - Send blob transactions (EIP-4844 type 3 not supported)
- Use
--private-keyfor mainnet deployments (use--ledger/--trezor/keystore) - Define custom chain objects in frontend (use
import { monadTestnet } from 'viem/chains') - Rely on historical state queries (not available on full nodes)
- Assume gas is charged on usage (it is charged on gas_limit)
Monad Deployment Workflow
Deploy Script Template
// 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));
}
}
Deploy Commands
# 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 \
--legacy
THE MOST CRITICAL CONCEPT
NOTHING 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”:
- Make it callable by ANYONE (not just admin)
- Give callers a REASON (profit, reward, their own interest)
- Make the incentive SUFFICIENT to cover gas + profit
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.
Examples of Proper Incentive Design
// 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);
}
Critical Gotchas (Memorize These)
1. Token Decimals Vary
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:
- USDC, USDT: 6 decimals
- WBTC: 8 decimals
- Most tokens (DAI, WMON): 18 decimals
- MON (native): 18 decimals
2. ERC-20 Approve Pattern Required
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);
3. No Floating Point in Solidity
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;
4. Reentrancy Attacks
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.
5. Never Use DEX Spot Prices as Oracles
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.
6. Vault Inflation Attack
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);
}
7. Use SafeERC20
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 tokens
8. Monad-Specific Gotchas
Gas 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.
Scaffold-Monad Development
Project Structure
packages/
âââ foundry/ # Smart contracts
â âââ contracts/ # Your Solidity files
â âââ script/ # Deploy scripts
âââ nextjs/
âââ app/ # React pages
âââ contracts/ # Generated ABIs + externalContracts.ts
Essential Hooks
// 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,
});
Frontend Chain Configuration
// CORRECT: Import from viem/chains
import { monadTestnet } from "viem/chains";
// WRONG: Do NOT define a custom chain object
const monadTestnet = { id: 10143, ... }; // NEVER DO THIS
SpeedRun Ethereum Challenges
These 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 |
DeFi Protocol Patterns on Monad
Euler (Lending)
- Modular lending protocol on Monad
- Supply collateral, borrow assets
- Health factor = collateral value / debt value
- Liquidation when health factor < 1
Morpho (Optimized Lending)
- Peer-to-peer lending optimization layer
- Better rates through direct matching
- Compatible with Monad’s parallel execution
AMM / DEX Patterns
- Constant product formula: x * y = k
- Slippage protection required
- LP tokens represent pool share
Oracles on Monad
- Chainlink: Standard price feeds,
latestRoundData() - Pyth: Pull-based oracle with sub-second updates, well-suited to Monad’s speed
// 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);
}
ERC-4626 (Tokenized Vaults)
- Standard interface for yield-bearing vaults
- deposit/withdraw with share accounting
- Protect against inflation attacks
Security Review Checklist
Before deployment, verify:
- Access control on all admin functions
- Reentrancy protection (CEI + nonReentrant)
- Token decimal handling correct
- Oracle manipulation resistant (Chainlink or Pyth, not DEX spot)
- Integer overflow handled (0.8+ or SafeMath)
- Return values checked (SafeERC20)
- Input validation present
- Events emitted for state changes
- Incentives designed for maintenance functions
- NO infinite approvals (use exact amounts, NEVER type(uint256).max)
Monad-Specific Checks
-
evm_version = "prague"in foundry.toml - Solidity version 0.8.27+ (recommend 0.8.28)
- All deploy commands use
--legacyflag - All deploy commands use
forge script(notforge create) - Gas limits set carefully (charged on limit, not usage)
- No reliance on historical state queries
- No EIP-4844 blob transactions
- Mainnet deployments use hardware wallet (not
--private-key) - Contract size under 128KB
- Wallet files stored at
~/.monad-walletwith chmod 600
Response Guidelines
When helping developers:
- Default to testnet – Chain ID 10143 unless user says mainnet
- Answer directly – Address their question first
- Show code – Provide working examples with Monad config
- Always use
--legacy– Remind about no EIP-1559 support - Always use
forge script– Never suggestforge create - Warn about gotchas – Proactively mention relevant pitfalls
- Reference challenges – Point to SpeedRun Ethereum for practice (note: they target Ethereum)
- Ask about incentives – For any “automatic” function, ask who calls it and why
- Gas limit awareness – Remind that gas is charged on limit, not usage
- Verification – Always include contract verification step after deployment