Test with deployProxy errors with Object.getLinkedBytecode

So I started writing high level tests using the upgrades plugin against the contracts (not the proxy factory) and got a very strange error that I cant seem to find any information on:

      at Object.getLinkedBytecode (node_modules/@openzeppelin/truffle-upgrades/src/validate.ts:150:46)
      at runMicrotasks (<anonymous>)
      at processTicksAndRejections (internal/process/task_queues.js:97:5)
      at deployProxy (node_modules/@openzeppelin/truffle-upgrades/src/deploy-proxy.ts:47:34)
      at Context.<anonymous> (test/ViaCash.js:544:29)

This is the test that is causing the error. Let me know if I should open a different thread to discuss this.

It may be possible. See this open issue: OpenZeppelin/openzeppelin-upgrades#175

Thanks

1 Like

Hi @bonedaddy,

In your other tests you have a step await Cash.link(abdkMathQuad); to link the library.
So assume we also need to do this when testing the proxy, though I am not sure the syntax to do this with Upgrades Plugins.

I was not able to run your tests locally. Can you please share the full error message that you saw? You shared the stack trace but not the error message itself.

You need to link a contract the normal Truffle way before running it through any of the upgrades plugin functions.

1 Like

I tried linking the normal truffle way within the test and still getting the failure. For what its worth the migrations also handle linking.

Here is the full error

  4) Contract: ViaCashUpgradesPluginIntegrationTest
       should not fail:
     TypeError: networkInfo.links[name].replace is not a function
      at Object.getLinkedBytecode (node_modules/@openzeppelin/truffle-upgrades/src/validate.ts:150:46)
      at processTicksAndRejections (internal/process/task_queues.js:97:5)
      at deployProxy (node_modules/@openzeppelin/truffle-upgrades/src/deploy-proxy.ts:47:34)
      at Context.<anonymous> (test/ViaCash.js:546:29)

To run the tests locally you need to have ganache-cli running, and ethereum-bridge running.

1 Like

Hi @bonedaddy,

I created an example to show linking with an upgradeable contract.
Can you try updating the way you are linking to this example?

Answer.sol

// contracts/Answer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;

library Answer {
    function getValue() public returns (uint256) {
        return 42;
    }
}

Box.sol

// contracts/Box.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
 
import "./Answer.sol";

contract Box {
    uint256 private value;
 
    // Emitted when the stored value changes
    event ValueChanged(uint256 newValue);
 
    // Stores a new value in the contract
    function store(uint256 newValue) public {
        value = newValue;
        emit ValueChanged(newValue);
    }
 
    // Reads the last stored value
    function retrieve() public view returns (uint256) {
        return value;
    }

    // Stores the answer in the contract
    function storeAnswer() public {
        value = Answer.getValue();
        emit ValueChanged(value);
    }
}

Migrations

// migrations/2_deploy.js
const Answer = artifacts.require("Answer");
const Box = artifacts.require("Box");

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

module.exports = async function (deployer) {
  await deployer.deploy(Answer);
  await deployer.link(Answer, Box);
  
  await deployProxy(Box, [7], { deployer, initializer: 'store', unsafeAllowLinkedLibraries: true });
};

Box.test.js

Implementation unit test

// test/Box.test.js
// Load dependencies
const { expect } = require('chai');
 
// Load compiled artifacts
const Answer = artifacts.require('Answer');
const Box = artifacts.require('Box');
 
// Start test block
contract('Box', function () {
  beforeEach(async function () {
    // Deploy a new Box contract for each test
    this.answer = await Answer.new();
    await Box.link('Answer', this.answer.address);
    this.box = await Box.new();
  });
 
  // Test case
  it('retrieve returns a value previously stored', async function () {
    // Store a value
    await this.box.store(42);
 
    // Test if the returned value is the same one
    // Note that we need to use strings to compare the 256 bit integers
    expect((await this.box.retrieve()).toString()).to.equal('42');
  });

  it('retrieve returns answer previously stored', async function () {
    // Store a value
    await this.box.storeAnswer();
 
    // Test if the returned value is the same one
    // Note that we need to use strings to compare the 256 bit integers
    expect((await this.box.retrieve()).toString()).to.equal('42');
  });
});

Box.proxy.test.js

// test/Box.proxy.test.js
// Load dependencies
const { expect } = require('chai');
const { deployProxy, silenceWarnings } = require('@openzeppelin/truffle-upgrades');
 
// Load compiled artifacts
const Answer = artifacts.require('Answer');
const Box = artifacts.require('Box');
 
// Start test block
contract('Box (proxy)', function () {

  before(async function () {
    await silenceWarnings();
  });


  beforeEach(async function () {
    // Deploy a new Box contract for each test
    this.answer = await Answer.new();
    await Box.link('Answer', this.answer.address);
    this.box = await deployProxy(Box, [7], {initializer: 'store', unsafeAllowLinkedLibraries: true});
  });
 
  // Test case
  it('retrieve returns a value previously initialized', async function () {
    // Test if the returned value is the same one
    // Note that we need to use strings to compare the 256 bit integers
    expect((await this.box.retrieve()).toString()).to.equal('7');
  });

  it('retrieve returns answer previously stored', async function () {
    // Store a value
    await this.box.storeAnswer();
 
    // Test if the returned value is the same one
    // Note that we need to use strings to compare the 256 bit integers
    expect((await this.box.retrieve()).toString()).to.equal('42');
  });
});

