Skip to main content
Glama

OpenZeppelin Contracts MCP Server

Official
by OpenZeppelin
zip-foundry.test.ts6.5 kB
import type { TestFn, ExecutionContext } from 'ava'; import _test from 'ava'; import { zipFoundry } from './zip-foundry'; import { buildERC20 } from './erc20'; import { buildERC721 } from './erc721'; import { buildERC1155 } from './erc1155'; import { buildAccount } from './account'; import { buildCustom } from './custom'; import { promises as fs } from 'fs'; import path from 'path'; import os from 'os'; import util from 'util'; import child from 'child_process'; import type { Contract } from './contract'; import { rimraf } from 'rimraf'; import type { JSZipObject } from 'jszip'; import type JSZip from 'jszip'; import type { GenericOptions } from './build-generic'; interface Context { tempFolder: string; } const test = _test as TestFn<Context>; test.beforeEach(async t => { t.context.tempFolder = await fs.mkdtemp(path.join(os.tmpdir(), 'openzeppelin-wizard-')); }); test.afterEach.always(async t => { await rimraf(t.context.tempFolder); }); test.serial('erc20 full', async t => { const opts: GenericOptions = { kind: 'ERC20', name: 'My Token', symbol: 'MTK', premint: '2000', access: 'roles', burnable: true, mintable: true, pausable: true, permit: true, votes: true, flashmint: true, }; const c = buildERC20(opts); await runTest(c, t, opts); }); test.serial('erc20 ownable, uups, crossChainBridging custom', async t => { const opts: GenericOptions = { kind: 'ERC20', name: 'My Token', symbol: 'MTK', premint: '2000', access: 'ownable', burnable: true, mintable: true, pausable: true, permit: true, votes: true, flashmint: true, crossChainBridging: 'custom', premintChainId: '10', upgradeable: 'uups', }; const c = buildERC20(opts); await runTest(c, t, opts); }); test.serial('erc20 uups, roles', async t => { const opts: GenericOptions = { kind: 'ERC20', name: 'My Token', symbol: 'MTK', upgradeable: 'uups', access: 'roles', }; const c = buildERC20(opts); await runTest(c, t, opts); }); test.serial('erc721 uups, ownable', async t => { const opts: GenericOptions = { kind: 'ERC721', name: 'My Token', symbol: 'MTK', upgradeable: 'uups', access: 'ownable', }; const c = buildERC721(opts); await runTest(c, t, opts); }); test.serial('erc1155 basic', async t => { const opts: GenericOptions = { kind: 'ERC1155', name: 'My Token', uri: 'https://myuri/{id}', }; const c = buildERC1155(opts); await runTest(c, t, opts); }); test.serial('erc1155 transparent, ownable', async t => { const opts: GenericOptions = { kind: 'ERC1155', name: 'My Token', uri: 'https://myuri/{id}', upgradeable: 'transparent', access: 'ownable', }; const c = buildERC1155(opts); await runTest(c, t, opts); }); test.serial('account ecdsa', async t => { const opts: GenericOptions = { kind: 'Account', name: 'My Account', signer: 'ECDSA' }; const c = buildAccount(opts); await runTest(c, t, opts); }); test.serial('account ecdsa uups', async t => { const opts: GenericOptions = { kind: 'Account', name: 'My Account', signer: 'ECDSA', upgradeable: 'uups' }; const c = buildAccount(opts); await runTest(c, t, opts); }); test.serial('custom basic', async t => { const opts: GenericOptions = { kind: 'Custom', name: 'My Contract' }; const c = buildCustom(opts); await runTest(c, t, opts); }); test.serial('custom transparent, managed', async t => { const opts: GenericOptions = { kind: 'Custom', name: 'My Contract', upgradeable: 'transparent', access: 'managed', }; const c = buildCustom(opts); await runTest(c, t, opts); }); async function runTest(c: Contract, t: ExecutionContext<Context>, opts: GenericOptions) { const zip = await zipFoundry(c, opts); assertLayout(zip, c, t); await extractAndRunPackage(zip, c, t); await assertContents(zip, c, t); } function assertLayout(zip: JSZip, c: Contract, t: ExecutionContext<Context>) { const sorted = Object.keys(zip.files).sort(); t.deepEqual(sorted, [ 'README.md', 'script/', `script/${c.name}.s.sol`, 'setup.sh', 'src/', `src/${c.name}.sol`, 'test/', `test/${c.name}.t.sol`, ]); } async function extractAndRunPackage(zip: JSZip, c: Contract, t: ExecutionContext<Context>) { const files = Object.values(zip.files); const tempFolder = t.context.tempFolder; const items = Object.values(files); for (const item of items) { if (item.dir) { await fs.mkdir(path.join(tempFolder, item.name)); } else { await fs.writeFile(path.join(tempFolder, item.name), await asString(item)); } } const setGitUser = 'git init && git config user.email "test@test.test" && git config user.name "Test"'; const setup = 'bash setup.sh'; const test = 'forge test' + (c.upgradeable ? ' --force' : ''); const script = `forge script script/${c.name}.s.sol` + (c.upgradeable ? ' --force' : ''); const exec = (cmd: string) => util.promisify(child.exec)(cmd, { env: { ...process.env, NO_COLOR: '' } }); const command = `cd "${tempFolder}" && ${setGitUser} && ${setup} && ${test} && ${script}`; const result = await exec(command); t.regex(result.stdout, /Initializing Foundry project\.\.\.\nDone\./); t.regex(result.stdout, /1 passed/); if (c.constructorArgs === undefined) { // the deployment is only run by default if there are no constructor args t.regex(result.stdout, /deployed to /); } const rerunCommand = `cd "${tempFolder}" && ${setup}`; const rerunResult = await exec(rerunCommand); t.regex(rerunResult.stdout, /Foundry project already initialized\./); } async function assertContents(zip: JSZip, c: Contract, t: ExecutionContext<Context>) { const normalizeVersion = (text: string) => text.replace(/\bv\d+\.\d+\.\d+\b/g, 'vX.Y.Z'); const contentComparison = [ normalizeVersion(await getItemString(zip, `setup.sh`)), await getItemString(zip, `README.md`), await getItemString(zip, `script/${c.name}.s.sol`), await getItemString(zip, `src/${c.name}.sol`), await getItemString(zip, `test/${c.name}.t.sol`), ]; t.snapshot(contentComparison); } async function getItemString(zip: JSZip, key: string) { const obj = zip.files[key]; if (obj === undefined) { throw Error(`Item ${key} not found in zip`); } return await asString(obj); } async function asString(item: JSZipObject) { return Buffer.from(await item.async('arraybuffer')).toString(); }

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