Skip to main content
Glama

OpenZeppelin Contracts MCP Server

Official
by OpenZeppelin
non-fungible.ts13.9 kB
import type { Contract } from './contract'; import { ContractBuilder } from './contract'; import { type Access, requireAccessControl, setAccessControl } from './set-access-control'; import { addPausable } from './add-pausable'; import { addUpgradeable } from './add-upgradeable'; import { defineFunctions } from './utils/define-functions'; import type { CommonContractOptions } from './common-options'; import { withCommonContractDefaults, getSelfArg } from './common-options'; import { setInfo } from './set-info'; import type { OptionsErrorMessages } from './error'; import { OptionsError } from './error'; import { contractDefaults as commonDefaults } from './common-options'; import { printContract } from './print'; import { toByteArray } from './utils/convert-strings'; export const defaults: Required<NonFungibleOptions> = { name: 'MyToken', symbol: 'MTK', burnable: false, enumerable: false, consecutive: false, pausable: false, upgradeable: false, mintable: false, sequential: false, access: commonDefaults.access, // TODO: Determine whether Access Control options should be visible in the UI before they are implemented as modules info: commonDefaults.info, } as const; export function printNonFungible(opts: NonFungibleOptions = defaults): string { return printContract(buildNonFungible(opts)); } export interface NonFungibleOptions extends CommonContractOptions { name: string; symbol: string; burnable?: boolean; enumerable?: boolean; consecutive?: boolean; pausable?: boolean; upgradeable?: boolean; mintable?: boolean; sequential?: boolean; } function withDefaults(opts: NonFungibleOptions): Required<NonFungibleOptions> { return { ...opts, ...withCommonContractDefaults(opts), burnable: opts.burnable ?? defaults.burnable, consecutive: opts.consecutive ?? defaults.consecutive, enumerable: opts.enumerable ?? defaults.enumerable, pausable: opts.pausable ?? defaults.pausable, upgradeable: opts.upgradeable ?? defaults.upgradeable, mintable: opts.mintable ?? defaults.mintable, sequential: opts.sequential ?? defaults.sequential, }; } export function isAccessControlRequired(opts: Partial<NonFungibleOptions>): boolean { return opts.mintable === true || opts.pausable === true || opts.upgradeable === true || opts.consecutive === true; } export function buildNonFungible(opts: NonFungibleOptions): Contract { const c = new ContractBuilder(opts.name); const allOpts = withDefaults(opts); const errors: OptionsErrorMessages = {}; if (allOpts.enumerable && allOpts.consecutive) { errors.enumerable = 'Enumerable cannot be used with Consecutive extension'; errors.consecutive = 'Consecutive cannot be used with Enumerable extension'; } if (allOpts.consecutive && allOpts.mintable) { errors.consecutive = 'Consecutive cannot be used with Mintable extension'; errors.mintable = 'Mintable cannot be used with Consecutive extension'; } if (allOpts.consecutive && allOpts.sequential) { errors.consecutive = 'Consecutive cannot be used with Sequential minting'; errors.sequential = 'Sequential minting cannot be used with Consecutive extension'; } if (Object.keys(errors).length > 0) { throw new OptionsError(errors); } addBase(c, toByteArray(allOpts.name), toByteArray(allOpts.symbol), allOpts.pausable); if (allOpts.pausable) { addPausable(c, allOpts.access); } if (allOpts.upgradeable) { addUpgradeable(c, allOpts.access); } if (allOpts.burnable) { addBurnable(c, allOpts.pausable); } if (allOpts.enumerable) { addEnumerable(c); } if (allOpts.consecutive) { addConsecutive(c, allOpts.pausable, allOpts.access); } if (allOpts.mintable) { addMintable(c, allOpts.enumerable, allOpts.pausable, allOpts.sequential, allOpts.access); } setAccessControl(c, allOpts.access); setInfo(c, allOpts.info); return c; } function addBase(c: ContractBuilder, name: string, symbol: string, pausable: boolean) { // Set metadata c.addConstructorCode('let uri = String::from_str(e, "www.mytoken.com");'); c.addConstructorCode(`let name = String::from_str(e, "${name}");`); c.addConstructorCode(`let symbol = String::from_str(e, "${symbol}");`); c.addConstructorCode(`Base::set_metadata(e, uri, name, symbol);`); // Set token functions c.addUseClause('stellar_tokens::non_fungible', 'Base'); c.addUseClause('stellar_tokens::non_fungible', 'NonFungibleToken'); c.addUseClause('stellar_macros', 'default_impl'); c.addUseClause('soroban_sdk', 'contract'); c.addUseClause('soroban_sdk', 'contractimpl'); c.addUseClause('soroban_sdk', 'String'); c.addUseClause('soroban_sdk', 'Env'); const nonFungibleTokenTrait = { traitName: 'NonFungibleToken', structName: c.name, tags: ['default_impl', 'contractimpl'], assocType: 'type ContractType = Base;', }; c.addTraitImplBlock(nonFungibleTokenTrait); if (pausable) { c.addUseClause('stellar_macros', 'when_not_paused'); c.addTraitFunction(nonFungibleTokenTrait, baseFunctions.transfer); c.addFunctionTag(baseFunctions.transfer, 'when_not_paused', nonFungibleTokenTrait); c.addFunctionTag(baseFunctions.transfer_from, 'when_not_paused', nonFungibleTokenTrait); c.addTraitFunction(nonFungibleTokenTrait, baseFunctions.transfer_from); } } function addBurnable(c: ContractBuilder, pausable: boolean) { c.addUseClause('stellar_tokens::non_fungible', 'burnable::NonFungibleBurnable'); const nonFungibleBurnableTrait = { traitName: 'NonFungibleBurnable', structName: c.name, tags: ['contractimpl'], section: 'Extensions', }; if (pausable) { c.addUseClause('stellar_macros', 'when_not_paused'); c.addTraitFunction(nonFungibleBurnableTrait, burnableFunctions.burn); c.addFunctionTag(burnableFunctions.burn, 'when_not_paused', nonFungibleBurnableTrait); c.addTraitFunction(nonFungibleBurnableTrait, burnableFunctions.burn_from); c.addFunctionTag(burnableFunctions.burn_from, 'when_not_paused', nonFungibleBurnableTrait); } else { // prepend '#[default_impl]' nonFungibleBurnableTrait.tags.unshift('default_impl'); c.addTraitImplBlock(nonFungibleBurnableTrait); } } function addEnumerable(c: ContractBuilder) { c.addUseClause('stellar_tokens::non_fungible', 'enumerable::{NonFungibleEnumerable, Enumerable}'); c.addUseClause('stellar_macros', 'default_impl'); const nonFungibleEnumerableTrait = { traitName: 'NonFungibleEnumerable', structName: c.name, tags: ['default_impl', 'contractimpl'], section: 'Extensions', }; c.addTraitImplBlock(nonFungibleEnumerableTrait); c.overrideAssocType('NonFungibleToken', 'type ContractType = Enumerable;'); } function addConsecutive(c: ContractBuilder, pausable: boolean, access: Access) { c.addUseClause('stellar_tokens::non_fungible', 'consecutive::{NonFungibleConsecutive, Consecutive}'); c.addUseClause('soroban_sdk', 'Address'); const nonFungibleConsecutiveTrait = { traitName: 'NonFungibleConsecutive', structName: c.name, tags: ['contractimpl'], section: 'Extensions', }; c.addTraitImplBlock(nonFungibleConsecutiveTrait); c.overrideAssocType('NonFungibleToken', 'type ContractType = Consecutive;'); const mintFn = access === 'ownable' ? consecutiveFunctions.batch_mint : consecutiveFunctions.batch_mint_with_caller; c.addFreeFunction(mintFn); if (pausable) { c.addFunctionTag(mintFn, 'when_not_paused'); } requireAccessControl(c, undefined, mintFn, access, { useMacro: true, role: 'minter', caller: 'caller', }); } function addMintable(c: ContractBuilder, enumerable: boolean, pausable: boolean, sequential: boolean, access: Access) { c.addUseClause('soroban_sdk', 'Address'); const accessProps = { useMacro: true, role: 'minter', caller: 'caller' }; let mintFn; if (enumerable) { if (sequential) { mintFn = access === 'ownable' ? enumerableFunctions.sequential_mint : enumerableFunctions.sequential_mint_with_caller; } else { mintFn = access === 'ownable' ? enumerableFunctions.non_sequential_mint : enumerableFunctions.non_sequential_mint_with_caller; } } else { if (sequential) { mintFn = access === 'ownable' ? baseFunctions.sequential_mint : baseFunctions.sequential_mint_with_caller; } else { mintFn = access === 'ownable' ? baseFunctions.mint : baseFunctions.mint_with_caller; } } c.addFreeFunction(mintFn); requireAccessControl(c, undefined, mintFn, access, accessProps); if (pausable) { c.addFunctionTag(mintFn, 'when_not_paused'); } } const baseFunctions = defineFunctions({ // NonFungible Trait balance: { args: [getSelfArg(), { name: 'owner', type: 'Address' }], returns: 'u32', code: ['Self::ContractType::balance(e, &owner)'], }, owner_of: { args: [getSelfArg(), { name: 'token_id', type: 'u32' }], returns: 'Address', code: ['Self::ContractType::owner_of(e, token_id)'], }, transfer: { args: [ getSelfArg(), { name: 'from', type: 'Address' }, { name: 'to', type: 'Address' }, { name: 'token_id', type: 'u32' }, ], code: ['Self::ContractType::transfer(e, &from, &to, token_id);'], }, transfer_from: { args: [ getSelfArg(), { name: 'spender', type: 'Address' }, { name: 'from', type: 'Address' }, { name: 'to', type: 'Address' }, { name: 'token_id', type: 'u32' }, ], code: ['Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);'], }, approve: { args: [ getSelfArg(), { name: 'approver', type: 'Address' }, { name: 'approved', type: 'Address' }, { name: 'token_id', type: 'u32' }, { name: 'live_until_ledger', type: 'u32' }, ], code: ['Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger)'], }, approve_for_all: { args: [ getSelfArg(), { name: 'owner', type: 'Address' }, { name: 'operator', type: 'Address' }, { name: 'live_until_ledger', type: 'u32' }, ], code: ['Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger)'], }, get_approved: { args: [getSelfArg(), { name: 'token_id', type: 'u32' }], returns: 'Option<Address>', code: ['Self::ContractType::get_approved(e, token_id)'], }, is_approved_for_all: { args: [getSelfArg(), { name: 'owner', type: 'Address' }, { name: 'operator', type: 'Address' }], returns: 'bool', code: ['Self::ContractType::is_approved_for_all(e, &owner, &operator)'], }, name: { args: [getSelfArg()], returns: 'String', code: ['Self::ContractType::name(e)'], }, symbol: { args: [getSelfArg()], returns: 'String', code: ['Self::ContractType::symbol(e)'], }, token_uri: { args: [getSelfArg(), { name: 'token_id', type: 'u32' }], returns: 'String', code: ['Self::ContractType::token_uri(e, token_id)'], }, // Mint mint: { args: [getSelfArg(), { name: 'to', type: 'Address' }, { name: 'token_id', type: 'u32' }], code: ['Base::mint(e, &to, token_id);'], }, mint_with_caller: { name: 'mint', args: [ getSelfArg(), { name: 'to', type: 'Address' }, { name: 'token_id', type: 'u32' }, { name: 'caller', type: 'Address' }, ], code: ['Base::mint(e, &to, token_id);'], }, sequential_mint: { name: 'mint', args: [getSelfArg(), { name: 'to', type: 'Address' }], code: ['Base::sequential_mint(e, &to);'], }, sequential_mint_with_caller: { name: 'mint', args: [getSelfArg(), { name: 'to', type: 'Address' }, { name: 'caller', type: 'Address' }], code: ['Base::sequential_mint(e, &to);'], }, }); const burnableFunctions = defineFunctions({ burn: { args: [getSelfArg(), { name: 'from', type: 'Address' }, { name: 'token_id', type: 'u32' }], code: ['Self::ContractType::burn(e, &from, token_id)'], }, burn_from: { args: [ getSelfArg(), { name: 'spender', type: 'Address' }, { name: 'from', type: 'Address' }, { name: 'token_id', type: 'u32' }, ], code: ['Self::ContractType::burn_from(e, &spender, &from, token_id)'], }, }); const enumerableFunctions = defineFunctions({ total_supply: { args: [getSelfArg()], returns: 'u32', code: ['non_fungible::enumerable::Enumerable::total_supply(e)'], }, non_sequential_mint: { name: 'mint', args: [getSelfArg(), { name: 'to', type: 'Address' }, { name: 'token_id', type: 'u32' }], code: ['Enumerable::non_sequential_mint(e, &to, token_id);'], }, non_sequential_mint_with_caller: { name: 'mint', args: [ getSelfArg(), { name: 'to', type: 'Address' }, { name: 'token_id', type: 'u32' }, { name: 'caller', type: 'Address' }, ], code: ['Enumerable::non_sequential_mint(e, &to, token_id);'], }, sequential_mint: { name: 'mint', args: [getSelfArg(), { name: 'to', type: 'Address' }], code: ['Enumerable::sequential_mint(e, &to);'], }, sequential_mint_with_caller: { name: 'mint', args: [getSelfArg(), { name: 'to', type: 'Address' }, { name: 'caller', type: 'Address' }], code: ['Enumerable::sequential_mint(e, &to);'], }, }); const consecutiveFunctions = defineFunctions({ batch_mint: { name: 'batch_mint', args: [getSelfArg(), { name: 'to', type: 'Address' }, { name: 'amount', type: 'u32' }], returns: 'u32', code: ['Consecutive::batch_mint(e, &to, amount);'], }, batch_mint_with_caller: { name: 'batch_mint', args: [ getSelfArg(), { name: 'to', type: 'Address' }, { name: 'amount', type: 'u32' }, { name: 'caller', type: 'Address' }, ], returns: 'u32', code: ['Consecutive::batch_mint(e, &to, amount);'], }, });

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