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 !