@kadena/hardhat-chainweb
Version:
Hardhat plugin for Kadena's Chainweb network
113 lines • 5.97 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.deployOnChainsUsingCreate2 = void 0;
exports.predictCreate2Address = predictCreate2Address;
const ethers_1 = require("ethers");
const hardhat_1 = __importDefault(require("hardhat"));
const utils_1 = require("../utils");
const pure_utils_1 = require("../pure-utils");
const deployCreate2Factory_1 = require("./deployCreate2Factory");
const networkStem = (0, pure_utils_1.getNetworkStem)(hardhat_1.default.config.defaultChainweb);
const { ethers } = hardhat_1.default;
function isContractDeployed(address) {
return ethers.provider.getCode(address).then((code) => code !== '0x');
}
function createSalt(sender, userSalt) {
const ADDRESS_LENGTH = 20;
const USER_SALT_LENGTH = 12;
// Convert the sender address and user salt to bytes
// The sender address is expected to be 20 bytes (Ethereum address)
const senderBytes = (0, ethers_1.getBytes)(sender);
// The user salt is expected to be 12 bytes (Kadena user salt)
const userSaltBytes = (0, ethers_1.getBytes)(ethers.dataSlice(ethers.id(userSalt), 0, USER_SALT_LENGTH));
if (senderBytes.length !== ADDRESS_LENGTH) {
throw new Error(`Sender address must be ${ADDRESS_LENGTH} bytes`);
}
if (userSaltBytes.length !== USER_SALT_LENGTH) {
throw new Error(`User salt must be ${USER_SALT_LENGTH} bytes`);
}
// Concatenate the sender address and user salt
const result = new Uint8Array(USER_SALT_LENGTH + ADDRESS_LENGTH);
result.set(senderBytes, 0);
result.set(userSaltBytes, ADDRESS_LENGTH);
return [result, ethers.toBigInt(userSaltBytes)];
}
async function predictCreate2Address(contractBytecode, signer, userSalt) {
const factoryAddress = await (0, deployCreate2Factory_1.getCreate2FactoryAddress)(signer);
const [salt] = createSalt(await signer.getAddress(), userSalt);
const predictedAddress = ethers.getCreate2Address(factoryAddress, salt, ethers.keccak256(contractBytecode));
return predictedAddress;
}
async function deployUsingCreate2(contractBytecode, signer, userSalt) {
const factoryAddress = await (0, deployCreate2Factory_1.getCreate2FactoryAddress)(signer);
const Factory = await hardhat_1.default.ethers.getContractFactory(deployCreate2Factory_1.create2Artifacts.abi, deployCreate2Factory_1.create2Artifacts.bin, signer);
const create2 = Factory.attach(factoryAddress);
const senderAddress = await signer.getAddress();
const [salt, userSaltBigInt] = createSalt(senderAddress, userSalt);
// Compute the predicted address
const predictedAddress = ethers.getCreate2Address(factoryAddress, salt, ethers.keccak256(contractBytecode));
const computedAddress = await create2.computeAddress(contractBytecode, userSaltBigInt);
if (computedAddress !== predictedAddress) {
console.log(`ADDRESS MISMATCH: computed address (${computedAddress}) != predicted address (${predictedAddress})`);
throw new Error(`ADDRESS MISMATCH: computed address (${computedAddress}) != predicted address (${predictedAddress})`);
}
if (await isContractDeployed(predictedAddress)) {
console.log(`Contract already deployed at ${predictedAddress}. Skipping deployment.`);
return predictedAddress;
}
// Deploy using CREATE2
const tx = await create2.deploy(contractBytecode, userSaltBigInt);
await tx.wait();
if (!(await isContractDeployed(predictedAddress))) {
console.log(`CREATE2 failed: No contract at predicted address ${predictedAddress}`);
throw new Error(`CREATE2 failed: No contract at predicted address ${predictedAddress}`);
}
return predictedAddress;
}
/**
* Deploy a contract on all chains in the network using create2.
*/
const deployOnChainsUsingCreate2 = async ({ name, signer, factoryOptions, constructorArgs = [], overrides, userSalt = 'KADENA/CREATE2/SALT', }) => {
const deployments = await (0, utils_1.runOverChains)(async (cwId) => {
var _a;
try {
const [defaultDeployer] = await ethers.getSigners();
const contractDeployer = (_a = signer !== null && signer !== void 0 ? signer : factoryOptions === null || factoryOptions === void 0 ? void 0 : factoryOptions.signer) !== null && _a !== void 0 ? _a : defaultDeployer;
const deployerAddress = await contractDeployer.getAddress();
console.log(`Deploying with signer: ${deployerAddress} on network ${cwId}`);
const factory = await ethers.getContractFactory(name, {
signer: contractDeployer,
...factoryOptions,
});
const transaction = await factory.getDeployTransaction(...(overrides ? [...constructorArgs, overrides] : constructorArgs));
// Prepare the bytecode of the contract to deploy
const bytecode = transaction.data;
const contractAddress = await deployUsingCreate2(bytecode, contractDeployer, userSalt);
const contract = factory.attach(contractAddress);
// Store deployment info in both formats
return {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
contract: contract,
address: contractAddress,
chain: cwId,
deployer: deployerAddress,
network: {
chainId: cwId,
name: `${networkStem}${cwId}`,
},
};
}
catch (error) {
console.error(`Failed to deploy to network ${cwId}:`, error);
return null;
}
});
return {
deployments: deployments.filter((d) => d !== null),
};
};
exports.deployOnChainsUsingCreate2 = deployOnChainsUsingCreate2;
//# sourceMappingURL=deployUsingCreate2.js.map