Dear OpenZeppelin Support Team,
I am writing to request assistance regarding an issue I am experiencing while developing an upgradeable ERC721 token intended to be soulbound (i.e., non-transferrable except for burning). My project is built using Hardhat, and I am using @openzeppelin/contracts-upgradeable@5.2.0 along with @openzeppelin/hardhat-upgrades. I have ensured that my contract imports only from the upgradeable package and have removed direct dependencies on @openzeppelin/contracts.
Despite these precautions, when I attempt to override the safeTransferFrom (and transferFrom) functions in my upgradeable contract to enforce soulbound behavior, I receive the following compilation error:
"Trying to override non-virtual function. Did you forget to add 'virtual'?"
After investigating, I discovered that in my installation of @openzeppelin/contracts-upgradeable version 5.2.0, the safeTransferFrom function in ERC721Upgradeable.sol is defined without the virtual keyword. It appears that the upgradeable package is internally referencing code from @openzeppelin/contracts, which lacks virtual declarations on safeTransferFrom (and related functions), thereby preventing me from overriding them in my contract.
My upgradeable contract is fully connected to and dependent upon the OpenZeppelin libraries, and I have verified that my package.json includes only the upgradeable package (with the non-upgradeable @openzeppelin/contracts removed). I have also reinstalled my node modules and cleared the lockfile to ensure a fresh environment. Despite these efforts, the compiler continues to reference a version of safeTransferFrom that is not overrideable.
Could you please advise:
- Is this behavior expected in @openzeppelin/contracts-upgradeable@5.2.0 due to internal dependencies on non-upgradeable code?
- If so, what is the recommended approach for enforcing soulbound behavior in an upgradeable ERC721 token? I ideally would like to override safeTransferFrom (or another appropriate hook such as _transfer or _beforeTokenTransfer) to block transfers while still allowing minting and burning.
- Are there any workarounds or future plans to mark these functions as virtual so that they can be overridden?
Any guidance or recommendations you can provide would be greatly appreciated, as a soulbound token is a non-negotiable requirement for our application.
Thank you very much for your time and assistance.
Best regards,
Outlaw