@moonsong-labs/moonwall-cli
Version:
Testing framework for the Moon family of projects
118 lines (115 loc) • 4.36 kB
JavaScript
import {
extractError
} from "./chunk-IINDK553.js";
// src/internal/devModeHelpers.ts
import { customWeb3Request, alith, createAndFinalizeBlock } from "@moonsong-labs/moonwall-util";
import Debug from "debug";
import { setTimeout } from "timers/promises";
import { assert } from "vitest";
import chalk from "chalk";
var debug = Debug("DevTest");
async function devForkToFinalizedHead(context) {
const api = context.providers.find(({ type }) => type == "moon").api;
const finalizedHead = context.genesis;
await api.rpc.engine.createBlock(true, true, finalizedHead);
while (true) {
const newHead = (await api.rpc.chain.getFinalizedHead()).toString();
if (newHead == finalizedHead) {
await setTimeout(100);
} else {
context.genesis = newHead;
break;
}
}
}
async function createDevBlock(context, transactions, options = { allowFailures: true }) {
const results = [];
const api = context.getSubstrateApi();
const txs = transactions == void 0 ? [] : Array.isArray(transactions) ? transactions : [transactions];
for await (const call of txs) {
if (typeof call == "string") {
results.push({
type: "eth",
hash: (await customWeb3Request(context.web3(), "eth_sendRawTransaction", [call])).result
});
} else if (call.isSigned) {
const tx = api.tx(call);
debug(
`- Signed: ${tx.method.section}.${tx.method.method}(${tx.args.map((d) => d.toHuman()).join("; ")}) [ nonce: ${tx.nonce}]`
);
results.push({
type: "sub",
hash: (await call.send()).toString()
});
} else {
const tx = api.tx(call);
debug(
`- Unsigned: ${tx.method.section}.${tx.method.method}(${tx.args.map((d) => d.toHuman()).join("; ")}) [ nonce: ${tx.nonce}]`
);
results.push({
type: "sub",
hash: (await call.signAndSend(alith)).toString()
});
}
}
const { parentHash, finalize } = options;
const blockResult = await createAndFinalizeBlock(api, parentHash, finalize);
if (results.length == 0) {
return {
block: blockResult,
result: null
};
}
const allRecords = await (await api.at(blockResult.hash)).query.system.events();
const blockData = await api.rpc.chain.getBlock(blockResult.hash);
const result = results.map((result2) => {
const extrinsicIndex = result2.type == "eth" ? allRecords.find(
({ phase, event: { section, method, data } }) => phase.isApplyExtrinsic && section == "ethereum" && method == "Executed" && data[2].toString() == result2.hash
)?.phase?.asApplyExtrinsic?.toNumber() : blockData.block.extrinsics.findIndex((ext) => ext.hash.toHex() == result2.hash);
const events = allRecords.filter(
({ phase }) => phase.isApplyExtrinsic && phase.asApplyExtrinsic.toNumber() === extrinsicIndex
);
const failure = extractError(events);
return {
extrinsic: extrinsicIndex >= 0 ? blockData.block.extrinsics[extrinsicIndex] : null,
events,
error: failure && (failure.isModule && api.registry.findMetaError(failure.asModule) || { name: failure.toString() }),
successful: extrinsicIndex !== void 0 && !failure,
hash: result2.hash
};
});
if (results.find((r) => r.type == "eth")) {
await setTimeout(2);
}
const actualEvents = result.flatMap((resp) => resp.events);
if (options.expectEvents && options.expectEvents.length > 0) {
const match = options.expectEvents.every((eEvt) => {
const found = actualEvents.map((aEvt) => eEvt.is(aEvt.event)).reduce((acc, curr) => acc || curr, false);
if (!found) {
options.logger ? options.logger(
`Event ${chalk.bgWhiteBright.blackBright(eEvt.meta.name)} not present in block`
) : console.error(
`Event ${chalk.bgWhiteBright.blackBright(eEvt.meta.name)} not present in block`
);
}
return found;
});
assert(match, "Expected events not present in block");
}
if (!options.allowFailures) {
actualEvents.forEach((event) => {
assert(
!api.events.system.ExtrinsicFailed.is(event.event),
"ExtrinsicFailed event detected, enable 'allowFailures' if this is expected."
);
});
}
return {
block: blockResult,
result: Array.isArray(transactions) ? result : result[0]
};
}
export {
devForkToFinalizedHead,
createDevBlock
};