@moonwall/cli
Version:
Testing framework for the Moon family of projects
171 lines • 6.48 kB
JavaScript
import { ALITH_PRIVATE_KEY, PRECOMPILES, createEthersTransaction, createViemTransaction, deployViemContract, } from "@moonwall/util";
import chalk from "chalk";
import { Interface, Wallet } from "ethers";
import fs, { readFileSync } from "node:fs";
import path from "node:path";
import { decodeFunctionResult, encodeFunctionData, toHex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { importJsonConfig } from "./configReader";
function getCompiledPath(contractName) {
const config = importJsonConfig();
const contractsDir = config.environments.find((env) => env.name === process.env.MOON_TEST_ENV)?.contracts;
if (!contractsDir) {
throw new Error(`Contracts directory not found for environment config ${process.env.MOON_TEST_ENV}\n` +
`Please specify path to Foundry directory at: ${chalk.bgWhiteBright.blackBright("moonwall.config.json > environments > contracts")}`);
}
const compiledJsonPath = recursiveSearch(contractsDir, `${contractName}.json`);
const solidityFilePath = recursiveSearch(contractsDir, `${contractName}.sol`);
if (!compiledJsonPath && !solidityFilePath) {
throw new Error(`Neither solidity contract ${contractName}.sol nor its compiled json exists in ${contractsDir}`);
}
if (!compiledJsonPath) {
throw new Error(`Compiled contract ${contractName}.json doesn't exist\n` +
`Please ${chalk.bgWhiteBright.blackBright("recompile contract")} ${contractName}.sol`);
}
return compiledJsonPath;
}
export function fetchCompiledContract(contractName) {
const compiledPath = getCompiledPath(contractName);
const json = readFileSync(compiledPath, "utf8");
const parsed = JSON.parse(json);
return {
abi: parsed.contract.abi,
bytecode: parsed.byteCode,
methods: parsed.contract.evm.methodIdentifiers,
deployedBytecode: `0x${parsed.contract.evm.deployedBytecode.object}`,
};
}
export function recursiveSearch(dir, filename) {
const files = fs.readdirSync(dir);
for (const file of files) {
const filepath = path.join(dir, file);
const stats = fs.statSync(filepath);
if (stats.isDirectory()) {
const searchResult = recursiveSearch(filepath, filename);
if (searchResult) {
return searchResult;
}
}
else if (stats.isFile() && file === filename) {
return filepath;
}
}
return null;
}
export async function interactWithPrecompileContract(context, callOptions) {
const { precompileName, ...rest } = callOptions;
const precompileInfo = PRECOMPILES[precompileName];
if (!precompileInfo) {
throw new Error(`No precompile found with the name: ${precompileName}`);
}
const [contractAddress, contractName] = Array.isArray(precompileInfo)
? precompileInfo
: [precompileInfo, precompileName];
return await interactWithContract(context, {
...rest,
contractName,
contractAddress: contractAddress,
});
}
export async function interactWithContract(context, callOptions) {
const { contractName, contractAddress, functionName, args = [], web3Library = "viem", gas = "estimate", value = 0n, privateKey = ALITH_PRIVATE_KEY, rawTxOnly = false, call = false, } = callOptions;
const { abi } = fetchCompiledContract(contractName);
const data = encodeFunctionData({
abi,
functionName,
args,
});
const account = privateKeyToAccount(privateKey);
const gasParam = gas === "estimate"
? await context.viem().estimateGas({
account: account.address,
to: contractAddress,
value: 0n,
data,
})
: gas > 0n
? gas
: 200000n;
if (!call && rawTxOnly) {
return web3Library === "viem"
? createViemTransaction(context, {
to: contractAddress,
data,
gas: gasParam,
privateKey,
value,
})
: createEthersTransaction(context, {
to: contractAddress,
data,
gas: gasParam,
value: toHex(value),
privateKey,
});
}
if (call) {
if (web3Library === "viem") {
const result = await context.viem().call({
account: account.address,
to: contractAddress,
value: 0n,
data,
gas: gasParam,
});
if (!result.data) {
throw new Error("No data field returned from call");
}
return decodeFunctionResult({ abi, functionName, data: result.data });
}
const result = await context.ethers().call({
from: account.address,
to: contractAddress,
value: toHex(value),
data,
gasLimit: toHex(gasParam),
});
return new Interface(abi).decodeFunctionResult(functionName, result);
}
if (!rawTxOnly) {
if (web3Library === "viem") {
const hash = await context.viem().sendTransaction({
account: account,
to: contractAddress,
value,
data,
gas: gasParam,
});
return hash;
}
const signer = new Wallet(privateKey, context.ethers().provider);
const { hash } = await signer.sendTransaction({
from: account.address,
to: contractAddress,
value: toHex(value),
data,
gasLimit: toHex(gasParam),
});
return hash;
}
throw new Error("This should never happen, if it does there's a logic error in the code");
}
export async function deployCreateCompiledContract(context, contractName, options) {
const { abi, bytecode, methods } = fetchCompiledContract(contractName);
const { privateKey = ALITH_PRIVATE_KEY, args = [], ...rest } = options || {};
const blob = {
...rest,
privateKey,
args,
};
const { contractAddress, logs, status, hash } = await deployViemContract(context, abi, bytecode, blob);
return {
contractAddress: contractAddress,
logs,
hash,
status,
abi,
bytecode,
methods,
};
}
//# sourceMappingURL=contractFunctions.js.map