Create3 deployement with proxy contract- ownership?

I have deployed an upgradable proxy contract with create3factory from solmate.
In my contract i transfer ownership to an address. But still the owner ship is not on that address. Its a differant address. Here is my contract how should i do?

:1234: Code to reproduce

Here is box contract:



    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.18;
    
    import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
    import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
    import "hardhat/console.sol";
    
    contract Box is Initializable, OwnableUpgradeable {
        uint256 public value;
    
        function initialize(uint256 _value, address _owner) public initializer {
            __Ownable_init();
            transferOwnership(_owner);
            value = _value;
            console.log(_owner);
        }
    
        /// @custom:oz-upgrades-unsafe-allow constructor
        constructor() {
            _disableInitializers();
        }
    
        // Emitted when the stored value changes
        event ValueChanged(uint256 newValue);
    
        // Stores a new value in the contract
        function store(uint256 newValue) public onlyOwner {
            value = newValue;
            emit ValueChanged(newValue);
        }
    
        // Reads the last stored value
        function retrieve() public view returns (uint256) {
            return value;
        }
    }

Here is create3 contract:

   

        // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.18;
    
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "solmate/src/utils/CREATE3.sol";
    
    contract Create3Factory is Ownable {
        event Deployed(address contractAddress);
    
        function deploy(
            bytes32 _salt,
            bytes memory _creationCode
        ) external payable onlyOwner returns (address deployed) {
            bytes32 salt = keccak256(abi.encodePacked(msg.sender, _salt));
            deployed = CREATE3.deploy(salt, _creationCode, msg.value);
            emit Deployed(deployed);
        }
    
        function getDeployedAddress(
            address _deployer,
            bytes32 _salt
        ) external view returns (address) {
            bytes32 salt = keccak256(abi.encodePacked(_deployer, _salt));
            return CREATE3.getDeployed(salt);
        }
    }
    
    

Deployment scripts:

    module.exports = async ({ getNamedAccounts, deployments }) => {
  const { deploy, log } = deployments;
  const { deployer } = await getNamedAccounts();
  const args = [];

  const Box = await hre.ethers.getContractFactory("Box");
  const Create3Factory = await hre.deployments.get("Create3Factory");
  const create3Factory = await hre.ethers.getContractAt(
    "Create3Factory",
    Create3Factory.address
  );

  const ProxyAdmin = await hre.deployments.get("ProxyAdmin");
  const proxyAdmin = await hre.ethers.getContractAt(
    "ProxyAdmin",
    ProxyAdmin.address
  );

  const box = await Box.deploy();
  await box.deployed();
  console.log("box address: ", box.address);

  const functionData = box.interface.encodeFunctionData("initialize", [
    42,
    deployer,
  ]);

  const TransparentProxyCreationCode = await getCreationCode({
    contractName: "TransparentProxy",
    constructorArgs: {
      types: ["address", "address", "bytes"],
      values: [box.address, proxyAdmin.address, functionData],
    },
  });

  const tx2 = await create3Factory.deploy(
    "0x43f379c0214535bb128fca03870dd998b303c983a3d40d49c0ab85fc3f8c0f1f",
    TransparentProxyCreationCode
  );
  const receipt = await tx2.wait();
  const parsedEvents = receipt.events?.map((event) => {
    return {
      name: event.event,
      args: event.args,
    };
  });

  if (!parsedEvents) {
    throw new Error("No events found");
  }

  const { contractAddress } = parsedEvents.find(
    (event) => event.name === "Deployed"
  )?.args;

  console.log("proxyAddress: ", contractAddress);
};
const getCreationCode = async ({ contractName, constructorArgs }) => {
  const bytecode = (await ethers.getContractFactory(contractName)).bytecode;

  return `${bytecode}${ethers.utils.defaultAbiCoder
    .encode(constructorArgs.types, constructorArgs.values)
    .slice(2)}`;
};

module.exports.tags = ["all", "proxy"];

Here is i am try to check owner of the contract:

const { ethers } = require("hardhat");

async function main() {
  const [deployer, user, user1] = await ethers.getSigners();

  console.log(user.address);

  const Box = await ethers.getContractFactory("Box");
  const box = Box.attach("PUT ADDRESS OF THE ADDRESS OF CONTRACT ADDRESS FROM DEPLOYMENT SCRIPT ");

  console.log(await box.owner());


}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

Here is result of console.log this script 0xE20420d16367dCbB69Be37f57F76cCe594b6C35C
this is not the deployer address. The deployer address is 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 which is first address of hardhat node.
What am i doing wrong?

