Configuration
dap.config.js is the single source of truth for DappQL codegen, which contracts to emit, where to put them, what flags to set, what MCP gates to open. This page is the full field reference.
Minimal config
export default {
targetPath: './src/contracts',
contracts: {
Token: { address: '0x...', abi: [/* ... */] },
},
}Run npx dappql and you get ./src/contracts/Token.ts plus ./src/contracts/index.ts.
Full field reference
type Config = {
// Required
contracts: Record<string, ContractConfig>
targetPath: string
// Codegen behavior
isModule?: boolean
isSdk?: boolean
agentsFile?: boolean | string
// ABI fetching
abiSourcePath?: string
etherscanApiKey?: string
etherscanApi?: string
chainId?: number
// @dappql/mcp
mcp?: {
rpc?: string
allowWrites?: boolean
allowCodegen?: boolean
}
}
type ContractConfig = {
address?: `0x${string}`
abi?: AbiItem[]
isTemplate?: boolean
}contracts (required)
A map of contract names to { address, abi, isTemplate }. The key becomes the module name in generated code (ContractName.ts), the namespace import in your app, and the resolver key for addressResolver.
contracts: {
Token: {
address: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599',
abi: [/* ABI JSON */],
},
USDC: {
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
abi: [/* ABI JSON */],
},
// Template, no single canonical address; use .at() per call
ERC20: {
isTemplate: true,
abi: [/* ERC20 ABI */],
},
}Per-contract fields:
address, the deploy address. Required for singletons. Optional for templates, but useful as a canonical-implementation reference.abi, the full ABI array. If omitted andetherscanApiKeyis set, the CLI will fetch it.isTemplate, flag contracts deployed at many addresses. Generated code omits the baked-indeployAddress, and every call requires.at(addr)or anaddressoption on mutations. See Template contracts.
targetPath (required)
Where to write generated contract modules. Relative to the config file's directory. Conventional: './src/contracts'.
The CLI cleans this directory before emitting, so keep it dedicated to generated output, don't mix in hand-written files.
isModule
isModule: true // emits ESM import paths (import from './X.js')
isModule: false // default, omits extensions, matches TS/CJS projectsSet to true for modern ESM projects (your package.json has "type": "module"). For most Next.js/Vite apps, the default is fine.
isSdk
isSdk: trueFlip on to additionally emit ${targetPath}/sdk.ts, a createSdk(publicClient, walletClient, addressResolver?) factory that wraps every contract into a single typed object. This is how protocols ship their frontend primitives as publishable SDKs. See SDK generation.
agentsFile
agentsFile: true // default, write ./AGENTS.md at repo root
agentsFile: false // opt out
agentsFile: './docs/AGENTS.md' // custom locationControls the per-project AGENTS.md the CLI generates for AI coding agents. See Generated AGENTS.md.
chainId
chainId: 8453 // Base mainnetUsed in two places:
- Included in the generated
AGENTS.mdsetup section. - Passed to Etherscan when fetching ABIs for contracts that omit them.
Not required if every contract has an abi in config.
abiSourcePath
abiSourcePath: './abis'Load ABIs from local JSON files named ${ContractName}.json. Checked before falling back to Etherscan. Useful when you generate ABIs from Foundry/Hardhat builds.
etherscanApiKey + etherscanApi
etherscanApiKey: process.env.ETHERSCAN_API_KEY,
etherscanApi: 'https://api.etherscan.io/v2/api', // defaultIf a contract omits abi, the CLI fetches it from Etherscan. Set etherscanApi to override, the v2 API works for Basescan, Arbiscan, and other chain explorers that mirror it.
mcp
Settings for @dappql/mcp:
mcp: {
rpc: 'https://mainnet.base.org', // overrides DAPPQL_RPC_URL env
allowWrites: false, // gate for the callWrite tool
allowCodegen: false, // gate for the regenerate tool
}All optional. rpc is consulted before the env var; allowWrites and allowCodegen default to false. See Safety model.
Typical configurations
React app with embedded ABIs
export default {
targetPath: './src/contracts',
isModule: true,
agentsFile: true,
contracts: {
Token: { address: '0x...', abi: [/* ... */] },
ToDo: { address: '0x...', abi: [/* ... */] },
},
}Protocol shipping a publishable SDK
export default {
name: 'My Protocol SDK',
targetPath: './src/contracts',
chainId: 8453,
isModule: true,
isSdk: true,
contracts: {
Factory: { address: '0x...', abi: [/* ... */] },
Registry: { address: '0x...', abi: [/* ... */] },
UserVault: { isTemplate: true, abi: [/* ... */] },
ERC20: { isTemplate: true, abi: [/* ... */] },
},
}Auto-fetched ABIs from Etherscan
export default {
targetPath: './src/contracts',
chainId: 1,
etherscanApiKey: process.env.ETHERSCAN_API_KEY,
contracts: {
WETH: { address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' },
USDC: { address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' },
},
}ABIs resolve per contract in this order: embedded abi → local JSON at abiSourcePath/ContractName.json → Etherscan.
MCP-ready with gated codegen
export default {
targetPath: './src/contracts',
isSdk: true,
contracts: { /* ... */ },
mcp: {
rpc: 'https://mainnet.base.org',
allowCodegen: true, // lets the MCP regenerate tool run
// allowWrites left off, safest default
},
}Generated output
After npx dappql:
./src/contracts/
├── Token.ts # per-contract: abi, call, mutation, events, deployAddress
├── ToDo.ts
├── ERC20.ts # isTemplate: true, no deployAddress, .at() required
├── index.ts # re-exports each contract as a namespace
└── sdk.ts # only when isSdk: truePlus a project-level AGENTS.md (unless agentsFile: false).
File format and extensions
dap.config.js, the conventional filename. The CLI also acceptsdap.config.mjsanddap.config.cjs.- Use
export default(ESM) ormodule.exports = { ... }(CJS) depending on your project. - The file is imported at runtime by the CLI; any JavaScript you'd expect works, dynamic construction, env vars, conditional logic.
Dynamic configurations
The config file is just JavaScript, build it programmatically if that fits:
import manifest from './manifest.json' with { type: 'json' }
export default {
targetPath: './src/contracts',
chainId: manifest.chainId,
contracts: Object.fromEntries(
Object.entries(manifest.contracts).map(
([name, { address, abi }]) => [name, { address, abi }]
),
),
}Perfect for generated manifests (Foundry's out/ directory, a deploy pipeline that spits out addresses, or a cross-chain build that iterates over a list of chains).
Related
- Getting started, the short walkthrough.
- SDK generation, the
isSdk: trueflow. - Templates, when to flag a contract as a template.
- Generated AGENTS.md, what the
agentsFileflag controls. - Safety model, the
mcpblock in depth.