Skip to main content
Glama

OpenZeppelin Contracts MCP Server

Official
by OpenZeppelin
erc721.ts9.92 kB
import type { Contract, ContractTrait, StoredContractTrait } from './contract'; import { ContractBuilder } from './contract'; import type { CommonContractOptions } from './common-options'; import { withCommonContractDefaults, getSelfArg } from './common-options'; import { contractDefaults as commonDefaults } from './common-options'; import { printContract } from './print'; import { setAccessControl } from './set-access-control'; import { setInfo } from './set-info'; import { indentLine } from './utils/format-lines'; import { defineFunctions } from './utils/define-functions'; export interface ERC721Options extends CommonContractOptions { name: string; burnable?: boolean; enumerable?: boolean; } export const defaults: Required<ERC721Options> = { name: 'MyToken', burnable: false, enumerable: false, access: commonDefaults.access, info: commonDefaults.info, } as const; export function printERC721(opts: ERC721Options = defaults): string { return printContract(buildERC721(opts)); } function withDefaults(opts: ERC721Options): Required<ERC721Options> { return { ...opts, ...withCommonContractDefaults(opts), burnable: opts.burnable ?? defaults.burnable, enumerable: opts.enumerable ?? defaults.enumerable, }; } export function isAccessControlRequired(_opts: Partial<ERC721Options>): boolean { // return opts.pausable === true; return false; } export function buildERC721(opts: ERC721Options): Contract { const c = new ContractBuilder(opts.name); const allOpts = withDefaults(opts); addBase(c); if (allOpts.burnable) { addBurnable(c, allOpts.enumerable); } if (allOpts.enumerable) { addEnumerable(c); } setAccessControl(c, allOpts.access); setInfo(c, allOpts.info); return c; } function addBase(c: ContractBuilder) { c.addImplementedTrait(erc721Trait); c.addImplementedTrait(erc165Trait); } function addBurnable(c: ContractBuilder, enumerable: boolean) { c.addImplementedTrait(burnableTrait); if (enumerable) { c.addFunctionCodeBefore( functions.burnable.burn, [`let owner = self.${erc721Trait.storage.name}.owner_of(token_id)?;`], burnableTrait, ); c.addFunctionCodeAfter( functions.burnable.burn, [ `self.${enumerableTrait.storage.name}._remove_token_from_owner_enumeration(owner, token_id, &self.${erc721Trait.storage.name})?;`, `self.${enumerableTrait.storage.name}._remove_token_from_all_tokens_enumeration(token_id);`, 'Ok(())', ], burnableTrait, ); } } function addEnumerable(c: ContractBuilder) { c.addImplementedTrait(enumerableTrait); c.addFunctionCodeAfter( functions.erc165.supports_interface, [indentLine(`|| self.${enumerableTrait.storage.name}.supports_interface(interface_id)`, 1)], erc165Trait, ); for (const fn of [ functions.erc721.safe_transfer_from, functions.erc721.safe_transfer_from_with_data, functions.erc721.transfer_from, ]) { c.addFunctionCodeBefore( fn, [`let previous_owner = self.${erc721Trait.storage.name}.owner_of(token_id)?;`], erc721Trait, ); c.addFunctionCodeAfter( fn, [ `self.${enumerableTrait.storage.name}._remove_token_from_owner_enumeration(previous_owner, token_id, &self.${erc721Trait.storage.name})?;`, `self.${enumerableTrait.storage.name}._add_token_to_owner_enumeration(to, token_id, &self.${erc721Trait.storage.name})?;`, 'Ok(())', ], erc721Trait, ); } } const ERC721_STORAGE_NAME = 'erc721'; const ENUMERABLE_STORAGE_NAME = 'enumerable'; const functions = { erc721: defineFunctions({ balance_of: { args: [getSelfArg('immutable'), { name: 'owner', type: 'Address' }], returns: { ok: 'U256', err: 'Self::Error' }, code: `self.${ERC721_STORAGE_NAME}.balance_of(owner)?`, }, owner_of: { args: [getSelfArg('immutable'), { name: 'token_id', type: 'U256' }], returns: { ok: 'Address', err: 'Self::Error' }, code: `self.${ERC721_STORAGE_NAME}.owner_of(token_id)?`, }, safe_transfer_from: { args: [ getSelfArg(), { name: 'from', type: 'Address' }, { name: 'to', type: 'Address' }, { name: 'token_id', type: 'U256' }, ], returns: { ok: '()', err: 'Self::Error' }, code: `self.${ERC721_STORAGE_NAME}.safe_transfer_from(from, to, token_id)?`, }, safe_transfer_from_with_data: { attribute: 'selector(name = "safeTransferFrom")', args: [ getSelfArg(), { name: 'from', type: 'Address' }, { name: 'to', type: 'Address' }, { name: 'token_id', type: 'U256' }, { name: 'data', type: 'Bytes' }, ], returns: { ok: '()', err: 'Self::Error' }, code: `self.${ERC721_STORAGE_NAME}.safe_transfer_from_with_data(from, to, token_id, data)?`, }, transfer_from: { args: [ getSelfArg(), { name: 'from', type: 'Address' }, { name: 'to', type: 'Address' }, { name: 'token_id', type: 'U256' }, ], returns: { ok: '()', err: 'Self::Error' }, code: `self.${ERC721_STORAGE_NAME}.transfer_from(from, to, token_id)?`, }, approve: { args: [getSelfArg(), { name: 'to', type: 'Address' }, { name: 'token_id', type: 'U256' }], returns: { ok: '()', err: 'Self::Error' }, code: `self.${ERC721_STORAGE_NAME}.approve(to, token_id)?`, }, set_approval_for_all: { args: [getSelfArg(), { name: 'operator', type: 'Address' }, { name: 'approved', type: 'bool' }], returns: { ok: '()', err: 'Self::Error' }, code: `self.${ERC721_STORAGE_NAME}.set_approval_for_all(operator, approved)?`, }, get_approved: { args: [getSelfArg('immutable'), { name: 'token_id', type: 'U256' }], returns: { ok: 'Address', err: 'Self::Error' }, code: `self.${ERC721_STORAGE_NAME}.get_approved(token_id)?`, }, is_approved_for_all: { args: [getSelfArg('immutable'), { name: 'owner', type: 'Address' }, { name: 'operator', type: 'Address' }], returns: 'bool', code: `self.${ERC721_STORAGE_NAME}.is_approved_for_all(owner, operator)`, }, }), erc165: defineFunctions({ supports_interface: { args: [getSelfArg('immutable'), { name: 'interface_id', type: 'FixedBytes<4>' }], returns: 'bool', code: `self.${ERC721_STORAGE_NAME}.supports_interface(interface_id)`, }, }), burnable: defineFunctions({ burn: { args: [getSelfArg(), { name: 'token_id', type: 'U256' }], returns: { ok: '()', err: 'Self::Error' }, code: `self.${ERC721_STORAGE_NAME}.burn(token_id)?`, }, }), enumerable: defineFunctions({ token_of_owner_by_index: { args: [getSelfArg('immutable'), { name: 'owner', type: 'Address' }, { name: 'index', type: 'U256' }], returns: { ok: 'U256', err: 'Self::Error' }, code: `self.${ENUMERABLE_STORAGE_NAME}.token_of_owner_by_index(owner, index)?`, }, total_supply: { args: [getSelfArg('immutable')], returns: 'U256', code: `self.${ENUMERABLE_STORAGE_NAME}.total_supply()`, }, token_by_index: { args: [getSelfArg('immutable'), { name: 'index', type: 'U256' }], returns: { ok: 'U256', err: 'Self::Error' }, code: `self.${ENUMERABLE_STORAGE_NAME}.token_by_index(index)?`, }, }), }; const erc721Trait: StoredContractTrait = { name: 'IErc721', associatedError: true, errors: [ { variant: 'InvalidOwner', value: { module: 'erc721', error: 'ERC721InvalidOwner' } }, { variant: 'NonexistentToken', value: { module: 'erc721', error: 'ERC721NonexistentToken' } }, { variant: 'IncorrectOwner', value: { module: 'erc721', error: 'ERC721IncorrectOwner' } }, { variant: 'InvalidSender', value: { module: 'erc721', error: 'ERC721InvalidSender' } }, { variant: 'InvalidReceiver', value: { module: 'erc721', error: 'ERC721InvalidReceiver' } }, { variant: 'InvalidReceiverWithReason', value: { module: 'erc721', error: 'InvalidReceiverWithReason' } }, { variant: 'InsufficientApproval', value: { module: 'erc721', error: 'ERC721InsufficientApproval' } }, { variant: 'InvalidApprover', value: { module: 'erc721', error: 'ERC721InvalidApprover' } }, { variant: 'InvalidOperator', value: { module: 'erc721', error: 'ERC721InvalidOperator' } }, ], storage: { name: ERC721_STORAGE_NAME, type: 'Erc721', }, modulePath: 'openzeppelin_stylus::token::erc721', priority: 1, requiredImports: [ { containerPath: 'alloc::vec', name: 'Vec' }, { containerPath: 'stylus_sdk::abi', name: 'Bytes' }, { containerPath: 'stylus_sdk::alloy_primitives', name: 'Address' }, { containerPath: 'stylus_sdk::alloy_primitives', name: 'U256' }, ], functions: Object.values(functions.erc721), }; const erc165Trait: ContractTrait = { name: 'IErc165', modulePath: 'openzeppelin_stylus::utils::introspection::erc165', priority: 4, requiredImports: [{ containerPath: 'stylus_sdk::alloy_primitives', name: 'FixedBytes' }], functions: Object.values(functions.erc165), }; const burnableTrait: ContractTrait = { name: 'IErc721Burnable', associatedError: true, modulePath: 'openzeppelin_stylus::token::erc721::extensions::burnable', priority: 3, functions: Object.values(functions.burnable), }; const enumerableTrait: StoredContractTrait = { name: 'IErc721Enumerable', associatedError: true, errors: [ { variant: 'OutOfBoundsIndex', value: { module: 'enumerable', error: 'ERC721OutOfBoundsIndex' } }, { variant: 'EnumerableForbiddenBatchMint', value: { module: 'enumerable', error: 'ERC721EnumerableForbiddenBatchMint' }, }, ], storage: { name: ENUMERABLE_STORAGE_NAME, type: 'Erc721Enumerable', }, modulePath: 'openzeppelin_stylus::token::erc721::extensions::enumerable', priority: 2, functions: Object.values(functions.enumerable), };

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