Skip to content

Commit

Permalink
feat: Create script to verify bytecode (#127)
Browse files Browse the repository at this point in the history
  • Loading branch information
andresaiello authored Mar 1, 2024
1 parent bee7a6b commit 4fd776a
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 0 deletions.
18 changes: 18 additions & 0 deletions scripts/tools/bytecode-checker/bytecode.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const ETHERSCAN_API_ENDPOINT = "https://api.etherscan.io";
export const BSC_ETHERSCAN_API_ENDPOINT = "https://api.bscscan.com";
export const ZETA_NODE_ENDPOINT = "http://100.71.167.102:8545";

export const etherscanApiKey = process.env.ETHERSCAN_API_KEY;
export const bscEtherscanApiKey = process.env.BSCSCAN_API_KEY;

export const ethConnectorAddress = "0x000007Cf399229b2f5A4D043F20E90C9C98B7C6a";
export const ethERC20CustodyAddress = "0x000001b91C19A31809e769110d35FAd2C15BCeA7";
export const ethTokenAddress = "0xf091867EC603A6628eD83D274E835539D82e9cc8";

export const bscConnectorAddress = "0x000063A6e758D9e2f438d430108377564cf4077D";
export const bscERC20CustodyAddress = "0x0000006Abbf11Ed0FabFD247f1F4d76A383cC395";
export const bscTokenAddress = "0x0000028a2eB8346cd5c0267856aB7594B7a55308";

export const BTC_BTC = "0x13A0c5930C028511Dc02665E7285134B6d11A5f4";
export const BNB_BSC = "0x48f80608B672DC30DC7e3dbBd0343c5F02C738Eb";
export const ETH_ETH = "0xd97B1de3619ed2c6BEb3860147E30cA8A7dC9891";
94 changes: 94 additions & 0 deletions scripts/tools/bytecode-checker/bytecode.helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
const fetch = require("node-fetch");
const fs = require("fs");
const path = require("path");
const { ethers } = require("ethers");
import { BigNumber } from "ethers";

import {
BSC_ETHERSCAN_API_ENDPOINT,
bscEtherscanApiKey,
ETHERSCAN_API_ENDPOINT,
etherscanApiKey,
ZETA_NODE_ENDPOINT,
} from "./bytecode.constants";

export const encodeNumber = (weiValue: BigNumber) => {
return ethers.utils.hexZeroPad(weiValue.toHexString(), 32).replace("0x", "");
};

export const encodeAddress = (address: string) => {
return ethers.utils.hexZeroPad(address, 32).replace("0x", "");
};

export const compareBytecode = (bytecodeA: string, bytecodeB: string) => {
if (bytecodeA === bytecodeB) {
console.log("Bytecode matches!");
} else {
console.error("Bytecode doesn't match!");
}
};

export const getEtherscanBytecode = async (network: "bsc" | "eth", contractAddress: string) => {
const endpoint = network === "bsc" ? BSC_ETHERSCAN_API_ENDPOINT : ETHERSCAN_API_ENDPOINT;
const apiKey = network === "bsc" ? bscEtherscanApiKey : etherscanApiKey;
const etherscanApiUrl = `${endpoint}/api?module=proxy&action=eth_getCode&address=${contractAddress}&apikey=${apiKey}`;

try {
const response = await fetch(etherscanApiUrl);
const data = await response.json();
const bytecode = data.result;
return bytecode;
} catch (error) {
console.error("Error fetching bytecode:", error);
}
};

// @dev: helper to find differences between two bytecodes when there's a mismatch
export const findDiff = (codeA: string, codeB: string) => {
console.log("Lengths:", codeA.length, codeB.length);
for (let i = 0; i < codeA.length; i++) {
if (codeA[i] !== codeB[i]) {
console.log(i, codeA.substring(i - 20, i + 40), codeB.substring(i - 20, i + 40));
}
}
};

export const getZetaNodeBytecode = async (contractAddress: string) => {
try {
const provider = new ethers.providers.JsonRpcProvider(ZETA_NODE_ENDPOINT);
const bytecode = await provider.getCode(contractAddress);
return bytecode;
} catch (error) {
console.error("Error fetching bytecode:", error);
console.error("Please make sure you are connected to tailscale.");
}
};

export const removeImmutableAddress = (bytecode: string, pattern: string) => {
const replacement = encodeAddress(ethers.constants.AddressZero);
const regex = new RegExp(pattern, "gi");

bytecode = bytecode.replace(regex, replacement);
return bytecode;
};

export const removeImmutableNumber = (bytecode: string, pattern: string) => {
const replacement = encodeNumber(BigNumber.from("0"));
const regex = new RegExp(pattern, "gi");

bytecode = bytecode.replace(regex, replacement);
return bytecode;
};

export const getDeployedBytecode = async (contract: string, kind: "evm" | "zevm") => {
try {
const filePath = path.join(__dirname, `../../../artifacts/contracts/${kind}/${contract}.json`);
const fileContent = fs.readFileSync(filePath, "utf8");
const jsonContent = JSON.parse(fileContent);
const deployedBytecode = jsonContent.deployedBytecode;

return deployedBytecode;
} catch (error) {
console.error("Error reading the file or parsing JSON:", error);
}
};
98 changes: 98 additions & 0 deletions scripts/tools/bytecode-checker/bytecode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { BigNumber } from "ethers";
import { parseEther } from "ethers/lib/utils";

import {
BNB_BSC,
bscConnectorAddress,
bscERC20CustodyAddress,
bscTokenAddress,
BTC_BTC,
ETH_ETH,
ethConnectorAddress,
ethERC20CustodyAddress,
ethTokenAddress,
} from "./bytecode.constants";
import {
compareBytecode,
encodeAddress,
encodeNumber,
getDeployedBytecode,
getEtherscanBytecode,
getZetaNodeBytecode,
removeImmutableAddress,
removeImmutableNumber,
} from "./bytecode.helpers";

const checkEthConnectorBytecode = async () => {
const remoteBytecode = await getEtherscanBytecode("eth", ethConnectorAddress);
const cleanRemoteBytecode = removeImmutableAddress(remoteBytecode, encodeAddress(ethTokenAddress));
const deployedBytecode = await getDeployedBytecode("ZetaConnector.eth.sol/ZetaConnectorEth", "evm");
compareBytecode(cleanRemoteBytecode, deployedBytecode);
};

const checkEthCustodyBytecode = async () => {
const remoteBytecode = await getEtherscanBytecode("eth", ethERC20CustodyAddress);
let cleanRemoteBytecode = removeImmutableAddress(remoteBytecode, encodeAddress(ethTokenAddress));
cleanRemoteBytecode = removeImmutableNumber(cleanRemoteBytecode, encodeNumber(parseEther("1000"))); // zetaMaxFee
const deployedBytecode = await getDeployedBytecode("ERC20Custody.sol/ERC20Custody", "evm");
compareBytecode(cleanRemoteBytecode, deployedBytecode);
};

const checkBscConnectorBytecode = async () => {
const remoteBytecode = await getEtherscanBytecode("bsc", bscConnectorAddress);
const cleanRemoteBytecode = removeImmutableAddress(remoteBytecode, encodeAddress(bscTokenAddress));
const deployedBytecode = await getDeployedBytecode("ZetaConnector.non-eth.sol/ZetaConnectorNonEth", "evm");
compareBytecode(cleanRemoteBytecode, deployedBytecode);
};

const checkBscCustodyBytecode = async () => {
const remoteBytecode = await getEtherscanBytecode("bsc", bscERC20CustodyAddress);
let cleanRemoteBytecode = removeImmutableAddress(remoteBytecode, encodeAddress(bscTokenAddress));
cleanRemoteBytecode = removeImmutableNumber(cleanRemoteBytecode, encodeNumber(parseEther("1000"))); // zetaMaxFee
const deployedBytecode = await getDeployedBytecode("ERC20Custody.sol/ERC20Custody", "evm");
compareBytecode(cleanRemoteBytecode, deployedBytecode);
};

const checkZRC20ETHBytecode = async () => {
const remoteBytecode = await getZetaNodeBytecode(ETH_ETH);
let cleanRemoteBytecode = removeImmutableNumber(remoteBytecode, encodeNumber(BigNumber.from("1"))); // ETH CHAIN ID
cleanRemoteBytecode = removeImmutableNumber(cleanRemoteBytecode, encodeNumber(BigNumber.from("1"))); // Gas COIN TYPE
const deployedBytecode = await getDeployedBytecode("ZRC20.sol/ZRC20", "zevm");
compareBytecode(cleanRemoteBytecode, deployedBytecode);
};

const checkZRC20BTCBytecode = async () => {
const remoteBytecode = await getZetaNodeBytecode(BTC_BTC);
let cleanRemoteBytecode = removeImmutableNumber(remoteBytecode, encodeNumber(BigNumber.from("8332"))); // BTC CHAIN ID
cleanRemoteBytecode = removeImmutableNumber(cleanRemoteBytecode, encodeNumber(BigNumber.from("1"))); // Gas COIN TYPE
const deployedBytecode = await getDeployedBytecode("ZRC20.sol/ZRC20", "zevm");
compareBytecode(cleanRemoteBytecode, deployedBytecode);
};

const checkZRC20BSCBytecode = async () => {
const remoteBytecode = await getZetaNodeBytecode(BNB_BSC);
let cleanRemoteBytecode = removeImmutableNumber(remoteBytecode, encodeNumber(BigNumber.from("56"))); // BSC CHAIN ID
cleanRemoteBytecode = removeImmutableNumber(cleanRemoteBytecode, encodeNumber(BigNumber.from("1"))); // Gas COIN TYPE
const deployedBytecode = await getDeployedBytecode("ZRC20.sol/ZRC20", "zevm");
compareBytecode(cleanRemoteBytecode, deployedBytecode);
};

const checkBytecode = async () => {
// ETH
await checkEthConnectorBytecode();
await checkEthCustodyBytecode();
// BSC
await checkBscConnectorBytecode();
await checkBscCustodyBytecode();
// ZEVM
await checkZRC20ETHBytecode();
await checkZRC20BTCBytecode();
await checkZRC20BSCBytecode();
};

checkBytecode()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});

0 comments on commit 4fd776a

Please sign in to comment.