Hi guys !
I love the work you’re doing and really want to use the openzeppelin sdk in my project, but I ran into some issues when trying to integrate your tool with truffle.
My goal is to use the SDK without command-line, only from programmatic libraries and directly from my migrations. Migrations are very useful when using upgradeable smart contract ! Currently, I saw two options to do so:
- By using the
@openzeppelin/cli, like shown here - By using the
@openzeppelin/upgrades, like documented here
But in both cases, I ran into some issues.
First, the cli solution works properly when migrating to live networks, but not so well when working on the test or dry-run network. When running truffle test, truffle raises a ganache instance and creates the test network. This means that inside my migration, the network name I receive will always be test, even if I never configured such network inside my truffle-config. In this case, the deployment crashes because it could not find a network named test inside the truffle-config.
A possible workaround is to deploy the contracts in the test suite, and make a condition to ignore migrations if network name is test.
When using the upgrades library, it will always try to recover contract artifacts from the filesystem. But once again, when running truffle test, there is no build directory generated. All the artifacts provided by the truffle-artifactor have been injected by truffle, and are not retrieved from filesystem. But @openzeppelin/upgrades cannot work without filesystem artifacts.
// Required by @openzeppelin/upgrades when running from truffle
global.artifacts = artifacts;
global.web3 = web3;
// Import dependencies from OpenZeppelin SDK programmatic library
const { Contracts, SimpleProject, ZWeb3 } = require('@openzeppelin/upgrades');
const fs = require('fs-extra');
module.exports = async function(deployer) {
ZWeb3.initialize(web3.currentProvider);
const extra_build_dir = `${__dirname}/../zos_build/contracts`;
if (fs.existsSync(extra_build_dir)) {
fs.removeSync(extra_build_dir)
}
fs.ensureDirSync(extra_build_dir);
fs.writeFileSync(`${extra_build_dir}/PlaceHolder_v0.json`, JSON.stringify(artifacts.require('PlaceHolder_v0')));
fs.writeFileSync(`${extra_build_dir}/PlaceHolder_v1.json`, JSON.stringify(artifacts.require('PlaceHolder_v1')));
fs.writeFileSync(`${extra_build_dir}/Initializable.json`, JSON.stringify(artifacts.require('Initializable')));
Contracts.setLocalBuildDir(extra_build_dir);
const PlaceHolder_v0 = Contracts.getFromLocal('PlaceHolder_v0');
const PlaceHolder_v1 = Contracts.getFromLocal('PlaceHolder_v1');
fs.removeSync(extra_build_dir);
/* Retrieve a couple of addresses to interact with the contracts. */
const [creatorAddress, initializerAddress] = await ZWeb3.accounts();
/* Create a SimpleProject to interact with OpenZeppelin programmatically. */
const myProject = new SimpleProject('MyProject', null, { from: creatorAddress });
const instance = await myProject.createProxy(PlaceHolder_v0, { initArgs: [42] });
console.log('Contract\'s storage value:', (await instance.methods.value().call({ from: initializerAddress })).toString());
await myProject.upgradeProxy(instance.address, PlaceHolder_v1, { initMethod: 'add', initArgs: [1] });
console.log('Contract\'s storage new value:', (await instance.methods.value().call({ from: initializerAddress })).toString());
};
My workaround in this situation (that is very nasty, and isn’t working completely) is to create a temporary directory in which we dump the artifacts provided by truffle for the sdk to read them later. Once the migration is done, I can call the contract and interact with its state, but the artifact is not upgraded and the final address is not stored.
Am I doing something wrong here ? It’s working with my first solution + workaround, but I wanted to know if I missed something. It’s pretty important to be able to run truffle test as it also works with plugins like truffle-coverage and it spares a loooooot of setup time there.
Thanks !
and thanks for the work around, much appreciated.