ChainSafe / web3-plugin-zksync

MIT License
4 stars 5 forks source link

Error: "The bytecode length in bytes must be divisible by 32!" when deploying contract with zkSync and Hardhat #59

Closed b-l-u-e closed 1 month ago

b-l-u-e commented 1 month ago

I'm encountering an issue when trying to deploy a smart contract using web3-plugin-zksync on zkSync along with Hardhat. The error message I receive states: Error: The bytecode length in bytes must be divisible by 32!

I've confirmed that the bytecode is correctly retrieved from Hardhat, but for some reason, it's not being deployed successfully due to this error.

Setup:

Web3 Plugin: web3-plugin-zksync
Blockchain Framework: Hardhat
Bytecode length: 946 bytes

Steps to Reproduce:

Compile a basic Solidity contract using Hardhat.
Use the following setup to deploy the contract via zkSync:

Code Example:

In Contracts.jsx (where I am deploying the contract from the frontend):

import axios from "axios";
import { useEffect, useState } from "react";
import Web3, { ContractAbi } from "web3";
import { ContractFactory } from "web3-plugin-zksync";

const web3 = new Web3("http://127.0.0.1:8545/");

export default function MyContracts() {
    const [contractAddress, setContractAddress] = useState("");
    const [contractAbi, setAbi] = useState<ContractAbi>([]);
    const [contractBytecode, setCode] = useState("");

    const getContractAddress = async () => {
        try {
            const response = await axios.get("http://localhost:4000/api/contractAddress");
            setContractAddress(response.data.address);
        } catch (e) {
            console.log(e);
        }
    };

    const getContractAbi = async () => {
        try {
            const response = await axios.get("http://localhost:4000/api/contractAbi");
            setAbi(response.data);
        } catch (e) {
            console.log(e);
        }
    };

    const getContractBytecode = async () => {
        try {
            const response = await axios.get("http://localhost:4000/api/contractBytecode");
            const bytecode = response.data.bytecode;
            console.log("Received bytecode:", bytecode); // Debugging: Log bytecode

            if (bytecode.length % 32 !== 0) {
                console.warn("Bytecode length is not divisible by 32. Padding with zeros.");
                const paddingLength = 32 - (bytecode.length % 32);
                const paddedBytecode = bytecode.padEnd(bytecode.length + paddingLength, '0');
                setCode(paddedBytecode);
            } else {
                setCode(bytecode);
            }
        } catch (e) {
            console.log(e);
        }
    };

    useEffect(() => {
        getContractAbi();
        getContractAddress();
        getContractBytecode();
    }, []);

    const deployContract = async () => {
        try {
            const accounts = await web3.eth.getAccounts();
            const defaultAccount = accounts[0];

            const contractFactory = new ContractFactory(contractAbi, contractBytecode, web3);

            const contract = await contractFactory.deploy({
                from: defaultAccount,
                gas: 1500000,
                gasPrice: '30000000000000'
            });

            console.log("Contract deployed at:", contract.options.address);
            setContractAddress(contract.options.address); // Update contract address state
        } catch (e) {
            console.error("Error deploying contract:", e);
        }
    };

    const testContract = async () => {
        try {
            const accounts = await web3.eth.getAccounts();
            const defaultAccount = accounts[0];

            const myContract = new web3.eth.Contract(contractAbi, contractAddress);

            const num = await myContract.methods.myNumber().call();
            console.log('Original number:', num);

            const receipt = await myContract.methods
                .setMyNumber(BigInt(num) + 1n)
                .send({
                    from: defaultAccount,
                    gas: 1000000,
                    gasPrice: "10000000000",
                });

            console.log(receipt);
            console.log("Transaction Hash:", receipt.transactionHash);

            // Get the updated value of my number
            const myNumberUpdated = await myContract.methods.myNumber().call();
            console.log("myNumber updated value:", myNumberUpdated);
        } catch (e) {
            console.log(e);
        }
    };

    return (
        <main className="p-2.5 space-y-2.5">
            <div className="flex items-center gap-5">
                <Typography variant="h4" component="h1" gutterBottom>
                    <strong>My Contracts</strong>
                </Typography>
                <Button
                    variant="contained"
                    color="primary"
                    onClick={deployContract}
                    sx={{ p: 1.5 }}
                >
                    Deploy new Contract
                </Button>
            </div>

            <Typography variant="h5" component="div">
                Contract deployed at address: <u><strong>{contractAddress}</strong></u>
                <Button
                    variant="outlined"
                    color="primary"
                    onClick={testContract}
                    sx={{ p: 1 }}
                    disabled={!contractAddress} // Disable button if no contract address
                >
                    Test Contract
                </Button>
            </Typography>
        </main>
    );
}

In server.js:


import dotenv from "dotenv"
import path from "path"
import { readFileSync } from "fs";
import cors from "cors"
import { fileURLToPath } from "url";

dotenv.config();

const app = express();
app.use(express.json());
app.use(cors({
    origin: "*"
}));

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

app.get("/api/contractAbi", (req, res) => {
    const abiPath = path.resolve(__dirname, 'MyContractAbi.json');
    const abi = JSON.parse(readFileSync(abiPath, 'utf8'));
    res.json(abi);
});

app.get('/api/contractBytecode', (req, res) => {
    try {
        const artifactPath = path.resolve(__dirname, 'artifacts/contracts/MyContract.sol/MyContract.json');
        const artifact = JSON.parse(readFileSync(artifactPath, 'utf8'));
        const bytecode = artifact.bytecode;

        res.json({ bytecode });
    } catch (error) {
        console.error('[server] Error reading bytecode:', error);
        res.status(500).json({ error: 'Error reading contract bytecode' });
    }
});

app.get('/api/contractAddress', (req, res) => {
    const addressPath = path.resolve(__dirname, 'MyContractAddress.txt');
    const address = readFileSync(addressPath, 'utf8');
    res.json({ address });
});

// Start the server
app.listen(process.env.PORT, () => {
    console.log(`[server] server running on http://localhost:${process.env.PORT}`);
}); ```
Redoudou commented 1 month ago

@jdevcs @Muhammad-Altabba can you help ? This is for the EthSafari Hackathon

Muhammad-Altabba commented 1 month ago

Hi @b-l-u-e, Thanks for the issue. Please refer to the documentation for the correct way to call the methods. I noticed that you are passing web to the ContractFactory instead of an instance of ZKsyncWallet. There might be another issue with the code as well. But I could notice this one fast. I hope it will fix your issue. And if not, try checking the other code accordingly to the documentation.

Please check: https://chainsafe.github.io/web3-plugin-zksync/classes/ContractFactory.html

And in both cases, we will try to improve the docs and the types for such a case.

b-l-u-e commented 1 month ago

Thank you.. let me work on it and get back to you

Muhammad-Altabba commented 1 month ago

Hi @b-l-u-e , Could you please close this issue if it was resolved. And you can open a new issue if you are facing some other error or so. All the best,

b-l-u-e commented 1 month ago

Hello, Thank you for your prompt response. Closed the issue.With regards,Winnie Fredrick.On 12 Sep 2024, at 09:45, Muhammad Altabba @.***> wrote: Hi @b-l-u-e , Could you please close this issue if it was resolved. And you can open a new issue if you are facing some other error or so. All the best,

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: @.***>