Tests

$ npx truffle test
Using network 'test'.


Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.


Warning: Potentially unsafe deployment of Box

    You are using the `unsafeAllowLinkedLibraries` flag to include external libraries.
    Make sure you have manually checked that the linked libraries are upgrade safe.



  Contract: Box (proxy)

Warning: All subsequent Upgrades warnings will be silenced.

    Make sure you have manually checked all uses of unsafe flags.

    ✓ retrieve returns a value previously initialized
    ✓ retrieve returns answer previously stored (70ms)

  Contract: Box
    ✓ retrieve returns a value previously stored (61ms)
    ✓ retrieve returns answer previously stored (52ms)


  4 passing (854ms)

@bonedaddy Truffle is not present in your package.json. What version of Truffle are you using?

@frangio I’m using truffle v5.1.51, adding it to package.json with npm install truffle@v5.1.51 doesn’t seem to solve anything.

@abcoathup I’ve changed the failing test to follow linking the contracts with how you showcased in the examples however the error remains, note here is what im doing:

contract("ViaCashUpgradesPluginIntegrationTest", async (accounts) => {
  it("should not fail", async () => {
    var oracle = await ViaOracle.deployed(); 
    var factory = await Factory.deployed();
    
    var strUtils = await stringutils.new();
    var abdkMath = await ABDKMathQuad.new();
    
    await Cash.link(stringutils, strUtils.address);
    await Cash.link(ABDKMathQuad, abdkMath.address);

    const cashUpgradeInst = await deployProxy(
      Cash,
      [
        web3.utils.utf8ToHex("Via_EUR"), // name
        web3.utils.utf8ToHex("Cash"), // type
        accounts[0], // owner
        oracle.address, // oracle
        factory.address // factory
      ],
      {
        "unsafeAllowCustomTypes": true, 
        "unsafeAllowLinkedLibraries": true
      }
    );
    await upgradeProxy(cashUpgradeInst.address, CashV2Test);
    await prepareUpgrade(cashUpgradeInst.address, CashV2Test);
  });
});
1 Like

Hi @bonedaddy,

We have found an error in Upgrades Plugins for Truffle and Hardhat. Users of the flag unsafeAllowCustomTypes should update their dependencies to the latest version.

See post for more details: Problem in Upgrades Plugins for users of unsafeAllowCustomTypes.

Thanks, ill try this and reply if i encounter any issues.

1 Like

@bonedaddy The latest update isn’t related to this problem you ran into.

I’ll have a good look tomorrow at the last failing test that you shared. I’ll get back to you with my findings.

2 Likes

Hi @bonedaddy,

I can see where you link to Cash but I don’t see where you link the libraries to CashV2Test.

If the implementation contract has a linked library then you need to link it for each version.

The code fails before there, it is the deployProxy call that is generating the error

    const cashUpgradeInst = await deployProxy(
      Cash,
      [
        web3.utils.utf8ToHex("Via_EUR"), // name
        web3.utils.utf8ToHex("Cash"), // type
        accounts[0], // owner
        oracle.address, // oracle
        factory.address // factory
      ],
      {
        "unsafeAllowCustomTypes": true, 
        "unsafeAllowLinkedLibraries": true
      }
    );

I was finally able to get to the root of the issue.

First of all I’d like to correct the syntax for linking suggested by @abcoathup above.

In order to link in your test you need to use one of the following options:

  • Cash.link(Library) if the library was deployed in a migration
  • Cash.link('LibraryName', address)
  • Cash.link({ 'LibraryName': address })

where Library = artifacts.require('LibraryName').

Nothing else will work.

Now, there are two mistakes in your tests.

  1. You’re running Cash.link(deployedLibrary). Note this doesn’t match any of the options I listed above. Truffle doesn’t recognize this as an error and it puts your contract in a buggy state. This results in the getLinkedBytecode error that started the thread.

  2. You’re trying to link ABDKMathQuad. This is a library with internal functions only, and it’s not necessary to deploy it or link it at all. There’s no harm in doing this other than spending gas unnecessarily for a contract that doesn’t need to be deployed. (I don’t understand why Truffle allows this.)

To summarize, in order to fix your issue you should remove all calls to Cash.link(abdkMathQuad).


As for stringutils, its functions are public rather than internal and from a quick test this seems to require linking. However, I would suggest making the functions internal as it’s likely to be more gas efficient.

2 Likes

This seems to have fixed the issue, however I’m getting another one but I think that might be due to a test issue, I will double check.

1 Like

A post was split to a new topic: Transaction hash is undefined