Skip to main content
Glama

OpenZeppelin Contracts MCP Server

Official
by OpenZeppelin
print.ts9.02 kB
import type { Contract, Parent, ContractFunction, FunctionArgument, Value, NatspecTag, ImportContract, } from './contract'; import type { Options, Helpers } from './options'; import { withHelpers } from './options'; import type { Lines } from './utils/format-lines'; import { formatLines, spaceBetween } from './utils/format-lines'; import { mapValues } from './utils/map-values'; import SOLIDITY_VERSION from './solidity-version.json'; import { inferTranspiled } from './infer-transpiled'; import { compatibleContractsSemver } from './utils/version'; import { stringifyUnicodeSafe } from './utils/sanitize'; import { importsCommunityContracts } from './utils/imports-libraries'; import { getCommunityContractsGitCommit } from './utils/community-contracts-git-commit'; export function printContract(contract: Contract, opts?: Options): string { const helpers = withHelpers(contract, opts); const fns = mapValues(sortedFunctions(contract), fns => fns.map(fn => printFunction(fn, helpers))); const hasOverrides = fns.override.some(l => l.length > 0); return formatLines( ...spaceBetween( [ `// SPDX-License-Identifier: ${contract.license}`, printCompatibleLibraryVersions(contract), `pragma solidity ^${SOLIDITY_VERSION};`, ], printImports(contract.imports, helpers), [ ...printNatspecTags(contract.natspecTags), [`contract ${contract.name}`, ...printInheritance(contract, helpers), '{'].join(' '), spaceBetween( contract.variables, printConstructor(contract, helpers), ...fns.code, ...fns.modifiers, hasOverrides ? [`// The following functions are overrides required by Solidity.`] : [], ...fns.override, ), `}`, ], ), ); } function printCompatibleLibraryVersions(contract: Contract): string { let result = `// Compatible with OpenZeppelin Contracts ${compatibleContractsSemver}`; if (importsCommunityContracts(contract)) { try { const commit = getCommunityContractsGitCommit(); result += ` and Community Contracts commit ${commit}`; } catch (e) { console.error(e); } } return result; } function printInheritance(contract: Contract, { transformName }: Helpers): [] | [string] { if (contract.parents.length > 0) { return ['is ' + contract.parents.map(p => transformName(p.contract)).join(', ')]; } else { return []; } } function printConstructor(contract: Contract, helpers: Helpers): Lines[] { const hasParentParams = contract.parents.some(p => p.params.length > 0); const hasConstructorCode = contract.constructorCode.length > 0; const parentsWithInitializers = contract.parents.filter(hasInitializer); if (hasParentParams || hasConstructorCode || (helpers.upgradeable && parentsWithInitializers.length > 0)) { if (helpers.upgradeable) { const upgradeableParents = parentsWithInitializers.filter(p => inferTranspiled(p.contract)); const nonUpgradeableParents = contract.parents.filter(p => !inferTranspiled(p.contract)); const constructor = printFunction2( [ nonUpgradeableParents.length > 0 ? '/// @custom:oz-upgrades-unsafe-allow-reachable constructor' : '/// @custom:oz-upgrades-unsafe-allow constructor', ], 'constructor', [], nonUpgradeableParents.flatMap(p => printParentConstructor(p, helpers)), ['_disableInitializers();'], ); const initializer = printFunction2( [], 'function initialize', contract.constructorArgs.map(a => printArgument(a, helpers)), ['public', 'initializer'], spaceBetween( upgradeableParents.flatMap(p => printParentConstructor(p, helpers)).map(p => p + ';'), contract.constructorCode, ), ); return spaceBetween(constructor, upgradeableParents.length > 0 ? initializer : []); } else { return printFunction2( [], 'constructor', contract.constructorArgs.map(a => printArgument(a, helpers)), contract.parents.flatMap(p => printParentConstructor(p, helpers)), contract.constructorCode, ); } } else if (!helpers.upgradeable) { return []; } else { return DISABLE_INITIALIZERS; } } const DISABLE_INITIALIZERS = [ '/// @custom:oz-upgrades-unsafe-allow constructor', 'constructor() {', ['_disableInitializers();'], '}', ]; function hasInitializer(parent: Parent) { // CAUTION // This list is validated by compilation of SafetyCheck.sol. // Always keep this list and that file in sync. return !['Initializable'].includes(parent.contract.name); } type SortedFunctions = Record<'code' | 'modifiers' | 'override', ContractFunction[]>; // Functions with code first, then those with modifiers, then the rest function sortedFunctions(contract: Contract): SortedFunctions { const fns: SortedFunctions = { code: [], modifiers: [], override: [] }; for (const fn of contract.functions) { if (fn.code.length > 0) { fns.code.push(fn); } else if (fn.modifiers.length > 0) { fns.modifiers.push(fn); } else { fns.override.push(fn); } } return fns; } function printParentConstructor({ contract, params }: Parent, helpers: Helpers): [] | [string] { const useTranspiled = helpers.upgradeable && inferTranspiled(contract); const fn = useTranspiled ? `__${contract.name}_init` : contract.name; if (useTranspiled || params.length > 0) { return [fn + '(' + params.map(printValue).join(', ') + ')']; } else { return []; } } export function printValue(value: Value): string { if (typeof value === 'object') { if ('lit' in value) { return value.lit; } else if ('note' in value) { return `${printValue(value.value)} /* ${value.note} */`; } else { throw Error('Unknown value type'); } } else if (typeof value === 'number') { if (Number.isSafeInteger(value)) { return value.toFixed(0); } else { throw new Error(`Number not representable (${value})`); } } else { return stringifyUnicodeSafe(value); } } function printFunction(fn: ContractFunction, helpers: Helpers): Lines[] { const { transformName } = helpers; if (fn.override.size <= 1 && fn.modifiers.length === 0 && fn.code.length === 0 && !fn.final) { return []; } const modifiers: string[] = [fn.kind]; if (fn.mutability !== 'nonpayable') { modifiers.push(fn.mutability); } if (fn.override.size === 1) { modifiers.push(`override`); } else if (fn.override.size > 1) { modifiers.push(`override(${[...fn.override].map(transformName).join(', ')})`); } modifiers.push(...fn.modifiers); if (fn.returns?.length) { modifiers.push(`returns (${fn.returns.join(', ')})`); } const code = [...fn.code]; if (fn.override.size > 0 && !fn.final) { const superCall = `super.${fn.name}(${fn.args.map(a => a.name).join(', ')});`; code.push(fn.returns?.length ? 'return ' + superCall : superCall); } if (modifiers.length + fn.code.length > 1) { return printFunction2( fn.comments, 'function ' + fn.name, fn.args.map(a => printArgument(a, helpers)), modifiers, code, ); } else { return []; } } // generic for functions and constructors // kindedName = 'function foo' or 'constructor' function printFunction2( comments: string[], kindedName: string, args: string[], modifiers: string[], code: Lines[], ): Lines[] { const fn: Lines[] = [...comments]; const headingLength = [kindedName, ...args, ...modifiers].map(s => s.length).reduce((a, b) => a + b); const braces = code.length > 0 ? '{' : '{}'; if (headingLength <= 72) { fn.push([`${kindedName}(${args.join(', ')})`, ...modifiers, braces].join(' ')); } else { fn.push(`${kindedName}(${args.join(', ')})`, modifiers, braces); } if (code.length > 0) { fn.push(code, '}'); } return fn; } function printArgument(arg: FunctionArgument, { transformName }: Helpers): string { let type: string; if (typeof arg.type === 'string') { if (/^[A-Z]/.test(arg.type)) { // eslint-disable-next-line @typescript-eslint/no-unused-expressions `Type ${arg.type} is not a primitive type. Define it as a ContractReference`; } type = arg.type; } else { type = transformName(arg.type); } return [type, arg.name].join(' '); } function printNatspecTags(tags: NatspecTag[]): string[] { return tags.map(({ key, value }) => `/// ${key} ${value}`); } function printImports(imports: ImportContract[], helpers: Helpers): string[] { // Sort imports by name imports.sort((a, b) => { if (a.name < b.name) return -1; if (a.name > b.name) return 1; return 0; }); const lines: string[] = []; imports.map(p => { const importContract = helpers.transformImport(p); lines.push(`import {${importContract.name}} from "${importContract.path}";`); }); return lines; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/OpenZeppelin/contracts-wizard'

If you have feedback or need assistance with the MCP directory API, please join our Discord server