Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ import "@nomicfoundation/hardhat-chai-matchers";
import "./scripts/deploy";
import "./scripts/publish";
import "./scripts/upgrade";
import "./scripts/check-beacon-versions";
import "./scripts/check-deployment-mismatch";
import "./scripts/sync-beacon-implementations";
import "./scripts/upgrade-factory-beacon";
import "./scripts/check-factory-proxy-type";
import "./scripts/deploy-new-factory";
import "./scripts/deploy-new-paymaster";
import "./scripts/fund-paymaster";
import "./scripts/verify-paymaster-factory";
import "./scripts/debug-paymaster-error";

import { HardhatUserConfig } from "hardhat/config";

Expand Down
93 changes: 93 additions & 0 deletions scripts/check-beacon-versions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { ethers } from "ethers";
import fs from "fs";
import { task } from "hardhat/config";

task("check-beacon-versions", "Checks what beacon versions are deployed")
.addParam("proxyfile", "location of the file with proxy contract addresses")
.setAction(async (cmd, hre) => {
console.log("Checking beacon versions for network:", hre.network.name);

// Read the deployed contract addresses
const proxyAddresses = JSON.parse(fs.readFileSync(cmd.proxyfile).toString());
console.log("Proxy addresses loaded:", proxyAddresses);

const factoryAddress = proxyAddresses.accountFactory;
if (!factoryAddress) {
throw new Error("No accountFactory address found in proxy file");
}

// Get provider
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { getProvider } = require("../test/utils");
const provider = getProvider();

// Factory ABI to read the beacon address
const factoryAbi = [
"function beacon() view returns (address)",
"function getEncodedBeacon() view returns (bytes)",
];

// Beacon ABI to read the implementation
const beaconAbi = [
"function implementation() view returns (address)",
];

try {
// Get beacon address from factory
const factory = new ethers.Contract(factoryAddress, factoryAbi, provider);
console.log("\n🏭 Checking factory at:", factoryAddress);

const beaconAddress = await factory.beacon();
console.log("📡 Factory points to beacon:", beaconAddress);

// Get implementation from beacon
const beacon = new ethers.Contract(beaconAddress, beaconAbi, provider);
const implementationAddress = await beacon.implementation();
console.log("🎯 Beacon points to implementation:", implementationAddress);

// Check if there are any other beacons we should know about
console.log("\n📊 Summary:");
console.log("Factory:", factoryAddress);
console.log("Beacon:", beaconAddress);
console.log("Implementation:", implementationAddress);

// Also check all proxy contracts and see what they point to
console.log("\n🔍 Checking all proxy contracts:");
const proxyAbi = [
"function implementation() view returns (address)",
"function beacon() view returns (address)",
];

for (const [contractName, contractAddress] of Object.entries(proxyAddresses)) {
if (contractName === "accountPaymaster") {
console.log(`${contractName}: ${contractAddress} (not a proxy)`);
continue;
}

try {
const proxy = new ethers.Contract(contractAddress as string, proxyAbi, provider);

// Try to get implementation directly (for transparent proxies)
try {
const impl = await proxy.implementation();
console.log(`${contractName}: ${contractAddress} -> impl: ${impl}`);
} catch {
// Try to get beacon (for beacon proxies)
try {
const beaconAddr = await proxy.beacon();
const beaconContract = new ethers.Contract(beaconAddr, beaconAbi, provider);
const impl = await beaconContract.implementation();
console.log(`${contractName}: ${contractAddress} -> beacon: ${beaconAddr} -> impl: ${impl}`);
} catch {
console.log(`${contractName}: ${contractAddress} (could not determine proxy type)`);
}
}
} catch (error) {
console.log(`${contractName}: ${contractAddress} (error reading: ${error.message})`);
}
}
} catch (error) {
console.error("Error:", error);
throw error;
}
});
116 changes: 116 additions & 0 deletions scripts/check-deployment-mismatch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { ethers } from "ethers";
import fs from "fs";
import { task } from "hardhat/config";

