Skip to content

Commit

Permalink
Add migration option to directly deploy v2.2 token
Browse files Browse the repository at this point in the history
* Add migration option to directly deploy v2.2 token

* Minor modification to migration script

* Update copyright headers for new files

* Minor changes to deploy scripts
  • Loading branch information
yvonnezhangc authored and circle-aloychan committed Oct 27, 2023
1 parent 08e3b16 commit 35d66ae
Show file tree
Hide file tree
Showing 15 changed files with 263 additions and 35 deletions.
8 changes: 8 additions & 0 deletions config.js.example
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,16 @@ module.exports = {
OWNER_ADDRESS: "",
// Master Minter - can configure minters and minter allowance
MASTERMINTER_ADDRESS: "",
// Master Minter Owner - owner of master minter contract
MASTERMINTER_OWNER_ADDRESS: "",
// Pauser - can pause the contract
PAUSER_ADDRESS: "",
// Blacklister - can blacklist addresses
BLACKLISTER_ADDRESS: "",
// FiatTokenProxy contract - override the contract address used in migrations
PROXY_CONTRACT_ADDRESS: "",
// FiatToken Implementation contract - deploy new proxy with an existing implementation contract
FIAT_TOKEN_IMPLEMENTATION_ADDRESS: "",
// LostAndFound - tokens that were locked in the contract are sent to this
LOST_AND_FOUND_ADDRESS: "",
// MockERC1271WalletOwner - can deploy and send transactions from a sample ERC1271 wallet
Expand All @@ -46,4 +50,8 @@ module.exports = {
TOKEN_CURRENCY: "USD",
// TokenDecimals - Number of decimals for the token e.g. 6
TOKEN_DECIMALS: 6,

// USE_VERSIONED_MIGRATIONS - whether or not to use migrations in migrations/versioned directory.
// These migrations deploy each version of FiatToken separately.
USE_VERSIONED_MIGRATIONS: true,
};
File renamed without changes.
179 changes: 179 additions & 0 deletions migrations/direct/2_deploy_implementation_and_proxy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/**
* SPDX-License-Identifier: Apache-2.0
*
* Copyright (c) 2023, Circle Internet Financial, LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

const fs = require("fs");
const path = require("path");

const FiatTokenV2_2 = artifacts.require("FiatTokenV2_2");
const FiatTokenProxy = artifacts.require("FiatTokenProxy");
const MasterMinter = artifacts.require("MasterMinter");
const SignatureChecker = artifacts.require("SignatureChecker");

const THROWAWAY_ADDRESS = "0x0000000000000000000000000000000000000001";

let proxyAdminAddress = "";
let ownerAddress = "";
let pauserAddress = "";
let blacklisterAddress = "";
let lostAndFoundAddress = "";
let masterMinterOwnerAddress = "";
let fiatTokenImplementationAddress = "";
let tokenName = "";
let tokenSymbol = "";
let tokenCurrency = "";
let tokenDecimals = "";

// Read config file if it exists
if (fs.existsSync(path.join(__dirname, "..", "..", "config.js"))) {
({
PROXY_ADMIN_ADDRESS: proxyAdminAddress,
OWNER_ADDRESS: ownerAddress,
PAUSER_ADDRESS: pauserAddress,
BLACKLISTER_ADDRESS: blacklisterAddress,
LOST_AND_FOUND_ADDRESS: lostAndFoundAddress,
MASTERMINTER_OWNER_ADDRESS: masterMinterOwnerAddress,
FIAT_TOKEN_IMPLEMENTATION_ADDRESS: fiatTokenImplementationAddress,
TOKEN_NAME: tokenName,
TOKEN_SYMBOL: tokenSymbol,
TOKEN_CURRENCY: tokenCurrency,
TOKEN_DECIMALS: tokenDecimals,
} = require("../../config.js"));
}

/**
* A utility script to directly deploy Fiat Token contract with the latest implementation
*
* Note: The proxy needs to be deployed before the master minter; the proxy cannot
* be initialized until the master minter is deployed.
*/
module.exports = async (deployer, network) => {
if (!proxyAdminAddress || !ownerAddress || !masterMinterOwnerAddress) {
throw new Error(
"PROXY_ADMIN_ADDRESS, OWNER_ADDRESS, and MASTERMINTER_OWNER_ADDRESS must be provided in config.js"
);
}

if (!pauserAddress || !blacklisterAddress || !lostAndFoundAddress) {
if (network === "mainnet") {
throw new Error(
"PAUSER_ADDRESS, BLACKLISTER_ADDRESS and LOST_AND_FOUND_ADDRESS must be provided in config.js"
);
} else {
// If we're not on mainnet, let the user values dictate this.
pauserAddress = pauserAddress || ownerAddress;
blacklisterAddress = blacklisterAddress || ownerAddress;
lostAndFoundAddress = lostAndFoundAddress || ownerAddress;
}
}

console.log(`Proxy Admin: ${proxyAdminAddress}`);
console.log(`Owner: ${ownerAddress}`);
console.log(`Pauser: ${pauserAddress}`);
console.log(`Blacklister: ${blacklisterAddress}`);
console.log(`Lost and Found: ${lostAndFoundAddress}`);
console.log(`Master Minter Owner: ${masterMinterOwnerAddress}`);
console.log(
`FiatTokenImplementationAddress: ${fiatTokenImplementationAddress}`
);

// If there is an existing implementation contract,
// we can simply point the newly deployed proxy contract to it.
// Otherwise, deploy the latest implementation contract code to the network.
if (!fiatTokenImplementationAddress) {
console.log("Deploying and linking SignatureChecker library contract...");
await deployer.deploy(SignatureChecker);
await deployer.link(SignatureChecker, FiatTokenV2_2);

console.log("Deploying implementation contract...");
await deployer.deploy(FiatTokenV2_2);
const fiatTokenV2_2 = await FiatTokenV2_2.deployed();
console.log("Deployed implementation contract at", FiatTokenV2_2.address);

console.log("Initializing implementation contract with dummy values...");
// These values are dummy values because we only rely on the implementation
// deployment for delegatecall logic, not for actual state storage.
await fiatTokenV2_2.initialize(
"",
"",
"",
0,
THROWAWAY_ADDRESS,
THROWAWAY_ADDRESS,
THROWAWAY_ADDRESS,
THROWAWAY_ADDRESS
);
await fiatTokenV2_2.initializeV2("");
await fiatTokenV2_2.initializeV2_1(THROWAWAY_ADDRESS);
await fiatTokenV2_2.initializeV2_2([], "");

fiatTokenImplementationAddress = FiatTokenV2_2.address;
}

console.log("Deploying proxy contract...");
await deployer.deploy(FiatTokenProxy, fiatTokenImplementationAddress);
const fiatTokenProxy = await FiatTokenProxy.deployed();
console.log("Deployed proxy contract at", FiatTokenProxy.address);

// Now that the proxy contract has been deployed, we can deploy the master minter.
console.log("Deploying master minter...");
await deployer.deploy(MasterMinter, FiatTokenProxy.address);
const masterMinter = await MasterMinter.deployed();
console.log("Deployed master minter at", MasterMinter.address);

// Change the master minter to be owned by the master minter owner
console.log("Reassigning master minter owner...");
await masterMinter.transferOwnership(masterMinterOwnerAddress);

// Now that the master minter is set up, we can go back to setting up the proxy and
// implementation contracts.
console.log("Reassigning proxy contract admin...");
// Need to change admin first, or the call to initialize won't work
// since admin can only call methods in the proxy, and not forwarded methods
await fiatTokenProxy.changeAdmin(proxyAdminAddress);

console.log("Initializing proxy contract...");

// Do the initial (V1) initialization.
// Note that this takes in the master minter contract's address as the master minter.
// The master minter contract's owner is a separate address.
const proxyAsV2_2 = await FiatTokenV2_2.at(FiatTokenProxy.address);
await proxyAsV2_2.initialize(
tokenName,
tokenSymbol,
tokenCurrency,
tokenDecimals,
masterMinter.address,
pauserAddress,
blacklisterAddress,
ownerAddress
);

// Do the V2 initialization
console.log("Initializing V2...");
await proxyAsV2_2.initializeV2(tokenName);

// Do the V2_1 initialization
console.log("Initializing V2.1...");
await proxyAsV2_2.initializeV2_1(lostAndFoundAddress);

// Do the V2_2 initialization
console.log("Initializing V2.2...");
await proxyAsV2_2.initializeV2_2([], tokenSymbol);

console.log("Deployment step 2 finished");
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,33 +20,39 @@ const fs = require("fs");
const path = require("path");
const MasterMinter = artifacts.require("MasterMinter.sol");
const FiatTokenProxy = artifacts.require("FiatTokenProxy.sol");
let masterMinterAddress = "";
let masterMinterOwnerAddress = "";
let fiatTokenAddress = "";

// Read config file if it exists
if (fs.existsSync(path.join(__dirname, "..", "config.js"))) {
if (fs.existsSync(path.join(__dirname, "..", "..", "config.js"))) {
({
MASTERMINTER_ADDRESS: masterMinterAddress,
MASTERMINTER_OWNER_ADDRESS: masterMinterOwnerAddress,
PROXY_CONTRACT_ADDRESS: fiatTokenAddress,
} = require("../config.js"));
} = require("../../config.js"));
}

module.exports = function (deployer, network) {
module.exports = async (deployer, network) => {
if (network === "development" || network === "coverage") {
// Change these if deploying for real, these are deterministic
// address from ganache
masterMinterAddress = "0x3e5e9111ae8eb78fe1cc3bb8915d5d461f3ef9a9";
masterMinterOwnerAddress = "0x3e5e9111ae8eb78fe1cc3bb8915d5d461f3ef9a9";
fiatTokenAddress = FiatTokenProxy.address;
}
console.log("deploying MasterMinter for fiat token at " + fiatTokenAddress);
deployer
.deploy(MasterMinter, fiatTokenAddress)
.then(function (mm) {
console.log("master minter deployed at " + mm.address);
console.log("reassigning owner to " + masterMinterAddress);
return mm.transferOwnership(masterMinterAddress);
})
.then(function () {
console.log("All done.");
});

console.log(
`Deploying MasterMinter for fiat token at ${fiatTokenAddress}...`
);

await deployer.deploy(MasterMinter, fiatTokenAddress);
const masterMinter = await MasterMinter.deployed();

console.log(
`>>>>>>> Deployed MasterMinter at ${masterMinter.address} <<<<<<<`
);
console.log(
`Reassigning MasterMinter owner to ${masterMinterOwnerAddress}...`
);

await masterMinter.transferOwnership(masterMinterOwnerAddress);
console.log("All done.");
};
23 changes: 23 additions & 0 deletions migrations/versioned/1_initial_migration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* SPDX-License-Identifier: Apache-2.0
*
* Copyright (c) 2023, Circle Internet Financial, LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

const Migrations = artifacts.require("Migrations");

module.exports = async (deployer) => {
await deployer.deploy(Migrations);
};
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ let tokenCurrency = "";
let tokenDecimals = 0;

// Read config file if it exists
if (fs.existsSync(path.join(__dirname, "..", "config.js"))) {
if (fs.existsSync(path.join(__dirname, "..", "..", "config.js"))) {
({
PROXY_ADMIN_ADDRESS: proxyAdminAddress,
OWNER_ADDRESS: ownerAddress,
Expand All @@ -47,7 +47,7 @@ if (fs.existsSync(path.join(__dirname, "..", "config.js"))) {
TOKEN_SYMBOL: tokenSymbol,
TOKEN_CURRENCY: tokenCurrency,
TOKEN_DECIMALS: tokenDecimals,
} = require("../config.js"));
} = require("../../config.js"));
}

module.exports = async (deployer, network) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ const THROWAWAY_ADDRESS = "0x0000000000000000000000000000000000000001";
let proxyContractAddress = "";

// Read config file if it exists
if (fs.existsSync(path.join(__dirname, "..", "config.js"))) {
({ PROXY_CONTRACT_ADDRESS: proxyContractAddress } = require("../config.js"));
if (fs.existsSync(path.join(__dirname, "..", "..", "config.js"))) {
({
PROXY_CONTRACT_ADDRESS: proxyContractAddress,
} = require("../../config.js"));
}

module.exports = async (deployer, network) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ let proxyContractAddress = "";
let tokenName = "";

// Read config file if it exists
if (fs.existsSync(path.join(__dirname, "..", "config.js"))) {
if (fs.existsSync(path.join(__dirname, "..", "..", "config.js"))) {
({
PROXY_ADMIN_ADDRESS: proxyAdminAddress,
PROXY_CONTRACT_ADDRESS: proxyContractAddress,
TOKEN_NAME: tokenName,
} = require("../config.js"));
} = require("../../config.js"));
}

module.exports = async (deployer, network) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ const THROWAWAY_ADDRESS = "0x0000000000000000000000000000000000000001";
let proxyContractAddress = "";

// Read config file if it exists
if (fs.existsSync(path.join(__dirname, "..", "config.js"))) {
({ PROXY_CONTRACT_ADDRESS: proxyContractAddress } = require("../config.js"));
if (fs.existsSync(path.join(__dirname, "..", "..", "config.js"))) {
({
PROXY_CONTRACT_ADDRESS: proxyContractAddress,
} = require("../../config.js"));
}

module.exports = async (deployer, network) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ let proxyContractAddress = "";
let lostAndFoundAddress = "";

// Read config file if it exists
if (fs.existsSync(path.join(__dirname, "..", "config.js"))) {
if (fs.existsSync(path.join(__dirname, "..", "..", "config.js"))) {
({
PROXY_ADMIN_ADDRESS: proxyAdminAddress,
PROXY_CONTRACT_ADDRESS: proxyContractAddress,
LOST_AND_FOUND_ADDRESS: lostAndFoundAddress,
} = require("../config.js"));
} = require("../../config.js"));
}

module.exports = async (deployer, network) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ const THROWAWAY_ADDRESS = "0x0000000000000000000000000000000000000001";
let proxyContractAddress = "";

// Read config file if it exists
if (fs.existsSync(path.join(__dirname, "..", "config.js"))) {
({ PROXY_CONTRACT_ADDRESS: proxyContractAddress } = require("../config.js"));
if (fs.existsSync(path.join(__dirname, "..", "..", "config.js"))) {
({
PROXY_CONTRACT_ADDRESS: proxyContractAddress,
} = require("../../config.js"));
}

module.exports = async (deployer, network) => {
Expand Down
Loading

0 comments on commit 35d66ae

Please sign in to comment.