Comparing coingecko with monad
coingecko
View full →Author
@0xinit
Stars
53
Repository
0xinit/cryptoskills
CoinGecko Solana API Development Guide
A comprehensive guide for integrating CoinGecko's on-chain API for Solana. Access real-time token prices, DEX pool data, OHLCV charts, trade history, and market analytics across 1,700+ decentralized exchanges.
Overview
CoinGecko's Solana API provides:
- Token Prices: Real-time prices by contract address (single or batch)
- Pool Data: Liquidity pool information, trending pools, top pools
- OHLCV Charts: Candlestick data for technical analysis
- Trade History: Recent trades for any pool
- DEX Discovery: List all DEXes operating on Solana
- Search: Find pools by token name, symbol, or address
- Megafilter: Advanced filtering across pools, tokens, and DEXes
Key Features
| Feature | Description |
|---|---|
| 250+ Networks | Multi-chain support including Solana |
| 1,700+ DEXes | Raydium, Orca, Jupiter, Meteora, Pump.fun, etc. |
| 15M+ Tokens | Comprehensive token coverage |
| Real-time Data | Updates every 10-30 seconds |
| Historical Data | OHLCV charts and trade history |
Quick Start
Get Your API Key
- Demo API (Free): Visit coingecko.com/en/api
- Pro API (Paid): Visit coingecko.com/en/api/pricing
Environment Setup
# .env file
COINGECKO_API_KEY=your_api_key_here
COINGECKO_API_TYPE=demo # or 'pro'
API Configuration
// Configuration for both Demo and Pro APIs
const CONFIG = {
demo: {
baseUrl: 'https://api.coingecko.com/api/v3/onchain',
headerKey: 'x-cg-demo-api-key',
rateLimit: 30, // calls per minute
},
pro: {
baseUrl: 'https://pro-api.coingecko.com/api/v3/onchain',
headerKey: 'x-cg-pro-api-key',
rateLimit: 500, // calls per minute (varies by plan)
},
};
const apiType = process.env.COINGECKO_API_TYPE || 'demo';
const apiKey = process.env.COINGECKO_API_KEY;
const BASE_URL = CONFIG[apiType].baseUrl;
const HEADER_KEY = CONFIG[apiType].headerKey;
// Solana network identifier
const NETWORK = 'solana';
Basic Token Price Fetch
async function getTokenPrice(tokenAddress: string): Promise<number | null> {
const url = `${BASE_URL}/simple/networks/${NETWORK}/token_price/${tokenAddress}`;
const response = await fetch(url, {
headers: {
[HEADER_KEY]: apiKey,
'Accept': 'application/json',
},
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const data = await response.json();
return data.data?.attributes?.token_prices?.[tokenAddress] ?? null;
}
// Usage
const USDC = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
const price = await getTokenPrice(USDC);
console.log(`USDC Price: $${price}`);
API Endpoints Reference
Simple Token Price
Get token prices by contract address.
Endpoint: GET /simple/networks/{network}/token_price/{addresses}
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
network | string | Yes | Network ID (solana) |
addresses | string | Yes | Comma-separated token addresses (max 30 Demo, 100 Pro) |
include_market_cap | boolean | No | Include market cap data |
include_24hr_vol | boolean | No | Include 24h volume |
include_24hr_price_change | boolean | No | Include 24h price change % |
async function getTokenPrices(addresses: string[]): Promise<Record<string, TokenPriceData>> {
const addressList = addresses.join(',');
const url = `${BASE_URL}/simple/networks/${NETWORK}/token_price/${addressList}`;
const params = new URLSearchParams({
include_market_cap: 'true',
include_24hr_vol: 'true',
include_24hr_price_change: 'true',
});
const response = await fetch(`${url}?${params}`, {
headers: { [HEADER_KEY]: apiKey },
});
const data = await response.json();
return data.data?.attributes || {};
}
Token Data by Address
Get detailed token information.
Endpoint: GET /networks/{network}/tokens/{address}
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
network | string | Yes | Network ID |
address | string | Yes | Token contract address |
include | string | No | Include top_pools for liquidity data |
interface TokenData {
address: string;
name: string;
symbol: string;
decimals: number;
image_url: string;
price_usd: string;
fdv_usd: string;
market_cap_usd: string;
total_supply: string;
volume_usd: {
h24: string;
};
price_change_percentage: {
h24: string;
};
}
async function getTokenData(address: string): Promise<TokenData> {
const url = `${BASE_URL}/networks/${NETWORK}/tokens/${address}?include=top_pools`;
const response = await fetch(url, {
headers: { [HEADER_KEY]: apiKey },
});
const data = await response.json();
return data.data?.attributes;
}
Multi-Token Data
Batch fetch multiple tokens.
Endpoint: GET /networks/{network}/tokens/multi/{addresses}
async function getMultipleTokens(addresses: string[]): Promise<TokenData[]> {
const addressList = addresses.join(',');
const url = `${BASE_URL}/networks/${NETWORK}/tokens/multi/${addressList}`;
const response = await fetch(url, {
headers: { [HEADER_KEY]: apiKey },
});
const data = await response.json();
return data.data?.map((item: any) => item.attributes) || [];
}
Pool Data by Address
Get detailed pool information.
Endpoint: GET /networks/{network}/pools/{address}
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
include | string | No | base_token, quote_token, dex |
include_volume_breakdown | boolean | No | Volume breakdown by timeframe |
interface PoolData {
address: string;
name: string;
pool_created_at: string;
base_token_price_usd: string;
quote_token_price_usd: string;
base_token_price_native_currency: string;
fdv_usd: string;
market_cap_usd: string;
reserve_in_usd: string;
price_change_percentage: {
m5: string;
h1: string;
h6: string;
h24: string;
};
transactions: {
m5: { buys: number; sells: number };
h1: { buys: number; sells: number };
h24: { buys: number; sells: number };
};
volume_usd: {
m5: string;
h1: string;
h6: string;
h24: string;
};
}
async function getPoolData(poolAddress: string): Promise<PoolData> {
const url = `${BASE_URL}/networks/${NETWORK}/pools/${poolAddress}`;
const params = new URLSearchParams({
include: 'base_token,quote_token,dex',
});
const response = await fetch(`${url}?${params}`, {
headers: { [HEADER_KEY]: apiKey },
});
const data = await response.json();
return data.data?.attributes;
}
Trending Pools
Get trending pools across all networks or filtered by network.
Endpoint: GET /networks/trending_pools
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
include | string | base_token | Attributes to include |
page | integer | 1 | Page number |
duration | string | 24h | 5m, 1h, 6h, 24h |
async function getTrendingPools(duration: '5m' | '1h' | '6h' | '24h' = '24h'): Promise<PoolData[]> {
const url = `${BASE_URL}/networks/trending_pools`;
const params = new URLSearchParams({
include: 'base_token,quote_token,dex,network',
duration,
page: '1',
});
const response = await fetch(`${url}?${params}`, {
headers: { [HEADER_KEY]: apiKey },
});
const data = await response.json();
return data.data?.map((item: any) => item.attributes) || [];
}
// Filter for Solana pools
async function getSolanaTrendingPools(): Promise<PoolData[]> {
const allPools = await getTrendingPools();
return allPools.filter(pool => pool.network === 'solana');
}
Top Pools on Network
Get top pools by volume on Solana.
Endpoint: GET /networks/{network}/pools
async function getTopPools(page: number = 1): Promise<PoolData[]> {
const url = `${BASE_URL}/networks/${NETWORK}/pools`;
const params = new URLSearchParams({
include: 'base_token,quote_token,dex',
page: page.toString(),
});
const response = await fetch(`${url}?${params}`, {
headers: { [HEADER_KEY]: apiKey },
});
const data = await response.json();
return data.data?.map((item: any) => item.attributes) || [];
}
Search Pools
Search for pools by token name, symbol, or address.
Endpoint: GET /search/pools
Parameters:
| Parameter | Type | Description |
|---|---|---|
query | string | Search term (name, symbol, address) |
network | string | Filter by network |
page | integer | Page number |
async function searchPools(query: string): Promise<PoolData[]> {
const url = `${BASE_URL}/search/pools`;
const params = new URLSearchParams({
query,
network: NETWORK,
include: 'base_token,quote_token,dex',
});
const response = await fetch(`${url}?${params}`, {
headers: { [HEADER_KEY]: apiKey },
});
const data = await response.json();
return data.data?.map((item: any) => item.attributes) || [];
}
// Search for SOL pools
const solPools = await searchPools('SOL');
Pool OHLCV Chart
Get candlestick data for technical analysis.
Endpoint: GET /networks/{network}/pools/{pool_address}/ohlcv/{timeframe}
Timeframes: day, hour, minute
Parameters:
| Parameter | Type | Description |
|---|---|---|
aggregate | integer | Candle aggregation (1, 5, 15 for minute; 1, 4, 12 for hour) |
before_timestamp | integer | Unix timestamp for pagination |
limit | integer | Number of candles (max 1000) |
currency | string | usd or token |
interface OHLCVData {
timestamp: number;
open: number;
high: number;
low: number;
close: number;
volume: number;
}
async function getPoolOHLCV(
poolAddress: string,
timeframe: 'day' | 'hour' | 'minute' = 'hour',
aggregate: number = 1,
limit: number = 100
): Promise<OHLCVData[]> {
const url = `${BASE_URL}/networks/${NETWORK}/pools/${poolAddress}/ohlcv/${timeframe}`;
const params = new URLSearchParams({
aggregate: aggregate.toString(),
limit: limit.toString(),
currency: 'usd',
});
const response = await fetch(`${url}?${params}`, {
headers: { [HEADER_KEY]: apiKey },
});
const data = await response.json();
return data.data?.attributes?.ohlcv_list?.map((candle: number[]) => ({
timestamp: candle[0],
open: candle[1],
high: candle[2],
low: candle[3],
close: candle[4],
volume: candle[5],
})) || [];
}
// Get hourly candles
const hourlyCandles = await getPoolOHLCV(poolAddress, 'hour', 1, 24);
// Get 5-minute candles
const fiveMinCandles = await getPoolOHLCV(poolAddress, 'minute', 5, 100);
Recent Trades
Get recent trades for a pool.
Endpoint: GET /networks/{network}/pools/{pool_address}/trades
interface TradeData {
block_number: number;
block_timestamp: string;
tx_hash: string;
tx_from_address: string;
from_token_amount: string;
to_token_amount: string;
price_from_in_currency_token: string;
price_to_in_currency_token: string;
price_from_in_usd: string;
price_to_in_usd: string;
kind: 'buy' | 'sell';
volume_in_usd: string;
}
async function getRecentTrades(poolAddress: string): Promise<TradeData[]> {
const url = `${BASE_URL}/networks/${NETWORK}/pools/${poolAddress}/trades`;
const response = await fetch(url, {
headers: { [HEADER_KEY]: apiKey },
});
const data = await response.json();
return data.data?.map((item: any) => item.attributes) || [];
}
List DEXes on Solana
Get all decentralized exchanges on Solana.
Endpoint: GET /networks/{network}/dexes
interface DexData {
id: string;
name: string;
}
async function getSolanaDexes(): Promise<DexData[]> {
const url = `${BASE_URL}/networks/${NETWORK}/dexes`;
const response = await fetch(url, {
headers: { [HEADER_KEY]: apiKey },
});
const data = await response.json();
return data.data?.map((item: any) => ({
id: item.id,
name: item.attributes.name,
})) || [];
}
Megafilter (Advanced)
Advanced filtering for pools across networks, DEXes, and tokens.
Endpoint: GET /pools/megafilter
Parameters:
| Parameter | Type | Description |
|---|---|---|
networks | string | Filter by network(s) |
dexes | string | Filter by DEX(es) |
sort | string | Sort order (e.g., pool_created_at_desc) |
min_reserve_in_usd | number | Minimum liquidity |
min_h24_volume_usd | number | Minimum 24h volume |
async function getMegafilterPools(options: {
dexes?: string[];
minLiquidity?: number;
minVolume?: number;
sort?: string;
}): Promise<PoolData[]> {
const url = `${BASE_URL}/pools/megafilter`;
const params = new URLSearchParams({
networks: NETWORK,
page: '1',
});
if (options.dexes) {
params.set('dexes', options.dexes.join(','));
}
if (options.minLiquidity) {
params.set('min_reserve_in_usd', options.minLiquidity.toString());
}
if (options.minVolume) {
params.set('min_h24_volume_usd', options.minVolume.toString());
}
if (options.sort) {
params.set('sort', options.sort);
}
const response = await fetch(`${url}?${params}`, {
headers: { [HEADER_KEY]: apiKey },
});
const data = await response.json();
return data.data?.map((item: any) => item.attributes) || [];
}
// Get newest Pump.fun pools
const pumpfunPools = await getMegafilterPools({
dexes: ['pump-fun'],
sort: 'pool_created_at_desc',
});
// Get high-volume Raydium pools
const raydiumPools = await getMegafilterPools({
dexes: ['raydium'],
minVolume: 100000,
minLiquidity: 50000,
});
Common Solana DEX Identifiers
| DEX | ID | Description |
|---|---|---|
| Raydium | raydium | Leading AMM on Solana |
| Orca | orca | User-friendly DEX |
| Jupiter | jupiter | Aggregator with pools |
| Meteora | meteora | Dynamic AMM |
| Pump.fun | pump-fun | Memecoin launchpad |
| OpenBook | openbook | Order book DEX |
| Lifinity | lifinity | Proactive market maker |
| Phoenix | phoenix | On-chain order book |
Common Token Addresses
| Token | Address |
|---|---|
| USDC | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v |
| USDT | Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB |
| SOL (Wrapped) | So11111111111111111111111111111111111111112 |
| JUP | JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN |
| BONK | DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263 |
| WIF | EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzLHYxdM65zcjm |
| PYTH | HZ1JovNiVvGrGNiiYvEozEVgZ58xaU3RKwX8eACQBCt3 |
| RAY | 4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R |
| ORCA | orcaEKTdK7LKz57vaAYr9QeNsVEPfiu6QeMU1kektZE |
Rate Limits
| Plan | Calls/Minute | Max Addresses/Request |
|---|---|---|
| Demo (Free) | 30 | 30 |
| Analyst | 500 | 50 |
| Lite | 500 | 50 |
| Pro | 1,000 | 100 |
| Enterprise | Custom | Custom |
Rate Limit Handling
class RateLimiter {
private calls: number[] = [];
private maxCalls: number;
private windowMs: number = 60000; // 1 minute
constructor(maxCallsPerMinute: number) {
this.maxCalls = maxCallsPerMinute;
}
async waitForSlot(): Promise<void> {
const now = Date.now();
this.calls = this.calls.filter(t => now - t < this.windowMs);
if (this.calls.length >= this.maxCalls) {
const oldestCall = this.calls[0];
const waitTime = this.windowMs - (now - oldestCall);
await new Promise(resolve => setTimeout(resolve, waitTime));
}
this.calls.push(Date.now());
}
}
// Usage
const rateLimiter = new RateLimiter(30); // Demo API
async function fetchWithRateLimit(url: string): Promise<any> {
await rateLimiter.waitForSlot();
const response = await fetch(url, {
headers: { [HEADER_KEY]: apiKey },
});
return response.json();
}
Error Handling
async function safeApiCall<T>(url: string): Promise<T | null> {
try {
const response = await fetch(url, {
headers: { [HEADER_KEY]: apiKey },
});
if (response.status === 401) {
throw new Error('Invalid API key');
}
if (response.status === 429) {
throw new Error('Rate limit exceeded - wait before retrying');
}
if (response.status === 404) {
console.warn('Resource not found');
return null;
}
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return response.json();
} catch (error) {
console.error('CoinGecko API error:', error);
throw error;
}
}
Common Error Codes
| Code | Meaning | Solution |
|---|---|---|
| 401 | Invalid API key | Check your API key |
| 429 | Rate limit exceeded | Wait and retry, or upgrade plan |
| 404 | Resource not found | Check address/network ID |
| 500+ | Server error | Retry with exponential backoff |
Best Practices
Security
- Never commit API keys to git
- Use environment variables
- Rotate keys periodically
- Use separate keys for dev/prod
Performance
- Batch token requests when possible
- Cache frequently accessed data
- Use appropriate timeframes for OHLCV
- Implement request queuing for rate limits
Data Quality
- Verify market cap data (may be null if unverified)
- Check pool liquidity before trusting prices
- Use multiple timeframes for price analysis
- Monitor last trade timestamp for activity
Resources
Skill Structure
coingecko/
├── SKILL.md # This file
├── resources/
│ ├── api-reference.md # Complete API endpoint reference
│ ├── network-dex-ids.md # Solana network and DEX identifiers
│ └── token-addresses.md # Common Solana token addresses
├── examples/
│ ├── token-prices/
│ │ └── get-token-price.ts # Token price examples
│ ├── pools/
│ │ └── pool-data.ts # Pool data examples
│ ├── ohlcv/
│ │ └── ohlcv-charts.ts # OHLCV chart examples
│ ├── trades/
│ │ └── recent-trades.ts # Trade history examples
│ └── integration/
│ └── full-client.ts # Complete client example
├── templates/
│ └── coingecko-client.ts # Production-ready client template
└── docs/
└── troubleshooting.md # Common issues and solutions
monad
View full →Author
@0xinit
Stars
53
Repository
0xinit/cryptoskills
Monad L1 Development
Chain Configuration
Mainnet
| Property | Value |
|---|---|
| Chain ID | 143 |
| Currency | MON (18 decimals) |
| EVM Version | Pectra fork |
| Block Time | 400ms |
| Finality | 800ms (2 slots) |
| Block Gas Limit | 200M |
| Tx Gas Limit | 30M |
| Gas Throughput | 500M gas/sec |
| Min Base Fee | 100 MON-gwei |
| Node Version | v0.12.7 / MONAD_EIGHT |
RPC Endpoints (Mainnet)
| URL | Provider | Rate Limit | Batch | Notes |
|---|---|---|---|---|
https://rpc.monad.xyz / wss://rpc.monad.xyz | QuickNode | 25 rps | 100 | Default |
https://rpc1.monad.xyz / wss://rpc1.monad.xyz | Alchemy | 15 rps | 100 | No debug/trace |
https://rpc2.monad.xyz / wss://rpc2.monad.xyz | Goldsky Edge | 300/10s | 10 | Historical state |
https://rpc3.monad.xyz / wss://rpc3.monad.xyz | Ankr | 300/10s | 10 | No debug |
https://rpc-mainnet.monadinfra.com / wss://rpc-mainnet.monadinfra.com | MF | 20 rps | 1 | Historical state |
Block Explorers
| Explorer | URL |
|---|---|
| MonadVision | https://monadvision.com |
| Monadscan | https://monadscan.com |
| Socialscan | https://monad.socialscan.io |
| Visualization | https://gmonads.com |
| Traces | Phalcon Explorer, Tenderly |
| UserOps | Jiffyscan |
Testnet
| Property | Value |
|---|---|
| Chain ID | 10143 |
| RPC | https://testnet-rpc.monad.xyz |
| WebSocket | wss://testnet-rpc.monad.xyz |
| Explorer | https://testnet.monadexplorer.com |
| Faucet | https://testnet.monad.xyz |
Key Differences from Ethereum
| Feature | Ethereum | Monad |
|---|---|---|
| Block time | 12s | 400ms |
| Finality | ~12-18 min | 800ms (2 slots) |
| Throughput | ~10 TPS | 10,000+ TPS |
| Gas charging | Gas used | Gas limit |
| Max contract size | 24.5 KB | 128 KB |
| Blob txns (EIP-4844) | Supported | Not supported |
| Global mempool | Yes | No (leader-based forwarding) |
| Account cold access | 2,600 gas | 10,100 gas |
| Storage cold access | 2,100 gas | 8,100 gas |
| Reserve balance | None | ~10 MON per account |
TIMESTAMP granularity | 1 per block | 2-3 blocks share same second |
| Precompile 0x0100 | N/A | EIP-7951 secp256r1 (P256) |
| EIP-7702 min balance | None | 10 MON for delegated EOAs |
| EIP-7702 CREATE/CREATE2 | Allowed | Banned for delegated EOAs |
| Tx types supported | 0,1,2,3,4 | 0,1,2,4 (no type 3) |
Gas Limit Charging Model
Monad charges gas_limit * price_per_gas, NOT gas_used * price_per_gas. This enables asynchronous execution — execution happens after consensus, so gas used isn't known at inclusion time.
gas_paid = gas_limit * price_per_gas
price_per_gas = min(base_price_per_gas + priority_price_per_gas, max_price_per_gas)
Set gas limits explicitly for fixed-cost operations (e.g., 21000 for transfers) to avoid overpaying.
Reserve Balance
Every account maintains a ~10 MON reserve for gas across the next 3 blocks. Transactions that would reduce balance below this threshold are rejected. This prevents DoS during asynchronous execution.
Block Lifecycle & Finality
Proposed → Voted (speculative finality, T+1) → Finalized (T+2) → Verified/state root (T+5)
| Phase | Latency | When to Use |
|---|---|---|
| Voted | 400ms | UI updates, most dApps |
| Finalized | 800ms | Conservative apps |
| Verified | ~2s | Exchanges, bridges, stablecoins |
Quick Start: viem Chain Definition
import { defineChain } from "viem";
export const monad = defineChain({
id: 143,
name: "Monad",
nativeCurrency: { name: "MON", symbol: "MON", decimals: 18 },
rpcUrls: {
default: { http: ["https://rpc.monad.xyz"], webSocket: ["wss://rpc.monad.xyz"] },
},
blockExplorers: {
default: { name: "MonadVision", url: "https://monadvision.com" },
monadscan: { name: "Monadscan", url: "https://monadscan.com" },
},
});
export const monadTestnet = defineChain({
id: 10143,
name: "Monad Testnet",
nativeCurrency: { name: "MON", symbol: "MON", decimals: 18 },
rpcUrls: {
default: { http: ["https://testnet-rpc.monad.xyz"], webSocket: ["wss://testnet-rpc.monad.xyz"] },
},
blockExplorers: {
default: { name: "Monad Explorer", url: "https://testnet.monadexplorer.com" },
},
testnet: true,
});
Quick Start: Foundry Setup
Install Monad Foundry Fork
curl -L https://raw.githubusercontent.com/category-labs/foundry/monad/foundryup/install | bash
foundryup --network monad
Project Init
forge init --template monad-developers/foundry-monad my-project
foundry.toml
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
evm_version = "prague"
[rpc_endpoints]
monad = "https://rpc.monad.xyz"
monad_testnet = "https://testnet-rpc.monad.xyz"
[etherscan]
monad = { key = "${ETHERSCAN_API_KEY}", chain = 143, url = "https://api.etherscan.io/v2/api?chainid=143" }
monad_testnet = { key = "${ETHERSCAN_API_KEY}", chain = 10143, url = "https://api.etherscan.io/v2/api?chainid=10143" }
Quick Start: Hardhat Configuration (v2)
const config: HardhatUserConfig = {
solidity: {
version: "0.8.28",
settings: {
evmVersion: "prague",
metadata: { bytecodeHash: "ipfs" },
},
},
networks: {
monadTestnet: {
url: "https://testnet-rpc.monad.xyz",
chainId: 10143,
accounts: [process.env.PRIVATE_KEY!],
},
monadMainnet: {
url: "https://rpc.monad.xyz",
chainId: 143,
accounts: [process.env.PRIVATE_KEY!],
},
},
etherscan: {
customChains: [{
network: "monadMainnet",
chainId: 143,
urls: {
apiURL: "https://api.etherscan.io/v2/api?chainid=143",
browserURL: "https://monadscan.com",
},
}],
},
sourcify: {
enabled: true,
apiUrl: "https://sourcify-api-monad.blockvision.org",
browserUrl: "https://monadvision.com",
},
};
Deployment
Foundry Deploy (Keystore)
cast wallet import monad-deployer --private-key $(cast wallet new | grep 'Private key:' | awk '{print $3}')
forge create src/MyContract.sol:MyContract \
--account monad-deployer \
--rpc-url https://rpc.monad.xyz \
--broadcast
forge create src/MyToken.sol:MyToken \
--account monad-deployer \
--rpc-url https://rpc.monad.xyz \
--constructor-args "MyToken" "MTK" 18 \
--broadcast
Foundry Deploy (Script)
forge script script/Deploy.s.sol \
--account monad-deployer \
--rpc-url https://rpc.monad.xyz \
--broadcast \
--slow
Hardhat Deploy
npx hardhat ignition deploy ignition/modules/Counter.ts --network monadMainnet
npx hardhat ignition deploy ignition/modules/Counter.ts --network monadMainnet --reset
Verification
MonadVision (Sourcify)
forge verify-contract <address> <ContractName> \
--chain 143 \
--verifier sourcify \
--verifier-url https://sourcify-api-monad.blockvision.org/
Monadscan (Etherscan)
forge verify-contract <address> <ContractName> \
--chain 143 \
--verifier etherscan \
--etherscan-api-key $ETHERSCAN_API_KEY \
--watch
Socialscan
forge verify-contract <address> <ContractName> \
--chain 143 \
--verifier etherscan \
--etherscan-api-key $SOCIALSCAN_API_KEY \
--verifier-url https://api.socialscan.io/monad-mainnet/v1/explorer/command_api/contract \
--watch
Hardhat Verify
npx hardhat verify <address> --network monadMainnet
For testnet verification, replace --chain 143 with --chain 10143 and use testnet RPC/explorer URLs.
Opcode Repricing Summary
Cold state access is ~4x more expensive on Monad than Ethereum. Warm access is identical.
| Access Type | Ethereum | Monad |
|---|---|---|
| Account (cold) | 2,600 | 10,100 |
| Storage slot (cold) | 2,100 | 8,100 |
| Account (warm) | 100 | 100 |
| Storage slot (warm) | 100 | 100 |
Selected precompile repricing:
| Precompile | Ethereum | Monad | Multiplier |
|---|---|---|---|
| ecRecover (0x01) | 3,000 | 6,000 | 2x |
| ecMul (0x07) | 6,000 | 30,000 | 5x |
| ecPairing (0x08) | 45,000 | 225,000 | 5x |
| point evaluation (0x0a) | 50,000 | 200,000 | 4x |
Monad-specific precompile: secp256r1 (P256) at 0x0100 for WebAuthn/passkey signature verification (EIP-7951).
EIP-1559 Parameters
| Parameter | Value |
|---|---|
| Block gas limit | 200M |
| Block gas target | 160M (80% of limit) |
| Per-transaction gas limit | 30M |
| Min base fee | 100 MON-gwei |
| Base fee max step size | 1/28 |
| Base fee decay factor | 0.96 |
The base fee controller increases slower and decreases faster than Ethereum's to prevent blockspace underutilization on a high-throughput chain.
Gas Optimization Tips
- Warm your storage — cold reads are 4x more expensive; use access lists (type 1/2 txns) for known slots
- Set explicit gas limits — you're charged for the limit, not usage
- Batch operations — high throughput means batching is less critical, but still saves gas limit overhead
- Avoid unnecessary cold precompile calls — ecPairing is 5x more expensive than Ethereum
- Design for parallel execution — per-user mappings over global counters where possible
- No blob transactions — use calldata for data availability
Parallel Execution
Monad executes transactions concurrently with optimistic conflict detection. No Solidity changes needed.
- Multiple virtual executors process transactions simultaneously
- Each generates "pending results" (inputs: SLOADs, outputs: SSTOREs)
- Serial commitment validates each result's inputs remain valid
- Conflict detected -> re-execute the affected transaction
- Results committed in original transaction order
Every transaction executes at most twice. Most transactions don't conflict, achieving near-linear speedup.
Parallel-Friendly Contract Design
| Pattern | Parallelizes Well | Why |
|---|---|---|
| Per-user mappings | Yes | Independent state per user |
| ERC-20 transfers between different pairs | Yes | Different storage slots |
| Global counter increment | No | All txns write same slot |
| AMM swaps on same pool | No | Same reserves storage |
| Independent NFT mints (incremental ID) | Partially | tokenId counter serializes |
Staking Precompile
Address: 0x0000000000000000000000000000000000001000
Only standard CALL is allowed. STATICCALL, DELEGATECALL, and CALLCODE are not permitted.
Core Functions
| Function | Selector | Gas Cost |
|---|---|---|
delegate(uint64) | 0x84994fec | 260,850 |
undelegate(uint64,uint256,uint8) | 0x5cf41514 | 147,750 |
compound(uint64) | 0xb34fea67 | 285,050 |
claimRewards(uint64) | 0xa76e2ca5 | 155,375 |
withdraw(uint64,uint8) | 0xaed2ee73 | 68,675 |
Delegate (Solidity)
address constant STAKING = 0x0000000000000000000000000000000000001000;
function delegateToValidator(uint64 validatorId) external payable {
(bool success,) = STAKING.call{value: msg.value}(
abi.encodeWithSelector(0x84994fec, validatorId)
);
require(success, "Delegation failed");
}
Delegate (viem)
import { encodeFunctionData } from "viem";
const STAKING_ADDRESS = "0x0000000000000000000000000000000000001000";
const hash = await walletClient.sendTransaction({
to: STAKING_ADDRESS,
value: parseEther("100"),
data: encodeFunctionData({
abi: [{ name: "delegate", type: "function", inputs: [{ name: "validatorId", type: "uint64" }], outputs: [] }],
functionName: "delegate",
args: [1n],
}),
});
EIP-7702 on Monad
Allows EOAs to gain smart contract capabilities via code delegation.
| Restriction | Detail |
|---|---|
| Minimum balance | Delegated EOAs cannot drop below 10 MON |
| CREATE/CREATE2 | Banned when delegated EOAs execute as smart contracts |
| Clearing delegation | Send type 0x04 pointing to address(0) |
import { walletClient } from "./client";
const authorization = await walletClient.signAuthorization({
account,
contractAddress: "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2",
});
const hash = await walletClient.sendTransaction({
authorizationList: [authorization],
data: "0xdeadbeef",
to: walletClient.account.address,
});
WebSocket Subscriptions
Standard eth_subscribe plus Monad-specific extensions:
newHeads — standard new block headers
logs — standard log filtering
monadNewHeads — Monad-specific block headers with extra fields
monadLogs — Monad-specific log events
Execution Events (Advanced)
For ultra-low-latency data consumption, Monad exposes execution events via shared-memory ring buffers. Consumer runs on same host as node. ~1 microsecond latency. Supported in C, C++, and Rust only.
Use execution events when JSON-RPC can't keep up with 10,000 TPS throughput. For most dApps, standard WebSocket subscriptions are sufficient.
Canonical Contracts
| Contract | Address |
|---|---|
| Wrapped MON (WMON) | 0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A |
| Staking Precompile | 0x0000000000000000000000000000000000001000 |
| Multicall3 | 0xcA11bde05977b3631167028862bE2a173976CA11 |
| USDC | 0x754704Bc059F8C67012fEd69BC8A327a5aafb603 |
| USDT0 | 0xe7cd86e13AC4309349F30B3435a9d337750fC82D |
| WETH | 0xEE8c0E9f1BFFb4Eb878d8f15f368A02a35481242 |
| WBTC | 0x0555E30da8f98308EdB960aa94C0Db47230d2B9c |
| ERC-4337 EntryPoint v0.7 | 0x0000000071727De22E5E9d8BAf0edAc6f37da032 |
| Safe | 0x69f4D1788e39c87893C980c06EdF4b7f686e2938 |
Supported Transaction Types
| Type | Name | Supported | Notes |
|---|---|---|---|
| 0 | Legacy | Yes | Pre-EIP-155 allowed but discouraged |
| 1 | EIP-2930 (access list) | Yes | |
| 2 | EIP-1559 (dynamic fee) | Yes | Recommended |
| 3 | EIP-4844 (blob) | No | Not supported on Monad |
| 4 | EIP-7702 (delegation) | Yes | With Monad-specific restrictions |
Smart Contract Tips
- Gas optimization still matters — even with cheap gas, optimize for users
- Same security model — all Solidity best practices (CEI, reentrancy guards) apply
- Parallel-friendly design — contracts with per-user mappings parallelize better than global counters
- 128 KB contract limit — larger contracts are possible but still optimize for gas
- No code changes needed for parallelism — it's at the runtime level
block.timestamp— 2-3 blocks may share the same second; don't rely on sub-second granularity- No blob transactions — EIP-4844 type 3 txns are not supported
Required Tooling Versions
| Tool | Minimum Version |
|---|---|
| Foundry | Monad fork (foundryup --network monad) |
| viem | 2.40.0+ |
| alloy-chains | 0.2.20+ |
| Hardhat Solidity | evmVersion: "prague" |
Pre-Deployment Checklist
- Using Monad Foundry fork or Hardhat with
evmVersion: "prague" - Correct chain ID (143 mainnet / 10143 testnet)
- Account funded with MON (remember ~10 MON reserve)
- Gas limit set explicitly for predictable cost (gas limit is charged, not gas used)
- Private key in env var, not hardcoded
- Contract size under 128 KB
- No EIP-4844 blob transactions (type 3 not supported)
- Verified on at least one explorer after deploy
Additional Reference
| File | Contents |
|---|---|
docs/architecture.md | MonadBFT consensus, parallel execution, deferred execution, MonadDb, JIT, RaptorCast |
docs/deployment.md | Foundry + Hardhat deploy/verify step-by-step guides |
docs/gas-and-opcodes.md | Gas pricing model, opcode repricing tables, precompile costs |
docs/staking.md | Staking precompile ABI, functions, events, epoch mechanics |
docs/ecosystem.md | Token addresses, bridges, oracles, indexers, canonical contracts |
docs/troubleshooting.md | Common issues and fixes for Monad development |
resources/contract-addresses.md | Key Monad contract addresses |
templates/deploy-monad.sh | Shell script for deploying to Monad |