To create an ERC-721 contract, implement its distinct-token ownership and transfer behavior, define how token IDs are issued, and decide how metadata is referenced. Then test the expected operations and failure cases, review the contract’s permissions and dependencies, and obtain qualified technical review before deployment. A tutorial or passing test suite is not a security audit or a guarantee of correctness.
What an ERC-721 contract needs to define
ERC-721 is a standard for contracts that track individually distinguishable tokens. Its interfaces cover ownership lookup, approvals, operator authorization, transfers, and transfer events. A token’s identity is the combination of its smart-contract address and token ID on an Ethereum chain, not the ID alone. That distinction matters in code, tests, and user interfaces: two contracts can use the same numeric ID without referring to the same token. source source
The standard defines token behavior, but it does not decide the project’s issuance policy or all its metadata conventions. A developer must specify who may mint, which identifiers are valid, and whether the contract exposes metadata. The optional ERC-721 metadata interface can provide a URI for a specific token ID. The URI may refer to onchain data, content-addressed material, or mutable offchain hosting; the interface alone does not guarantee that the referenced content remains available, permanent, accurate, or unchanged. source
Treat the walkthrough below as an implementation and review method, not copy-ready audited code. No particular compiler release, library, or deployment service is prescribed. Before coding, choose the exact standard and implementation approach, then check that its documentation matches the source, compiler, dependencies, and configuration used by the project.
Create erc721: structure the contract and token issuance
Separate the contract’s responsibilities in the design. ERC-721 behavior handles token ownership and transfers. Project-specific logic determines issuance and any metadata rules. The source should make these choices easy to identify: a reviewer should be able to locate the minting authority, understand how IDs are assigned, and see whether metadata can change.
Before implementing a minting path, write down answers to these questions:
- Which account or accounts may create tokens?
- How is a new token ID selected, and how does the contract handle an ID that already exists?
- Is issuance unlimited, or does the project define a cap?
- Can minting authority be transferred or revoked, and who is allowed to make that change?
These are design choices, not universal ERC-721 requirements. The intended recipient and identifier should be explicit inputs or otherwise unambiguous. Tests should verify that issuance records ownership as intended and that a repeated ID does not silently replace an existing token.
Metadata is another deliberate choice. If the contract provides token URIs, decide whether a URI is set at minting, whether it can later change, and which authority may update it. Also document what the URI points to and which offchain systems, if any, are needed to retrieve the referenced content. A URI is a pointer rather than the content itself, so the contract’s behavior and the content’s availability are separate concerns. source
For every privileged action, identify the authorized caller and the state it can change. If administrators can mint or update URIs, specify how that authority is assigned and maintained, then test that an unauthorized account cannot use those functions. The selected control pattern is part of the implementation and should be reviewed against the project’s needs; merely naming an administrator does not explain the consequences of that authority.
Implement ownership, transfers, and approvals
A transfer changes the recorded owner of a token. ERC-721 standardizes owner lookup, approvals, operator authorization, transfer behavior, and transfer events. Direct transfers and transfers made by an approved caller are distinct routes, so implementers should describe the caller conditions for each route and test both. source
Approvals authorize an account or operator to act within the contract’s defined token-authorization rules. Make the intended scope clear in the design and verify the implementation rather than assuming that a successful approval call proves later transfers are properly restricted. Useful checks include an owner granting an approval, changing it, and an authorized caller performing a transfer; tests should also show that an unapproved account is rejected.
Inspect authorization state after token movement. A test should check the new owner and the relevant approval state, not only whether the transaction completed. Include cases where the caller is not the owner, lacks the required approval, or names a nonexistent token. For every rejected action, confirm that the contract did not leave ownership or related state in an unintended condition.
Transfers to contract accounts need their own compatibility decision. Determine which transfer path the application will call and consult the selected standard text and implementation documentation for the exact recipient-handling behavior. The evidence used here establishes ERC-721’s ownership, approval, and transfer interfaces, but does not specify every detail of recipient callbacks or failure behavior. Do not infer those details from a simplified example; make them part of the implementation’s documented assumptions and tests.
Test the ERC-721 contract before deployment
Translate each requirement into a test with a known starting state, an action, and an expected result. A practical test plan can include:
- Issuance: an authorized caller creates a token for the expected recipient; an unauthorized caller is rejected; and reusing an identifier is handled as specified.
- Ownership lookup: an existing token reports its owner, and a nonexistent identifier produces the behavior required by the chosen implementation.
- Direct transfer: the owner transfers a token, after which ownership lookup reflects the intended recipient.
- Approved transfer: an approved account or operator can act only within the intended scope; an unapproved caller cannot.
- Authorization changes: approval grants and updates behave as designed, and tests inspect relevant approval state after a transfer.
- Failure paths: attempts involving a wrong owner, missing approval, or nonexistent token are rejected without unintended state changes.
- Metadata: the returned URI and any permitted update behavior match the written policy.
- Interfaces: the deployed implementation supports the required ERC-721 behavior and any optional extension the project says it provides.
The particular testing framework and syntax depend on the project’s toolchain, so use the documentation for the versions actually selected. Keep test results precise: they demonstrate what the test cases exercised, not every possible input, execution path, or integration environment. If a test assumes a particular recipient behavior or metadata source, record that assumption and test it rather than treating it as a universal standard rule.
Functional testing and security verification are related but not interchangeable. The OWASP Smart Contract Security Verification Standard offers a structured framework for designing, building, and testing EVM-based smart contracts and decentralized applications. Its guidance still needs to be supplemented with project-specific threat modeling and review. A checklist or test suite does not itself establish that a contract is secure. source
Review deployment and security assumptions
Before deployment, review the exact source and build configuration together. Check the compiler settings, dependencies, constructor inputs, and permissions, and ensure the code being prepared for deployment corresponds to the implementation that was tested. If the design includes administrative, pause, or upgrade functions, document who controls them and what behavior they can change. Their presence is not evidence that the contract is safe; they add behavior and authority that reviewers need to understand.
Review metadata separately from ownership and transfer logic. Confirm which URI each token exposes, whether the contract permits updates, who can make them, and what external hosting or storage assumptions apply. The ERC-721 metadata interface does not guarantee the availability, permanence, accuracy, or licensing of referenced content. source
A pre-deployment handoff should state what the tests cover, what assumptions remain, and who has reviewed the code. A qualified technical reviewer should examine the implementation, dependencies, permissions, and operating assumptions for the intended use. Do not label a tutorial, a checklist, or a test run as an independent audit. The goal is a documented implementation whose issuance policy and essential ownership and authorization behavior can be verified, with unresolved dependencies made visible before deployment.
Sources and references
- ERC-721: Non-Fungible Token Standard — Ethereum Improvement Proposals; retrieved 2026-10-18.
- ERC-721: Non-Fungible Token Standard — Ethereum Improvement Proposals; retrieved 2026-10-18.
- ERC-721: Non-Fungible Token Standard — Ethereum Improvement Proposals; retrieved 2026-10-18.
- OWASP Smart Contract Security Verification Standard — OWASP Foundation; retrieved 2026-10-19.