task("check-deployment-mismatch", "Checks for deployment mismatches between expected and actual beacon addresses")
.addParam("proxyfile", "location of the file with proxy contract addresses")
.addParam("expectedbeacon", "expected beacon address")
.setAction(async (cmd, hre) => {
console.log("🔍 Checking for deployment mismatches on network:", hre.network.name);
console.log("Expected beacon address:", cmd.expectedbeacon);

// Read the deployed contract addresses
const proxyAddresses = JSON.parse(fs.readFileSync(cmd.proxyfile).toString());

const factoryAddress = proxyAddresses.accountFactory;
if (!factoryAddress) {
throw new Error("No accountFactory address found in proxy file");
}

// Get provider
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { getProvider } = require("../test/utils");
const provider = getProvider();

// Factory ABI to read the beacon address
const factoryAbi = [
"function beacon() view returns (address)",
];

// Beacon ABI to read the implementation
const beaconAbi = [
"function implementation() view returns (address)",
"function owner() view returns (address)",
];

try {
// Get actual beacon address from factory
const factory = new ethers.Contract(factoryAddress, factoryAbi, provider);
const actualBeaconAddress = await factory.beacon();

console.log("\n📊 MISMATCH ANALYSIS:");
console.log("🏭 Factory address:", factoryAddress);
console.log("🎯 Expected beacon:", cmd.expectedbeacon);
console.log("📡 Actual beacon: ", actualBeaconAddress);
console.log("❌ MISMATCH:", actualBeaconAddress.toLowerCase() !== cmd.expectedbeacon.toLowerCase() ? "YES" : "NO");

// Check both beacons
console.log("\n🔍 CHECKING BOTH BEACONS:");

// Check expected beacon
console.log("\n1️⃣ Expected Beacon:", cmd.expectedbeacon);
try {
const expectedBeacon = new ethers.Contract(cmd.expectedbeacon, beaconAbi, provider);
const expectedImpl = await expectedBeacon.implementation();
console.log(" ✅ Implementation:", expectedImpl);

try {
const expectedOwner = await expectedBeacon.owner();
console.log(" 👤 Owner:", expectedOwner);
} catch {
console.log(" 👤 Owner: (not readable or no owner function)");
}
} catch (error) {
console.log(" ❌ Error reading expected beacon:", error.message);
}

// Check actual beacon
console.log("\n2️⃣ Actual Beacon:", actualBeaconAddress);
try {
const actualBeacon = new ethers.Contract(actualBeaconAddress, beaconAbi, provider);
const actualImpl = await actualBeacon.implementation();
console.log(" ✅ Implementation:", actualImpl);

try {
const actualOwner = await actualBeacon.owner();
console.log(" 👤 Owner:", actualOwner);
} catch {
console.log(" 👤 Owner: (not readable or no owner function)");
}
} catch (error) {
console.log(" ❌ Error reading actual beacon:", error.message);
}

// Check if there are any accounts using the old beacon
console.log("\n🔍 IMPACT ANALYSIS:");
if (actualBeaconAddress.toLowerCase() !== cmd.expectedbeacon.toLowerCase()) {
console.log("⚠️ POTENTIAL ISSUES:");
console.log(" • The factory is pointing to a different beacon than expected");
console.log(" • New accounts will use beacon:", actualBeaconAddress);
console.log(" • If old accounts exist, they might use beacon:", cmd.expectedbeacon);
console.log(" • Upgrading only one beacon will not upgrade all accounts");
console.log("\n💡 RECOMMENDATIONS:");
console.log(" • Check if both beacons have accounts pointing to them");
console.log(" • Upgrade both beacons to ensure all accounts are updated");
console.log(" • Or migrate all accounts to use the same beacon");
} else {
console.log("✅ No mismatch detected - factory points to expected beacon");
}

// Try to get some bytecode to see if the contracts are identical
console.log("\n🔍 BYTECODE COMPARISON:");
try {
const expectedBytecode = await provider.getCode(cmd.expectedbeacon);
const actualBytecode = await provider.getCode(actualBeaconAddress);

console.log("Expected beacon bytecode length:", expectedBytecode.length);
console.log("Actual beacon bytecode length:", actualBytecode.length);
console.log("Bytecode identical:", expectedBytecode === actualBytecode ? "✅ YES" : "❌ NO");
} catch (error) {
console.log("Error comparing bytecode:", error.message);
}
} catch (error) {
console.error("Error:", error);
throw error;
}
});
99 changes: 99 additions & 0 deletions scripts/check-factory-proxy-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { ethers } from "ethers";
import { task } from "hardhat/config";
import { Wallet } from "zksync-ethers";

