How to deploy an upgradeable contract with a linked library in Truffle?

:computer: Environment

:memo:Details

:1234: Code to reproduce

1 Like

Hi @xinya12,

Welcome to the community :wave:

I can do this using the CLI directly, I will need to come back to you on the commands to do this in Truffle.

The following is a simple example:

A.sol

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

library A {
    function doStuff() public returns (uint256) {
        return 42;
    }
}

B.sol

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

import "./A.sol";

contract B {

    event Stuff(uint256 value);

    function doStuff() public {
        uint256 value = A.doStuff();
        emit Stuff(value);
    }
}

Deploy using the CLI

$ npx oz deploy
✓ Compiled contracts with solc 0.6.11 (commit.5ef660b1)
Compilation warnings:
contracts/A.sol:5:5: Warning: Function state mutability can be restricted to pure
    function doStuff() public returns (uint256) {
    ^ (Relevant source part starts here and spans across multiple lines).

? Choose the kind of deployment upgradeable
? Pick a network development
? Pick a contract to deploy B
✓ Added contract B
✓ A library uploaded
✓ Contract B deployed
All implementations have been deployed
? Call a function to initialize the instance after creating it? No
✓ Setting everything up to create contract instances
✓ Instance created at 0x26b4AFb60d6C903165150C6F0AA14F8016bE4aec
To upgrade this instance run 'oz upgrade'
0x26b4AFb60d6C903165150C6F0AA14F8016bE4aec

doStuff

$ npx oz send-tx
? Pick a network development
? Pick an instance B at 0x26b4AFb60d6C903165150C6F0AA14F8016bE4aec
? Select which function doStuff()
✓ Transaction successful. Transaction hash: 0x27170076feb0f6887cc0624524950284bcafcdcbdbc0ae4b01f37b9c4d27a59b
Events emitted:
 - Stuff(42)

The following Truffle deploy fails as it doesn’t link the library.

2_deploy.js

// Load zos scripts and truffle wrapper function
const { scripts, ConfigManager } = require('@openzeppelin/cli');
const { add, push, create, link } = scripts;

async function deploy(options) {
  // Register B in the project
  add({ contractsData: [{ name: 'B' }] });

  // Push implementation contracts to the network
  await push(options);

  // Create an instance of B
  await create(Object.assign({ contractAlias: 'B' }, options));
}

module.exports = function(deployer, networkName, accounts) {
  deployer.then(async () => {
    const { network, txParams } = await ConfigManager.initNetworkConfiguration({ network: networkName, from: accounts[1] })
    await deploy({ network, txParams })
  })
}
$ npx truffle migrate --network development

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



Starting migrations...
======================
> Network name:    'development'
> Network id:      1594874772369
> Block gas limit: 6721975 (0x6691b7)


2_deploy.js
===========

Error: B deployment failed with error: B bytecode contains unlinked libraries: A
    at Object.buildDeploymentCallData (/home/abcoathup/projects/forum/xinya12/node_modules/@openzeppelin/upgrades/src/utils/ABIs.ts:27:11)
    at Object.<anonymous> (/home/abcoathup/projects/forum/xinya12/node_modules/@openzeppelin/upgrades/src/utils/Transactions.ts:131:17)
    at Generator.next (<anonymous>)
    at fulfilled (/home/abcoathup/projects/forum/xinya12/node_modules/@openzeppelin/upgrades/lib/utils/Transactions.js:10:58)
    at process._tickCallback (internal/process/next_tick.js:68:7)
Truffle v5.1.34 (core: 5.1.34)
Node v10.21.0

@abcoathup. Thanks very much for replying. So you mean there’s no way to do “link” in the truffle javascript file, right? If so, should I make every deploy to the CLI ? That seems a little complicated because there’s a lot deply statements in my truffle migration file.

1 Like

Hi @xinya12,

I will need to come back to you on the commands to do the link in a Truffle migrations script.

I tried to do it in Truffle but I don’t know the syntax.

@abcoathup Thanks so much. Please forgive my English. I’m not a native English speaker, so I’m not very sure what do you mean by

" I will need to come back to you on the commands to do this in Truffle.".

Do you need me to paste my truffle code here? If so ,here is my code:

async function deploy(options, contractName, initialize) {
    add({ contractsData: [{ name: contractName, alias: contractName }] });

    await push(options);

    if (initialize) {
        await create(Object.assign({ contractAlias: contractName, methodName: 'initialize', methodArgs: [] }, options));
    } else {
        await create(Object.assign({ contractAlias: contractName }, options));
    }

}

module.exports = async function (deployer, networkName, accounts) {
    const { network, txParams } = await ConfigManager.initNetworkConfiguration({ network: networkName, from: accounts[0] })
    await deployer.then(async () => {
        await deploy({ network, txParams }, A.contractName, false)
    })
    await deployer.link(A, B);
    await deployer.then(async () => {
        await deploy({ network, txParams }, B.contractName, false)
    })
    console.log(poolPawn.address);
}

And I deployed by

truffle migrate --network development --reset

I just wanted to try to deploy the contract using oz’s format and link them using truffle’s native format. Apprently that’s wrong.

Hi @xinya12,

I need to find out the answer as I don’t know how to do it. When I find out how to do it, I will reply to this post.

1 Like

@abcoathup Got it ! Thanks so much!

1 Like

@xinya12 Unfortunately it’s not possible to deploy the library using Truffle and then link that into an upgradeable contract deployed using OpenZeppelin SDK.

If you deploy the proxy using OpenZeppelin CLI it will take care of deploying and linking the library for you, and I’m not entirely sure right now but it may store the deployed library’s address in the artifact, so that you’ll be able to reuse it from a Truffle migration afterwards.


We’re currently working on a redesign of the JavaScript library that will make it possible (and very easy!!) to do exactly what you’re asking about. Unfortunately it’s not released yet and I can’t confirm when we’ll be able to release it, but I’ll let you know as soon as possible.

2 Likes

@frangio @abcoathup Thanks so much for replying. Seems I’ll have to use the native openzeppelin CLI. Actually I wanted to use truffle just because I can put all the deploy and initial statements in the javascript file like 2_deploy.js and 3_init.js, so that I can just run

truffle migrate

to make things done. So another question is, if I have to deploy the contracts using oz CLI one by one, or is there a way to make all the deploys and initials done by just one CLI command?

Thanks!

1 Like

Hi @xinya12,

One option could be to as follows:

From: How to upgrade contracts programmatically?
You can use shelljs or child_process to call the CLI via your js code directly. You should set the OPENZEPPELIN_NON_INTERACTIVE env flag to prevent the CLI from making any interactive prompts while running on a script.

Also, when running non-interactively, you’ll have to manually call oz compile and oz push before every oz upgrade. The push command takes care of deploying your implementation contracts to the network. It is handled automatically for you when running the commands interactively, but not on a script.

1 Like

@abcoathup Got it ! I’ll take a look at the post.
Thanks for your time.

1 Like