Can't set the value of a variable when passing encoded data as parameter with upgradeable contracts

I posted many days ago and I thought to have understood the answers but apparently here I am asking the same questions again!

I have these contracts:

import "@openzeppelin/contracts/access/Ownable.sol";

contract Box is Ownable {
    uint256 public _x;
    bool private _initialized;
    string private _version;

    function initialize(uint256 x, string memory version) public {
        require(!_initialized, "Contract instance has already been initialized");
        _initialized = true;
        _x = x;
        _version = version;
    }

    function getValueOfX() public view returns(uint256) {
        return _x;
    }

    function versionOfContract() public view returns(string memory) {
        return _version;
    }

    function isInitialised() public view returns(bool) {
        return _initialized;
    }

    function setValueOfX(uint256 x) public {
        _x = x;
    }

    function getTen() public pure returns(uint256) {
        return 10;
    }
}

This is my proxy:

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


import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";

contract MyProxy is TransparentUpgradeableProxy {
    constructor(address _logic, address _admin, bytes memory _data) TransparentUpgradeableProxy(_logic, _admin, _data) {}
}

And this is my admin:

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

import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
contract Admin is ProxyAdmin {
    constructor() ProxyAdmin() {}
}

I'm using hardhat to deploy the contracts on my local newtwork using this script:

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

async function main() {
    // We get the contract to deploy
    const Box = await ethers.getContractFactory("Box");
    const Admin = await ethers.getContractFactory("Admin");
    const Proxy = await ethers.getContractFactory("MyProxy");

    [owner, addr1, addr2, ...addrs] = await ethers.getSigners();

    const box = await Box.deploy();
    const admin = await Admin.deploy();
    const iface = new ethers.utils.Interface(["function initialize(uint256 x, string memory version)"]);
    const encodedFunctionData = iface.encodeFunctionData(
        "initialize",
        [
            "1",
            "1"
        ]
    )
    
    console.log(encodedFunctionData);
    const proxy = await Proxy.deploy(box.address, owner.address, encodedFunctionData);

    console.log(`Address of proxy: ${proxy.address}, \nAddress of implementation: ${box.address}`);
  }
  
  main()
    .then(() => process.exit(0))
    .catch((error) => {
      console.error(error);
      process.exit(1);
    });

So when I deploy my proxy I pass as the third parameter the encoded data that would initialize my implementation contract, right? No, wrong. To test if everything went smooth I'm using hardhat console like so:

 > npx hardhat console --network localhost
 > const d = await ethers.getContractFactory("Box")
 > const dd = d.attach("0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0")
 > await dd.getValueOfX()
 BigNumber { value: "0" }

So apparently my initialize function is not being called when I deploy the smart contract! It's been days now I'm trying to get it work but I can't. Please help me.

contract-development
contract-deployment
upgrading

You don't have to deploy proxy/admin contracts manually, using the hardhat upgrades library you just deploy your contract with upgrades.deployProxy() ....
Its all well explained here.
Hope you work it out :+1:

Yes I know but I want to understand how to do that without the libraries

In that case check the binaries:

Is this the address of the proxy or the implementation? It needs to be the proxy. If it's the impementation, you will get 0.

It is the proxy, I'm sure

Ok, this may be because you're setting the proxy admin to be "owner", which is the same account that you're using to query getValueOfX. For security reasons, the proxy admin does not have access to the underlying implementation functions.

In this line:

const proxy = await Proxy.deploy(box.address, owner.address, encodedFunctionData);

Try changing owner.address to admin.address.

Hi, can you make an example of encodedFunctionData in js/ts code?
I want to represent case with proxy, in that contract pass _data as:

// in proxy.sol contract
bytes memory data

this data in target contract:

    function initialize(
        IExecutionDelegate _executionDelegate,
        IPolicyManager _policyManager,
        address _oracle,
        uint _blockRange
    ) external initializer {}

For _executionDelegate and _policyManager - I think it's just addresses of deployed contracts, but what is format for passing this in js/ts?

I try this:

            const PrxContract = await ethers
                .getContractFactory('ERC1967Proxy', signers.proxyDeployer)
            const prxContract = await PrxContract.deploy(
                blurExchange.target,
                ethers.toUtf8Bytes('4ce6bf0b0000000000000000000000009fe46736679d2d9a65f0992f2272de9f3c7fa6e0000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f05120000000000000000000000009965507d1a55bcc2695c58ba16fb37d819b0a4dc000000000000000000000000000000000000000000000000000000000000001e')
            );

but it's return error:

Error: VM Exception while processing transaction: reverted with reason string 'Address: low-level delegate call failed'

Please, somebody, help, I'm stuck in this stuff

@adminoid The addresses should be strings.
See the getInitializerData function in this testcase https://github.com/OpenZeppelin/openzeppelin-upgrades/blob/a41b4317c0ca368cfc6b98b8fe6c33f480bbb766/packages/plugin-hardhat/test/import-with-deploy.js#L38 for an example of how to encode the initializer function call. The args would be an array of your initializer arguments.

1 Like

Thank you very much! It is exactly what I need!

1 Like