Is UUPS upgradeProxy() secure?

I know that TransparentUpgradeProxy requires you to be admin to upgrade it, but for UUPS I didn't see anything. I tried to test this in my Truffle test suite by deploying with a different address but I am getting these results:
upgradeProxy(existingAddress, ContractV2, { deployer: address2 })
I get the error: Cannot use 'in' operator to search for 'sendAsync' in undefined
upgradeProxy(existingAddress, ContractV2, { from: address2 })
success <- this should fail
upgradeProxy(existingAddress, ContractV2, { address2 })
success <- this should fail

So I guess I am using the options incorrectly. What's the right way to do this?

When I look at unknown-1337.json I see an admin address in there but it does not match any of the accounts on ganache or my deployment account.

Can anyone show me how I can be assured that no one else can upgrade over my implementation?

tia

deployer should be the Truffle migration deployer (see https://docs.openzeppelin.com/upgrades-plugins/1.x/api-truffle-upgrades#common-options). It is not an address argument.

Restrictions for upgrading a UUPS proxy must be handled in your implementation contract by overriding the _authorizeUpgrade function. See https://docs.openzeppelin.com/contracts/4.x/api/proxy#UUPSUpgradeable

Thank you so apparently extending UUPSUpgradeable adds 4kb to my contract. That is 16% of the max contract size. Shoudl I just go back to TransparentProxy? or is there anything else I can do?

The UUPS pattern puts the upgrade logic in the implementation contract, whereas the Transparent proxy pattern puts the upgrade logic in the proxy contract. For more details, see https://docs.openzeppelin.com/contracts/4.x/api/proxy#transparent-vs-uups

This means the actual proxies for UUPS are cheaper to deploy, although the implementation contract is larger.

Did you compile with optimizations enabled? It only seems to add 2.3kb to contract size.

I had my optimizer enabled and runs set at 200. Should I lower that? Will I get the same gas savings?

Edit: It was the same with runs set to 1. It was 4.13 KiB larger for me using truffle run contract-size.

Can you share what contract you're testing with?

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract Blank is UUPSUpgradeable, OwnableUpgradeable {
    function _authorizeUpgrade(address) internal override onlyOwner {}
}

extra 4.33 KiB for me with truffle run contract-size when compared with

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract Blank is OwnableUpgradeable {
    function _transferOwnership(address newOwner) internal override {}
}

Hi @frangio do you know how I can reduce it to 2.3kb as you did?

It's definitely 2.3kb in your test code as well. Is there a chance you're counting hex characters? You need to divide by 2.