UUPS Upgradeable Contract - Unable to upgrade despite being the owner

I'm trying to upgrade a contract using the UUPSUpgradeable Proxy. I initially deployed the contract with an address (eg: 0x0A) and so I'm assuming that this first address is the ProxyAdmin. I transferred the ownership of the contract to a different address (eg: 0x0B) but when I try to upgrade it, I get the error: OwnableUnauthorizedAccount(0x0B).

One thing I'm not sure, do we need to implement a function that uses changeAdmin from ERC1967Utils? At least from my understanding we dont, but I could be wrong. I thought I might try this with AccessControl like in this instance (UUPS Upgradeable Contract - Transfer Ownership) but if I'm missing some other call then it wouldnt make a difference.

What seems to be causing the error in this case?

:1234: Code to reproduce

/// @custom:oz-upgrades-from Staking
contract Staking is Initializable, UUPSUpgradeable, OwnableUpgradeable, PausableUpgradeable {
    /*************************************************************/
    /*                        INITIALIZER                        */
    /*************************************************************/

    function initialize(address _token) public initializer {
        TestToken = ITestToken(_token);
        name = "Staking";
        __Pausable_init();
        __Ownable_init(msg.sender);
        __UUPSUpgradeable_init();
    }

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function _authorizeUpgrade(address) internal override onlyOwner {}

...
}

contract UpgradeStaking is Script {

    address StakingAddress;
    Staking stakingContract;

    function run() external {
        // uint256 deployerPK = vm.envUint("PRIVATE_KEY");
        string memory mnemonic = vm.envString("MNEMONIC");
        uint256 deployerPk = vm.deriveKey(mnemonic, 1);
        address deployer = vm.addr(deployerPk);

        vm.startBroadcast(deployerPk);
        console.log("Deployer Address:", deployer);

        stakingAddress = 0x004fC...91227D;
        stakingContract = Staking(stakingAddress);

        console.log(stakingContract.owner());

        Upgrades.upgradeProxy(stakingAddress, "Staking.sol", "");

        vm.stopBroadcast();
    }
}

:computer: Environment

Foundry
Openzeppelin v5

Since you are using UUPS, there is no ProxyAdmin, and there is no need to include a function with changeAdmin.

Ensure you are interacting with the proxy address, not the implementation address. Is your stakingAddress the address of the proxy? Does your script at console.log(stakingContract.owner()); print the correct owner?

When running forge script, ensure you include --sender <current owner address> according to https://docs.openzeppelin.com/upgrades-plugins/1.x/foundry-upgrades#deploying_and_verifying

1 Like

Thanks @ericglau. I tried including --sender 0xcurrentOwner but it also failed. I think this is because in the original contract deployment I mistakenly used Upgrades.deployTransparentProxy, but should have used Upgrades.deployUUPSProxy. Will follow up on this.

Not sure if this is the right place to post this, but I can start a new thread if needed. I deployed a UUPS proxy but I'm having some trouble with the contract verification:

contract DeployStaking is Script {

    address Token;
    address Staking;

    function run() external {
        uint256 deployerPK = vm.envUint("PRIVATE_KEY");
        address deployer = vm.addr(deployerPK);

        vm.startBroadcast(deployerPK);
        console.log("Deployer Address:", deployer);

        Token = 0x50F...ba0;

        Staking = Upgrades.deployUUPSProxy(
            "Staking.sol",
            abi.encodeCall(
                Staking.initialize,
                (Token)
            )
        );
        console.log("Staking deployed at:", Staking);

        vm.stopBroadcast();
    }
}

Logs:

ONCHAIN EXECUTION COMPLETE & SUCCESSFUL.
Total Paid: 0.00239978696818239 ETH (2398329 gas * avg 1.00060791 gwei)
##
Start verification for (2) contracts
Start verifying contract `0xacD...E9e` deployed on sepolia

Submitting verification for [lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol:AccessControlUpgradeable] 0xacD3...E9e.
Encountered an error verifying this contract:
Response: `NOTOK`
Details: `Invalid constructor arguments provided. Please verify that they are in ABI-encoded format`

Command

forge script script/StakingDeployScript.s.sol:DeployStaking --rpc-url $SEPOLIA_RPC_URL \
 --broadcast \
 --via-ir \
 --ffi \
 --etherscan-api-key $ETHERSCAN_API_KEY \
 --verify \
 -vvvv

The contract is the same contract as above and there are no constructor arguments, I'm not sure why its throwing that error - seems like it’s trying to verify AccessControlUpgradeable? Previously when I deployed with deployTransparentProxy I dont get any issue. Not sure why it’s bahaving this way this time round?

Regarding upgrading the UUPS upgradeable contract:

I was able to transfer the ownership and have the new owner upgrade the contract after redeploying the contract using Upgrades.deployUUPSProxy. I guess when it was deployed via Upgrades.deployTransparentProxy, there was a ProxyAdmin and transferring ownership does not grant the new owner the privilege to upgrade the contract without being the ProxyAdmin to begin with.

Still unsure about the verifying the contracts through --verify, thought its a little odd it succeeded in the very beginning but when I tried again it didnt work the second time.

I'm not sure why it seems to be verifying AccessControlUpgradeable at 0xacD...E9e, since AccessControlUpgradeable is an abstract contract and not deployed directly. What contract is deployed at 0xacD...E9e address on sepolia?

Perhaps try including the --force parameter when calling forge script to ensure old artifacts are cleaned up first.

That would be the implementation contract. Using --force did not work too, same behaviour with trying to verify AccessControlUpgradeable. I tried deploying a simple non-upgradeable test contract and got the same error, so I think it might be something with my environment or project setup.

I have tried starting another project from scratch as well, but I run into the same problem with trying to verify AccessControlUpgradeable, so I'm a little clueless as how to try to resolve this.
Any suggestions? :sweat_smile:

Ended up fixing it by removing all dependencies from the project, did a foundryup , forge install to reinstall all the dependencies I was using, and it fixed the issue.

I suspect I must have accidentally done something to one of the dependencies (like clicking on discard changes in source control in vscode) which ended up causing this unusual behaviour during the verification.

Glad my woes are over hah.

1 Like