Edit:
Here is i deploy to mumbai network.First i deploy create3factory: https://mumbai.polygonscan.com/address/0x844b8e887b1e2561da375b48a73ec916972592cf
Second i deploy proxyadmin:
https://mumbai.polygonscan.com/address/0x7ff287395cccf64244226b1a165b96aedb7bee91
Third i deploy boxV1 contract and call proxy:
BoxV1 : https://mumbai.polygonscan.com/address/0xead45dfc2a64855e35421bf5e52ace65e064eae4
create3Factory deploys this contract: https://mumbai.polygonscan.com/address/0x52079aa2e26232fcec8ad69218ccafc2ff4890ec
What is this contract? its not proxy contract? When i get the owner of the proxy contract ( which is this one 0x173415F1501A6Ccc4a57CCe2BD3bcE268417ff0a coming from create3factory contract event)
i get this contract address. How can i able to call proxy contract functions with onlyowner modifer? ıf this contract is the owner of the proxy i shouldnt able to call it. ? What am i missing?

:computer: Environment

Hardhat

Check the value of deployer that you got from:

e.g. add in the deployment script:

console.log(`deployer: ${deployer}`)

and tell me the output from it.

deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266

@SKYBITDev3 I made it much more simplier. I comment out create3 factory and just deploy transparent proxy contract. I overwrite the _checkOwner function to see what is going on.
After i deploy, i called the transferOwnership function its passed even i have the transferownership event, when i call owner() function i can see my newOwner is the owner. But when i try to call function with onlyOwner modifier function i am getting same error "Ownable: caller is not the owner".
On the console i can see the owner still the old one. What is going on?

// contracts/Box.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "hardhat/console.sol";

contract Box is Initializable, OwnableUpgradeable {
    uint256 public value;

    function _checkOwner() internal view override(OwnableUpgradeable) {
        console.log("msgSender: ", _msgSender());
        console.log("owner: ", owner());
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }


    function initialize(uint256 _value, address _owner) public initializer {
        __Ownable_init();
        _transferOwnership(msg.sender);
        value = _value;
        console.log(_owner);
    }

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

    // Emitted when the stored value changes
    event ValueChanged(uint256 newValue);

    // Stores a new value in the contract
    function store(uint256 newValue) public onlyOwner {
        value = newValue;
        emit ValueChanged(newValue);
    }

    // Reads the last stored value
    function retrieve() public view returns (uint256) {
        return value;
    }
}

Did you call initialize?

msg.sender should be _owner

same result. It still say when i try call store owner is old one.

No i didnt call initialize isnt initialize like constructor when i deploy the contract it runs the initialize function then disable it.

OpenZeppelin's script for deploying upgradeable contracts calls initialize. But if you deployed it like a normal contract initialize wouldn't be called. It's just a normal function, so it needs to be called somehow for the code within it to execute.

1 Like

When i deploy the transparent proxy i give the initialize parameters so it should be called. When i try to call after the contract deployment its give me "Initializable: contract is already initialized". error so its already initialized.

But your polygonscan links show that you used 0xeac9edfe37fa378e8795253d292e6393d29abca2 to deploy.

dont get confused. This is from hardhat local node. On mumbai i can indeed see 0xeac9edfe37fa378e8795253d292e6393d29abca2 address as deployer

How about instead try this script to deploy an upgradeable contract via CREATE3?: https://github.com/SKYBITDev3/SKYBIT-Keyless-Deployment/blob/main/scripts/deployViaCREATE3-TESTERC20UG.js

Change the address of the factory in the script to your CREATE3 factory and enter the contract name and initialization arguments. factoryToUse can be ZeframLou or SKYBIT.

You'd also need to change in the CREATE3-deploy-functions.js script getDeployed to getDeployedAddress because in your factory contract you didn't use the same function name as in the solmate CREATE3 library contract.

I dont think this would work. Because i couldnt work with directly transparent proxy contract. As i mentioned Create3 deployement with proxy contract- ownership? - #4 by Furkan_Sezal , i disabled the create3 factory and directly deploy the contract on remix i have same issue as well. Even i got correct ownership ( it looks like ) when i try to call store function , it still taking old owner

It has worked fine for my upgradeable contracts, assigning admin role to the address that I specify in initializer argument. You should try it with your contract and see how it goes.

Looks like something wrong with this contract. I didnt post it before because i think its just a implementation of the open zeppelin contract. But that Ownable causing my problems but why?
Even if i transferOwnership of this contract it still says new owner is not the owner.

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

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import "hardhat/console.sol";

contract TransparentProxy is TransparentUpgradeableProxy, Ownable {
    constructor(
        address _logic,
        address _admin,
        bytes memory _data
    ) payable TransparentUpgradeableProxy(_logic, _admin, _data) {

        transferOwnership(msg.sender);
    }
}

When using a CREATE2 or CREATE3 factory msg.sender won't be what you expect. I explained it at https://github.com/SKYBITDev3/SKYBIT-Keyless-Deployment/tree/main#msgsender

1 Like