Skip to content

Commit

Permalink
Update l2 ERC-20 metadata script (#194)
Browse files Browse the repository at this point in the history
  • Loading branch information
AntonD3 authored Feb 7, 2024
1 parent b7615f8 commit 6f322ce
Show file tree
Hide file tree
Showing 2 changed files with 190 additions and 1 deletion.
3 changes: 2 additions & 1 deletion l2-contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
"deploy-force-deploy-upgrader": "ts-node src/deployForceDeployUpgrader.ts",
"publish-bridge-preimages": "ts-node src/publish-bridge-preimages.ts",
"deploy-l2-weth": "ts-node src/deployL2Weth.ts",
"upgrade-bridge-contracts": "ts-node src/upgradeBridgeImpl.ts"
"upgrade-bridge-contracts": "ts-node src/upgradeBridgeImpl.ts",
"update-l2-erc20-metadata": "ts-node src/updateL2ERC20Metadata.ts"
},
"dependencies": {
"dotenv": "^16.0.3"
Expand Down
188 changes: 188 additions & 0 deletions l2-contracts/src/updateL2ERC20Metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import * as hre from "hardhat";
import "@nomiclabs/hardhat-ethers";
import { Command } from "commander";
import { Wallet, ethers, BigNumber } from "ethers";
import * as fs from "fs";
import * as path from "path";
import { Provider } from "zksync-web3";
import { getNumberFromEnv, web3Provider } from "../../l1-contracts/scripts/utils";
import { Deployer } from "../../l1-contracts/src.ts/deploy";
import { getL1TxInfo } from "./utils";

// From openzeppelin-upgradable v4.9.5 Initializable contract implementation.
const INITIALIZED_STORAGE_SLOT = 0;
const priorityTxMaxGasLimit = BigNumber.from(getNumberFromEnv("CONTRACTS_PRIORITY_TX_MAX_GAS_LIMIT"));
const provider = web3Provider();
const testConfigPath = path.join(process.env.ZKSYNC_HOME as string, "etc/test_config/constant");
const ethTestConfig = JSON.parse(fs.readFileSync(`${testConfigPath}/eth.json`, { encoding: "utf-8" }));

async function getReinitializeTokenCalldata(
newName: string,
newSymbol: string,
ignoreNameGetter: boolean,
ignoreSymbolGetter: boolean,
ignoreDecimalsGetter: boolean,
version: ethers.BigNumberish
) {
const l2StandardERC20Artifact = await hre.artifacts.readArtifact("L2StandardERC20");
const l2StandardERC20Interface = new ethers.utils.Interface(l2StandardERC20Artifact.abi);

const availableGetters = {
ignoreName: ignoreNameGetter,
ignoreSymbol: ignoreSymbolGetter,
ignoreDecimals: ignoreDecimalsGetter,
};

console.log("Using the following arguments:");
console.log(`availableGetters = ${JSON.stringify(availableGetters, null, 4)}`);
console.log(`newName = "${newName}"`);
console.log(`newSymbol = "${newSymbol}"`);
console.log(`version = ${version}\n`);

return l2StandardERC20Interface.encodeFunctionData("reinitializeToken", [
availableGetters,
newName,
newSymbol,
version,
]);
}

async function getReinitializeTokenTxInfo(
deployer: Deployer,
refundRecipient: string,
gasPrice: BigNumber,
tokenAddress: string,
newName: string,
newSymbol: string,
ignoreNameGetter: boolean,
ignoreSymbolGetter: boolean,
ignoreDecimalsGetter: boolean,
version: ethers.BigNumberish
) {
const l2Calldata = await getReinitializeTokenCalldata(
newName,
newSymbol,
ignoreNameGetter,
ignoreSymbolGetter,
ignoreDecimalsGetter,
version
);
return await getL1TxInfo(
deployer,
tokenAddress,
l2Calldata,
refundRecipient,
gasPrice,
priorityTxMaxGasLimit,
provider
);
}

async function main() {
const program = new Command();

program.version("0.1.0").name("update-l2-erc20-metadata");

program
.option("--token-address <upgrades-info>")
.option("--gas-price <gas-price>")
.option("--deployer-private-key <deployer-private-key>")
.option("--refund-recipient <refund-recipient>")
.option("--new-name <new-name>")
.option("--new-symbol <new-symbol>")
.option("--ignore-name-getter")
.option("--ignore-symbol-getter")
.option("--ignore-decimals-getter")
.option("--reinitialization-version <version>")
.action(async (cmd) => {
const gasPrice = cmd.gasPrice
? ethers.utils.parseUnits(cmd.gasPrice, "gwei")
: (await provider.getGasPrice()).mul(3).div(2);
const deployWallet = cmd.deployerPrivateKey
? new Wallet(cmd.deployerPrivateKey, provider)
: Wallet.fromMnemonic(
process.env.MNEMONIC ? process.env.MNEMONIC : ethTestConfig.mnemonic,
"m/44'/60'/0'/0/1"
).connect(provider);
const deployer = new Deployer({ deployWallet });
const refundRecipient = cmd.refundRecipient ? cmd.refundRecipient : deployWallet.address;
console.log("Gas price: ", ethers.utils.formatUnits(gasPrice, "gwei"));
console.log(
"IMPORTANT: gasPrice that you provide in the transaction should be <= to the one provided to this tool."
);

console.log("Refund recipient: ", refundRecipient);

const tokenAddress = cmd.tokenAddress;
const newName = cmd.newName;
const newSymbol = cmd.newSymbol;
const ignoreNameGetter = cmd.ignoreNameGetter ?? false;
const ignoreSymbolGetter = cmd.ignoreSymbolGetter ?? false;
const ignoreDecimalsGetter = cmd.ignoreDecimalsGetter ?? false;

let version;
if (cmd.reinitializationVersion === undefined) {
const provider = new Provider(process.env.API_WEB3_JSON_RPC_HTTP_URL);
const initializableStorageValue = BigNumber.from(
await provider.getStorageAt(tokenAddress, INITIALIZED_STORAGE_SLOT)
);

// currently, it's saved in the first `uint8` variable, so should be stored in the lowest byte
const initializedValue = initializableStorageValue.mod(BigNumber.from(2).pow(8));
version = initializedValue.add(1);
} else {
version = parseInt(cmd.reinitializationVersion);
}

if (newName && ignoreNameGetter) {
console.log("\x1b[31mWarning: ignore name getter flag used while new name is not empty\x1b[0m");
}
if (newSymbol && ignoreSymbolGetter) {
console.log("\x1b[31mWarning: ignore symbol getter flag used while new symbol is not empty\x1b[0m");
}

const governanceCall = await getReinitializeTokenTxInfo(
deployer,
refundRecipient,
gasPrice,
tokenAddress,
newName,
newSymbol,
ignoreNameGetter,
ignoreSymbolGetter,
ignoreDecimalsGetter,
version
);

const operation = {
calls: [governanceCall],
predecessor: ethers.constants.HashZero,
salt: ethers.constants.HashZero,
};

console.log("Governance calls: ");
console.log(JSON.stringify(operation, null, 4) + "\n");

const governance = deployer.governanceContract(deployWallet);
const scheduleTransparentCalldata = governance.interface.encodeFunctionData("scheduleTransparent", [
operation,
0,
]);
const executeCalldata = governance.interface.encodeFunctionData("execute", [operation]);

console.log("scheduleTransparentCalldata: ");
console.log(scheduleTransparentCalldata);

console.log("executeCalldata: ");
console.log(executeCalldata);
});

await program.parseAsync(process.argv);
}

main()
.then(() => process.exit(0))
.catch((err) => {
console.error("Error:", err);
process.exit(1);
});

0 comments on commit 6f322ce

Please sign in to comment.