task("check-factory-proxy-type", "Checks the factory proxy type and admin")
.addParam("factoryaddress", "address of the factory proxy")
.setAction(async (cmd, hre) => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { LOCAL_RICH_WALLETS, getProvider } = require("../test/utils");
let privateKey: string;

if (hre.network.name == "inMemoryNode" || hre.network.name == "dockerizedNode") {
console.log("Using local rich wallet");
privateKey = LOCAL_RICH_WALLETS[0].privateKey;
} else {
if (!process.env.WALLET_PRIVATE_KEY) throw "Wallet private key wasn't found in .env file!";
privateKey = process.env.WALLET_PRIVATE_KEY;
}

const wallet = new Wallet(privateKey, getProvider());
const provider = getProvider();

console.log("🔍 Analyzing factory proxy on network:", hre.network.name);
console.log("🎯 Factory address:", cmd.factoryaddress);
console.log("👤 Using wallet:", wallet.address);

try {
// TransparentUpgradeableProxy admin functions
const proxyAdminAbi = [
"function admin() external view returns (address)",
"function implementation() external view returns (address)",
"function changeAdmin(address newAdmin) external",
"function upgradeTo(address newImplementation) external",
"function upgradeToAndCall(address newImplementation, bytes calldata data) external payable",
];

// Factory implementation functions
const factoryAbi = [
"function beacon() view returns (address)",
];

// Check if we can read admin (this will fail if we're the admin)
console.log("\n📊 PROXY ANALYSIS:");

// Try different approaches to get proxy info
try {
// Method 1: Try to call admin() - this will fail if we are the admin
const proxyAsAdmin = new ethers.Contract(cmd.factoryaddress, proxyAdminAbi, provider);
const admin = await proxyAsAdmin.admin();
console.log("👤 Proxy admin:", admin);
console.log("🔍 Admin matches wallet:", admin.toLowerCase() === wallet.address.toLowerCase());
} catch (adminError) {
console.log("⚠️ Cannot read admin (likely because we ARE the admin)");
console.log(" Error:", adminError.message);
}

try {
// Method 2: Try to get implementation
const proxyAsAdmin = new ethers.Contract(cmd.factoryaddress, proxyAdminAbi, provider);
const impl = await proxyAsAdmin.implementation();
console.log("📋 Current implementation:", impl);
} catch (implError) {
console.log("⚠️ Cannot read implementation via admin interface");
console.log(" Error:", implError.message);
}

// Method 3: Use a different address to check admin
console.log("\n🔍 CHECKING PROXY WITH NON-ADMIN ADDRESS:");
const randomProvider = new ethers.JsonRpcProvider(provider.connection.url);
const proxyAsReader = new ethers.Contract(cmd.factoryaddress, proxyAdminAbi, randomProvider);

try {
const admin = await proxyAsReader.admin();
console.log("👤 Proxy admin:", admin);
console.log("🔍 Admin matches our wallet:", admin.toLowerCase() === wallet.address.toLowerCase());

const impl = await proxyAsReader.implementation();
console.log("📋 Current implementation:", impl);

// Now try to read the beacon from the implementation
const factoryImpl = new ethers.Contract(impl, factoryAbi, provider);
const currentBeacon = await factoryImpl.beacon();
console.log("📡 Current beacon address:", currentBeacon);
} catch (error) {
console.log("❌ Error reading proxy info:", error.message);
}

console.log("\n💡 CONCLUSIONS:");
console.log("✅ Factory is a TransparentUpgradeableProxy");
console.log("✅ Cannot change beacon address without deploying new implementation");
console.log("📝 OPTIONS TO CHANGE BEACON:");
console.log(" 1. Deploy new factory implementation with correct beacon");
console.log(" 2. Use proxy admin to upgrade to new implementation");
console.log(" 3. Keep current setup (both beacons synchronized)");
} catch (error) {
console.error("❌ Error during analysis:", error);
throw error;
}
});
Loading
Loading