Accessing proxy via .deployed() in Truffle

Trey asked on Telegram

Whenever I deploy a new contract (using Openzeppelin Upgrades Plugin), it deploys an AdminUpgradabilityProxy and my logic contract (MyToken).

If I then go to the truffle console and do

let myToken = await MyToken.deployed()
myToken.address

Then it gives me the address back for the Proxy, which works as expected.

As a quick check, I can test the totalSupply:

myToken.totalSupply()
answer: 1000000

However, if I do any kind of migration using the upgradeProxy, it doesn’t have the same behavior. It deploys out the new contract (MyTokenV2), but the address for the new token is for the logic contract instead of the proxy address, which isn’t usable directly (no storage variables, not the right contract, etc.). So the following does NOT work:

let myTokenV2 = await MyTokenV2.deployed()
myTokenV2.totalSupply()
answer: 0

Unfortunately, the MyToken contract (pointing to the Proxy address) does not have any of the new methods from the upgrades, and using the MyTokenV2 and MyTokenV3 variables does not point to the proxy so doesn’t work.

Is this what’s supposed to be happening? I would expect that since the MyTokenV2 and MyTokenV3 contracts are deployed using the upgradeProxy function, that they would also point to the Proxy address like MyToken does.

Here’s the upgrade migration, for reference:

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

const MyToken = artifacts.require('MyToken');
const MyTokenV2 = artifacts.require('MyTokenV2');

module.exports = async function (deployer) {
  const existing = await MyToken.deployed();
  const instance = await upgradeProxy(existing.address, MyTokenV2, { deployer, unsafeAllowCustomTypes: true  });
  console.log("Upgraded", instance.address);
};

(same code for later migration swapping out V1 to V2 and V2 to V3)

Having the MyTokenV2 and MyTokensV3 not point to the proxy address feels like a bug to me, but maybe I’m just not understanding something about how this is supposed to be working.

Any guidance here?

In Truffle, subsequent implementation contracts have the implementation address.

So we have to do the following:

$ npx truffle console --network rinkeby
truffle(rinkeby)> box = await Box.deployed()
truffle(rinkeby)> boxV2 = await BoxV2.at(box.address)
truffle(rinkeby)> (await boxV2.retrieve()).toString()
'42'
truffle(rinkeby)> await boxV2.increment()
{ tx:
...
truffle(rinkeby)> (await boxV2.retrieve()).toString()
'43'

Trey responded:

ok, must have missed that. It’s kind of weird that the first implementation is forever tied to the proxy address like that and updated ones with the current implementation aren’t.

Thanks @abcoathup! I’ll switch over to using the “.at” option now.


@frangio replied:

it used to work like you expected but i changed it (https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/109)… now that i think of it, your intuition makes sense and i think we should reintroduce that behavior so that you’d be able to use deployed