Function invoked using 'call' during upgrade process doesn't work

I am attempting to call a function during the upgrade process using call as per the docs, but it doesn't seem to work.

foo = await FooV1.deployed()
await foo.GetX()

I expect GetX to return 1 as this was supposed to be set during the upgrade process. However, it returns 0. Is this call functionality actually working, or am I doing something wrong?

:1234: Code to reproduce


contract FooV1 {
    uint256 public X;

    function GetX() public view returns(uint256) {
        return X;


contract FooV2  {
    uint256 public X;

    function GetX() public view returns(uint256) {
        return X;

    function SetX(uint256 x) public{
        X = x;


const FooV1 = artifacts.require("FooV1");
const FooV2 = artifacts.require("FooV1");

const { deployProxy upgradeProxy } = require('@openzeppelin/truffle-upgrades');

module.exports = async function (deployer) {
  const proxy = await deployProxy(FooV1, [], { deployer });
  await upgradeProxy(proxy.address, FooV2, { deployer, call: { fn: "SetX()", args: [1] } });

:computer: Environment

Truffle v5.4.15 (core: 5.4.15)
Solidity - ^0.8.0 (solc-js)
Node v14.18.0
Web3.js v1.5.3

Can you try "SetX" without parentheses? I'm not sure why this wouldn't result in an error though. May be related to Truffle Migrations.

I tried that also. No difference.

Sorry, the release was missing on npm. Please update the plugin, make sure it's 1.11.0, and try again.

It's working correctly for me locally, so yes it should work as you expect.

1 Like

Thanks, this worked, but it calls it with msg.sender as address(0) which means a function marked as onlyOwner, or containing calls that depend on msg.sender being the deployer, will fail.

Is this a problem with the functionality not passing on the from value, or is there a way I can specify this in the call to upgradeProxy?

There is no way for msg.sender to be address(0) in this context. But it could be some address other than the one you're expecting. If you're using transparent proxies it will be the address of the ProxyAdmin instance.

If your contract was Ownable previously, then using an onlyOwner function will probably not work because the owner is probably not the ProxyAdmin.

If you're trying to make your contract Ownable in this upgrade, you will have a hard time initializing the owner variable because Ownable's initializer will refuse to run. This is something we're improving for next version.