UUPS, ProxyAdmin, Contract "owner" and Burn

Hey folks,

I am converting my contract to Upgradeable using UUPS. I generated the contract skeleton via OpenZeppelin Wizard and I am curious about a few things:

  1. ProxyAdmin is the deployer address. I thought it would only be allowed to call upgrade-related functions, but from my unit tests, proxyAdmin can call contract functions such as safeMint();

In my research, it is advised to have a ProxyAdmin who can upgrade contracts, but cannot call contract funcions, and create a contract owner (do not confuse with token owner) who is allowed to call contract functions (like safeMint) but cannot upgrade contract.

Can you please point me to documentation regarding this separation of roles and how to implement it?

  1. I have implemented ERC721BurnableUpgradeable and I would like only the contract owner to be able to burn tokens. Right now, the token owner is allowed to burn,

Contract burn code (no changes from OpenZeppelin Wizard):

    function _burn(uint256 tokenId)


      it("Token owner cannot burn", async function () {
        // deploy contract
        const { contract, annAccount } = await loadFixture(deployFixture);

        // mint token to annAccount
        await contract.safeMint(annAccount.address, tokenOne, ipfsFirstToken);

        // check Ann received the token        
        await expect(

        // Ann cannot burn her token
        await expect(
        ).to.revertedWith("Ownable: caller is not the owner");

        // Ann still has her token
        await expect(

Test result:

Proxy Admin: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Contract deployed to: 0x70e0bA845a1A0F2DA3359C97E0285013525FFC49

AssertionError: Expected transaction to be reverted with reason 'Ownable: caller is not the owner', but it didn't revert
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async Context.<anonymous> (test\Contract.js:260:9)

Oddly, proxyAdmin (deployer contract) cannot burn. If I switch the burn statement on test for this:
await contract.burn(tokenOne);

Then I get this error:
'ERC721: caller is not token owner nor approved'

How can I only allow contract owner (or proxyAdmin in my current scenario) to be the only one allowed to burn a token?

Thanks in advance!

How can I only allow contract owner (or proxyAdmin in my current scenario) to be the only one allowed to burn a token?

It sounds like role-based access control will give you what you're looking for here. You can add it to the contract using the Wizard (Access Control --> Roles) and easily assign/modify roles by using Defender:

  1. Import the contract into Admin
  2. Select the contract from the Admin dashboard, then select Admin Action --> Modify Access
  3. Choose the role (burner role) and the account you'd like to assign the role to.

At 6:20 in this video there's a walkthrough of this step.

1 Like