Repro steps
mkdir repro && cd repro
npm init -y
- Install
npm
packages
npm i --save-dev truffle &&
npm i --save-dev @openzeppelin/truffle-upgrades &&
npm i --save-dev @openzeppelin/contracts-upgradeable &&
npm i --save-dev @truffle/hdwallet-provider
-
npx truffle init
-
contracts/AppleCoin.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.6.0;
import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/presets/ERC20PresetMinterPauserUpgradeable.sol";
contract OrangeCoin is ERC20PresetMinterPauserUpgradeable {
function initialize(string memory name, string memory symbol) public override virtual initializer {
ERC20PresetMinterPauserUpgradeable.initialize(name, symbol);
}
}
contract AppleCoin is ERC20PresetMinterPauserUpgradeable {
function initialize(string memory name, string memory symbol) public override virtual initializer {
ERC20PresetMinterPauserUpgradeable.initialize(name, symbol);
}
}
migrations/2_deploy_orange_apple.js
const AppleCoin = artifacts.require('AppleCoin');
const OrangeCoin = artifacts.require('OrangeCoin');
const { deployProxy } = require('@openzeppelin/truffle-upgrades');
module.exports = async function (deployer) {
await deployProxy(AppleCoin, ["Apple Coin", "ACN"], { deployer, unsafeAllowCustomTypes : true, initializer: 'initialize' });
await deployProxy(OrangeCoin, ["Orange Coin", "OCN"], { deployer, unsafeAllowCustomTypes : true, initializer: 'initialize' });
};
-
Put the required stuff in
truffle-config.js
such as Infura key -
npx truffle migrate network --rinkeby
Result
Some data is stored in .openzeppelin/rinkeby.json
Some data is stored in build/contracts/AppleCoin.json
and OrangeCoin.json
Does it mean I need to keep build
folder in the repo to keep track of deployments?
(currently I had it in .gitignore
)
Some data is displayed in the truffle console
2_deploy_orange_apple.js
========================
Warning: Potentially unsafe deployment of AppleCoin
You are using the `unsafeAllowCustomTypes` flag to skip storage checks for structs and enums.
Make sure you have manually checked the storage layout for incompatibilities.
Deploying 'AppleCoin'
---------------------
> transaction hash: 0x610808c26130f970cb0cd806059ca982ee09c9f48b917e393773c1b7514d6f8e
> Blocks: 1 Seconds: 12
> contract address: 0x70a53f2E794EBBaBb3e860Fe457BFa6f9a528778
> block number: 7836548
> block timestamp: 1609775689
> account: 0xBAE8E5F877E15df0a1Ec36bFa6a9903B837d003a
> balance: 7.6445294721
> gas used: 1803517 (0x1b84fd)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.03607034 ETH
Pausing for 1 confirmations...
------------------------------
> confirmation number: 1 (block: 7836549)
Deploying 'ProxyAdmin'
----------------------
> transaction hash: 0x1f869f042afb812571f295e32f0ae3285abe72ac4661442d97ad1c7e7f29498c
> Blocks: 2 Seconds: 24
> contract address: 0xa0b8826281583Bd05a644392158e1DC1C106Ec9B
> block number: 7836551
> block timestamp: 1609775734
> account: 0xBAE8E5F877E15df0a1Ec36bFa6a9903B837d003a
> balance: 7.6269302321
> gas used: 879962 (0xd6d5a)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.01759924 ETH
Pausing for 1 confirmations...
------------------------------
> confirmation number: 1 (block: 7836552)
Deploying 'AdminUpgradeabilityProxy'
------------------------------------
> transaction hash: 0x906643ac72752811841c88febf0782612e816c1ded52c48efea9649100751a64
> Blocks: 1 Seconds: 24
> contract address: 0x0EF0eEBD75eb3cAf29a4bfc3348cF998cf5624e5
> block number: 7836554
> block timestamp: 1609775779
> account: 0xBAE8E5F877E15df0a1Ec36bFa6a9903B837d003a
> balance: 7.6088829121
> gas used: 902366 (0xdc4de)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.01804732 ETH
Pausing for 1 confirmations...
------------------------------
> confirmation number: 1 (block: 7836555)
Warning: Potentially unsafe deployment of OrangeCoin
You are using the `unsafeAllowCustomTypes` flag to skip storage checks for structs and enums.
Make sure you have manually checked the storage layout for incompatibilities.
Deploying 'AdminUpgradeabilityProxy'
------------------------------------
> transaction hash: 0x4bb22381d055686fae41e8fa0f75a88fa5224d2e6b6dae9465a94e04a6b2bdb5
> Blocks: 1 Seconds: 20
> contract address: 0x919720A1C2B31d9c706ac0853f5b8B3B6466C297
> block number: 7836557
> block timestamp: 1609775824
> account: 0xBAE8E5F877E15df0a1Ec36bFa6a9903B837d003a
> balance: 7.5908353521
> gas used: 902378 (0xdc4ea)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.01804756 ETH
Pausing for 1 confirmations...
------------------------------
> confirmation number: 1 (block: 7836558)
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.08976446 ETH
Summary
=======
> Total deployments: 4
> Final cost: 0.08976446 ETH
Deployed contracts:
Verify on Etherscan: truffle-flattener contracts/OrangeCoin.sol > merged.sol
(and remove duplicate SPDX)
-
Implementation: https://rinkeby.etherscan.io/address/0x70a53f2E794EBBaBb3e860Fe457BFa6f9a528778#code
-
Orange Coin proxy: https://rinkeby.etherscan.io/address/0x919720A1C2B31d9c706ac0853f5b8B3B6466C297#readProxyContract
-
Apple Coin proxy: https://rinkeby.etherscan.io/address/0x0EF0eEBD75eb3cAf29a4bfc3348cF998cf5624e5#readProxyContract
EDIT / UPDATE:
As I’m figuring out what is going on will surely update this post
I think I’ll create a base contract and add a dummy function so that the contracts are not identical.
I think there are some “optimisations” along the way that try to preserve gas. This might be simpler, maybe, potentially.
contract OrangeCoin is ERC20PresetMinterPauserUpgradeable {
function dummyOrange() public returns(string memory) {
return "orange";
}
function initialize(string memory name, string memory symbol) public override virtual initializer {
ERC20PresetMinterPauserUpgradeable.initialize(name, symbol);
}
}
contract AppleCoin is ERC20PresetMinterPauserUpgradeable {
function dummyApple() public returns(string memory) {
return "apple";
}
function initialize(string memory name, string memory symbol) public override virtual initializer {
ERC20PresetMinterPauserUpgradeable.initialize(name, symbol);
}
}