check_token_permissions
Analyze token contract permissions to verify minting, pausing, blacklisting, fee changes, trading restrictions, and ownership status on Base mainnet.
Instructions
Check owner permissions on a token: can mint? can pause? can blacklist? can change fees? Can disable trading? Ownership renounced?
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| token_address | Yes | Token contract address on Base mainnet |
Implementation Reference
- src/index.ts:777-868 (handler)Implementation of the check_token_permissions MCP tool. It retrieves the contract bytecode, extracts selectors, checks for specific dangerous function selectors, retrieves ownership information, and summarizes the findings.
server.tool( "check_token_permissions", "Check owner permissions on a token: can mint? can pause? can blacklist? can change fees? Can disable trading? Ownership renounced?", { token_address: z.string().describe("Token contract address on Base mainnet"), }, async ({ token_address }) => { try { const code = await getContractBytecode(token_address); if (code === "0x" || code.length <= 2) { return ok({ token: token_address, isContract: false, message: "Not a contract" }); } const selectors = extractSelectors(code); const ownership = await checkOwnership(token_address); const permissions: Record<string, { present: boolean; risk: string; detail: string }> = { canMint: { present: selectors.includes("40c10f19"), risk: "critical", detail: "mint(address,uint256) -- can create new tokens", }, canPause: { present: selectors.includes("8456cb59"), risk: "high", detail: "pause() -- can freeze all transfers", }, canBlacklist: { present: selectors.includes("44337ea1"), risk: "high", detail: "blacklist(address) -- can block specific addresses", }, canChangeFees: { present: selectors.includes("69fe0e2d"), risk: "high", detail: "setFee(uint256) -- can change transaction fees", }, canDisableTrading: { present: selectors.includes("1a8145bb"), risk: "critical", detail: "setTradingActive(bool) -- can disable all trading", }, canSetMaxTx: { present: selectors.includes("e4748b9e"), risk: "high", detail: "setMaxTxAmount(uint256) -- can restrict transaction sizes", }, canSetMaxWallet: { present: selectors.includes("8ee88c53"), risk: "high", detail: "setMaxWalletSize(uint256) -- can restrict wallet holdings", }, canBurnOthers: { present: selectors.includes("79cc6790"), risk: "medium", detail: "burnFrom(address,uint256) -- can burn tokens from other addresses", }, canUpgrade: { present: selectors.includes("3659cfe6") || selectors.includes("4f1ef286"), risk: "critical", detail: "upgradeTo/upgradeToAndCall -- proxy can be upgraded to new logic", }, canRenounceOwnership: { present: selectors.includes("715018a6"), risk: "info", detail: "renounceOwnership() -- ownership can be given up (positive sign)", }, canTransferOwnership: { present: selectors.includes("f2fde38b"), risk: "medium", detail: "transferOwnership(address) -- ownership can be moved to another address", }, }; const dangerousPermissions = Object.entries(permissions) .filter(([, v]) => v.present && (v.risk === "critical" || v.risk === "high")) .map(([k]) => k); return ok({ token: token_address, ownership: serializeBigInts(ownership) as Record<string, unknown>, permissions, dangerousPermissions, riskSummary: dangerousPermissions.length === 0 ? "No dangerous owner permissions detected" : `${dangerousPermissions.length} dangerous permission(s) found: ${dangerousPermissions.join(", ")}`, }); } catch (err) { return fail(`check_token_permissions failed: ${err instanceof Error ? err.message : String(err)}`); } } );