UNPKG

@moonwall/util

Version:

Testing framework for the Moon family of projects

872 lines (857 loc) 29.7 kB
// src/functions/block.ts import Bottleneck from "bottleneck"; // src/functions/logger.ts import { regex } from "arkregex"; import pino from "pino"; import pinoPretty from "pino-pretty"; var globWildcardRegex = regex("\\*", "g"); var logLevel = process.env.LOG_LEVEL || "info"; var prettyStream = pinoPretty({ colorize: true, translateTime: "HH:MM:ss.l", ignore: "pid,hostname", sync: true // Important for worker threads }); var pinoOptions = { level: logLevel, formatters: { level: (label) => { return { level: label }; } } }; var loggers = /* @__PURE__ */ new Map(); function createLogger(options) { const { name, level = logLevel, enabled = true } = options; const existingLogger = loggers.get(name); if (existingLogger) { return existingLogger; } const loggerConfig = { name, level, enabled, formatters: pinoOptions.formatters }; const logger2 = pino(loggerConfig, prettyStream); loggers.set(name, logger2); return logger2; } function getLogger(name) { return loggers.get(name); } function clearLoggers() { loggers.clear(); } function setLoggerEnabled(pattern, enabled) { const patternRegex = new RegExp(pattern.replace(globWildcardRegex, ".*")); loggers.forEach((logger2, name) => { if (patternRegex.test(name)) { logger2.level = enabled ? logLevel : "silent"; } }); } function setupLogger(name) { const logger2 = createLogger({ name: `test:${name}`, enabled: process.argv.includes("--printlogs") }); return logger2; } // src/functions/block.ts var logger = createLogger({ name: "test:blocks" }); var debug = logger.debug.bind(logger); async function createAndFinalizeBlock(api, parentHash, finalize = false) { const startTime = Date.now(); const block = parentHash ? await api.rpc("engine_createBlock", true, finalize, parentHash) : await api.rpc("engine_createBlock", true, finalize); return { duration: Date.now() - startTime, hash: block.hash, // toString doesn't work for block hashes proofSize: block.proof_size // TODO: casting can be removed once api-augment is updated }; } function calculateFeePortions(amount) { const burnt = amount * 80n / 100n; return { burnt, treasury: amount - burnt }; } var getBlockExtrinsic = async (api, blockHash, section, method) => { const apiAt = await api.at(blockHash); const [{ block }, records] = await Promise.all([ api.rpc.chain.getBlock(blockHash), apiAt.query.system.events() ]); const extIndex = block.extrinsics.findIndex( (ext) => ext.method.section === section && ext.method.method === method ); const extrinsic = extIndex > -1 ? block.extrinsics[extIndex] : null; const events = records.filter(({ phase }) => phase.isApplyExtrinsic && phase.asApplyExtrinsic.eq(extIndex)).map(({ event }) => event); const resultEvent = events.find( (event) => event.section === "system" && (event.method === "ExtrinsicSuccess" || event.method === "ExtrinsicFailed") ); return { block, extrinsic, events, resultEvent }; }; var getBlockTime = (signedBlock) => signedBlock.block.extrinsics.find((item) => item.method.section === "timestamp").method.args[0].toNumber(); var checkBlockFinalized = async (api, number) => { return { number, //@ts-expect-error - remove once pJs exposes this finalized: await api._rpcCore.provider.send("moon_isBlockFinalized", [ await api.rpc.chain.getBlockHash(number) ]) }; }; var fetchBlockTime = async (api, blockNum) => { const hash = await api.rpc.chain.getBlockHash(blockNum); const block = await api.rpc.chain.getBlock(hash); return getBlockTime(block); }; var fetchHistoricBlockNum = async (api, blockNumber, targetTime) => { if (blockNumber <= 1) { return 1; } const time = await fetchBlockTime(api, blockNumber); if (time <= targetTime) { return blockNumber; } return fetchHistoricBlockNum( api, blockNumber - Math.ceil((time - targetTime) / 3e4), targetTime ); }; var getBlockArray = async (api, timePeriod, bottleneck) => { let limiter = bottleneck; if (!limiter) { limiter = new Bottleneck({ maxConcurrent: 10, minTime: 100 }); } const finalizedHead = await limiter.schedule(() => api.rpc.chain.getFinalizedHead()); const signedBlock = await limiter.schedule( () => api.rpc.chain.getBlock(finalizedHead) ); const lastBlockNumber = signedBlock.block.header.number.toNumber(); const lastBlockTime = getBlockTime(signedBlock); const firstBlockTime = lastBlockTime - timePeriod; debug(`Searching for the block at: ${new Date(firstBlockTime)}`); const firstBlockNumber = await limiter.wrap(fetchHistoricBlockNum)( api, lastBlockNumber, firstBlockTime ); const length = lastBlockNumber - firstBlockNumber; return Array.from({ length }, (_, i) => firstBlockNumber + i); }; function extractWeight(weightV1OrV2) { if ("isSome" in weightV1OrV2) { const weight = weightV1OrV2.unwrap(); if ("refTime" in weight) { return weight.refTime.unwrap(); } return weight; } if ("refTime" in weightV1OrV2) { return weightV1OrV2.refTime.unwrap(); } return weightV1OrV2; } function extractPreimageDeposit(request) { const deposit = "deposit" in request ? request.deposit : request; if ("isSome" in deposit && deposit.isSome) { return { accountId: deposit.unwrap()[0].toHex(), amount: deposit.unwrap()[1] }; } if ("isNone" in deposit && deposit.isNone) { return void 0; } return { accountId: deposit[0].toHex(), amount: deposit[1] }; } function mapExtrinsics(extrinsics, records, fees) { return extrinsics.map((extrinsic, index) => { let dispatchError; let dispatchInfo; const events = records.filter(({ phase }) => phase.isApplyExtrinsic && phase.asApplyExtrinsic.eq(index)).map(({ event }) => { if (event.section === "system") { if (event.method === "ExtrinsicSuccess") { dispatchInfo = event.data[0]; } else if (event.method === "ExtrinsicFailed") { dispatchError = event.data[0]; dispatchInfo = event.data[1]; } } return event; }); return { dispatchError, dispatchInfo, events, extrinsic, fee: fees ? fees[index] : void 0 }; }); } async function checkTimeSliceForUpgrades(api, blockNumbers, currentVersion) { const apiAt = await api.at(await api.rpc.chain.getBlockHash(blockNumbers[0])); const onChainRt = (await apiAt.query.system.lastRuntimeUpgrade()).unwrap().specVersion; return { result: !onChainRt.eq(currentVersion), specVersion: onChainRt }; } // src/functions/common.ts import { BN } from "@polkadot/util"; import { regex as regex2 } from "arkregex"; var wsProtocolRegex = regex2("^ws(s)?:"); function normalizeUrlToHttps(url) { return url.replace(wsProtocolRegex, "http$1:"); } function sortObjectByKeys(obj) { const sortedKeys = Object.keys(obj).sort(); const sortedObj = {}; for (const key of sortedKeys) { sortedObj[key] = obj[key]; } return sortedObj; } var Perthing = class { unit; perthing; constructor(unit, num, denom) { let numerator = num; let denominator = denom; if (!(numerator instanceof BN)) { numerator = new BN(numerator.toString()); } if (denominator && !(denominator instanceof BN)) { denominator = new BN(denominator.toString()); } this.unit = unit; if (denominator) { this.perthing = numerator.mul(unit).div(denominator); } else { this.perthing = numerator; } } value() { return this.perthing; } of(value) { return this.divNearest(this.perthing.mul(value), this.unit); } ofCeil(value) { return this.divCeil(this.perthing.mul(value), this.unit); } toString() { return `${this.perthing.toString()}`; } divCeil(a, num) { const dm = a.divmod(num); if (dm.mod.isZero()) return dm.div; return dm.div.negative !== 0 ? dm.div.isubn(1) : dm.div.iaddn(1); } divNearest(a, num) { const dm = a.divmod(num); if (dm.mod.isZero()) return dm.div; const mod = dm.div.negative !== 0 ? dm.mod.isub(num) : dm.mod; const half = num.ushrn(1); const r2 = num.andln(1); const cmp = mod.cmp(half); if (cmp <= 0 || r2 === new BN(1) && cmp === 0) return dm.div; return dm.div.negative !== 0 ? dm.div.isubn(1) : dm.div.iaddn(1); } }; var Perbill = class extends Perthing { constructor(numerator, denominator) { super(new BN(1e9), numerator, denominator); } }; var Percent = class extends Perthing { constructor(numerator, denominator) { super(new BN(100), numerator, denominator); } }; function getObjectMethods(obj) { const properties = /* @__PURE__ */ new Set(); let currentObj = obj; while (currentObj) { for (const item of Object.getOwnPropertyNames(currentObj)) { properties.add(item); } currentObj = Object.getPrototypeOf(currentObj); } return Array.from(properties).filter((item) => typeof obj[item] === "function"); } async function directRpcRequest(endpoint, method, params = [], timeoutMs = 1e4) { const data = { jsonrpc: "2.0", id: 1, method, params }; if (endpoint.startsWith("ws")) { console.log("you've passed a websocket to fetch, is this intended?"); } const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeoutMs); try { const response = await fetch(endpoint, { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json" }, signal: controller.signal }); clearTimeout(timeoutId); const responseData = await response.json(); if (responseData.error) { throw new Error(responseData.error.message); } return responseData.result; } catch (error) { clearTimeout(timeoutId); if (error.name === "AbortError") { throw new Error( `RPC request to ${endpoint} timed out after ${timeoutMs}ms (method: ${method})` ); } console.error( `[directRpcRequest] Error for ${endpoint} method ${method}:`, error.message, error.cause ); throw new Error(`RPC request to ${endpoint} failed (method: ${method}): ${error.message}`); } } // src/functions/contextHelpers.ts function filterAndApply(events, section, methods, onFound) { return events.filter(({ event }) => section === event.section && methods.includes(event.method)).map((record) => onFound(record)); } function getDispatchError({ event: { data: [dispatchError] } }) { return dispatchError; } function getDispatchInfo({ event: { data, method } }) { return method === "ExtrinsicSuccess" ? data[0] : data[1]; } function extractError(events = []) { return filterAndApply(events, "system", ["ExtrinsicFailed"], getDispatchError)[0]; } function isExtrinsicSuccessful(events = []) { return filterAndApply(events, "system", ["ExtrinsicSuccess"], () => true).length > 0; } function extractInfo(events = []) { return filterAndApply( events, "system", ["ExtrinsicFailed", "ExtrinsicSuccess"], getDispatchInfo )[0]; } function extractFee(events = []) { return filterAndApply( events, "balances", ["Withdraw"], ({ event }) => event.data )[0]; } // src/functions/contracts.ts import fs from "fs"; import path from "path"; function getAllCompiledContracts(contractsDir = "./", recurse = false) { const contractsPath = path.isAbsolute(contractsDir) ? contractsDir : path.join(process.cwd(), contractsDir); const contracts = fs.readdirSync(contractsPath, { withFileTypes: true }); let contractNames = []; for (const dirent of contracts) { const fullDirentPath = path.join(contractsPath, dirent.name); if (dirent.isDirectory() && recurse) { contractNames = contractNames.concat(getAllCompiledContracts(fullDirentPath, recurse)); } else if (dirent.isFile() && path.extname(dirent.name) === ".json") { contractNames.push(path.basename(dirent.name, ".json")); } } return contractNames; } function getCompiled(contractPath) { const filePath = path.join(process.cwd(), `${contractPath}.json`); if (!fs.existsSync(filePath)) { throw new Error(`Contract name (${contractPath}) doesn't exist in test suite`); } try { const json = fs.readFileSync(filePath, "utf8"); return JSON.parse(json); } catch (_error) { throw new Error( `Contract name ${contractPath} is not compiled. Please check compiled json exists` ); } } // src/functions/ethers.ts import { Wallet } from "ethers"; // src/constants/accounts.ts import { Keyring } from "@polkadot/api"; // src/constants/chain.ts var DEBUG_MODE = process.env.DEBUG_MODE || false; var DISPLAY_LOG = process.env.MOONBEAM_LOG || false; var MOONBEAM_LOG = process.env.MOONBEAM_LOG || "info"; var BASE_PATH = process.env.BASE_PATH; var CUSTOM_SPEC_PATH = process.env.CUSTOM_SPEC_PATH; var BINARY_PATH = process.env.BINARY_PATH || "../target/release/moonbeam"; var RELAY_BINARY_PATH = process.env.RELAY_BINARY_PATH || "../target/release/polkadot"; var RELAY_LOG = process.env.RELAY_LOG; var OVERRIDE_RUNTIME_PATH = process.env.OVERRIDE_RUNTIME_PATH || void 0; var ETHAPI_CMD = process.env.ETHAPI_CMD || ""; var WASM_RUNTIME_OVERRIDES = process.env.WASM_RUNTIME_OVERRIDES || ""; var GLMR = 1000000000000000000n; var DEFAULT_GENESIS_BALANCE = 2n ** 80n; var DEFAULT_GENESIS_STAKING = 1000n * GLMR; var DEFAULT_GENESIS_MAPPING = 100n * GLMR; var PROPOSAL_AMOUNT = 1000n * GLMR; var VOTE_AMOUNT = 10n * GLMR; var MIN_GLMR_STAKING = 1000n * GLMR; var MIN_GLMR_DELEGATOR = 1n * GLMR; var WEIGHT_PER_SECOND = 1000000000000n; var GAS_PER_SECOND = 40000000n; var GAS_PER_WEIGHT = WEIGHT_PER_SECOND / GAS_PER_SECOND; var GAS_PER_POV_BYTES = 4n; var BLOCK_WEIGHT_LIMIT = WEIGHT_PER_SECOND / 2n; var BLOCK_GAS_LIMIT = BLOCK_WEIGHT_LIMIT / GAS_PER_WEIGHT; var EXTRINSIC_GAS_LIMIT = BLOCK_GAS_LIMIT * 3n / 4n - BLOCK_GAS_LIMIT / 10n; var MAX_BLOCK_DEV_POV = 4 * 1024 * 1024 + 512; var MAX_ETH_POV_PER_TX = EXTRINSIC_GAS_LIMIT / GAS_PER_POV_BYTES; var EXTRINSIC_BASE_WEIGHT = 25e7; var WEIGHT_PER_GAS = 1000000000000n / 40000000n; var MIN_GAS_PRICE = 10000000000n; var MINUTES = 60 / 12; var HOURS = MINUTES * 60; var DAYS = HOURS * 24; var TWO_MINS = 2 * 60 * 1e3; var FIVE_MINS = 5 * 60 * 1e3; var TEN_MINS = 10 * 60 * 1e3; var THIRTY_MINS = 30 * 60 * 1e3; var ONE_HOURS = 60 * 60 * 1e3; var TWO_HOURS = 2 * 60 * 60 * 1e3; var THREE_HOURS = 3 * 60 * 60 * 1e3; var FOUR_HOURS = 4 * 60 * 60 * 1e3; var MAX_BLOCK_WEIGHT = 5e11; var TARGET_FILL_AMOUNT = (MAX_BLOCK_WEIGHT * 0.75 * 0.25 - EXTRINSIC_BASE_WEIGHT) / MAX_BLOCK_WEIGHT * 1e9; var TARGET_FILL_PERMILL = 0.25 * 1e6; var WEIGHT_FEE = 50n * 1000n; // src/constants/accounts.ts var keyringEth = new Keyring({ type: "ethereum" }); var keyringEd25519 = new Keyring({ type: "ed25519" }); var keyringSr25519 = new Keyring({ type: "sr25519" }); var ALITH_ADDRESS = "0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"; var ALITH_PRIVATE_KEY = "0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133"; var BALTATHAR_PRIVATE_KEY = "0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b"; var CHARLETH_PRIVATE_KEY = "0x0b6e18cafb6ed99687ec547bd28139cafdd2bffe70e6b688025de6b445aa5c5b"; var DOROTHY_PRIVATE_KEY = "0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68"; var ETHAN_PRIVATE_KEY = "0x7dce9bc8babb68fec1409be38c8e1a52650206a7ed90ff956ae8a6d15eeaaef4"; var FAITH_PRIVATE_KEY = "0xb9d2ea9a615f3165812e8d44de0d24da9bbd164b65c4f0573e1ce2c8dbd9c8df"; var GOLIATH_PRIVATE_KEY = "0x96b8a38e12e1a31dee1eab2fffdf9d9990045f5b37e44d8cc27766ef294acf18"; var GERALD_PRIVATE_KEY = "0x99B3C12287537E38C90A9219D4CB074A89A16E9CDB20BF85728EBD97C343E342"; var ALITH_GENESIS_FREE_BALANCE = DEFAULT_GENESIS_BALANCE - DEFAULT_GENESIS_MAPPING; var ALITH_GENESIS_LOCK_BALANCE = DEFAULT_GENESIS_STAKING; var ALITH_GENESIS_TRANSFERABLE_BALANCE = ALITH_GENESIS_FREE_BALANCE - ALITH_GENESIS_LOCK_BALANCE; var alith = keyringEth.addFromUri(ALITH_PRIVATE_KEY); var baltathar = keyringEth.addFromUri(BALTATHAR_PRIVATE_KEY); var charleth = keyringEth.addFromUri(CHARLETH_PRIVATE_KEY); var dorothy = keyringEth.addFromUri(DOROTHY_PRIVATE_KEY); var ethan = keyringEth.addFromUri(ETHAN_PRIVATE_KEY); var faith = keyringEth.addFromUri(FAITH_PRIVATE_KEY); var goliath = keyringEth.addFromUri(GOLIATH_PRIVATE_KEY); var gerald = keyringEth.addFromUri(GERALD_PRIVATE_KEY); // src/functions/ethers.ts var transactionHandlers = { legacy: (blob, params) => { blob.gasPrice = params.gasPrice || "10000000000"; blob.gasLimit = params.gasLimit || "200000"; blob.type = 0; }, eip2930: (blob, params) => { blob.gasPrice = params.gasPrice || "10000000000"; blob.gasLimit = params.gasLimit || "200000"; blob.accessList = params.accessList || []; blob.type = 1; }, eip1559: (blob, params) => { blob.accessList = params.accessList || []; blob.maxFeePerGas = params.maxFeePerGas || "10000000000"; blob.maxPriorityFeePerGas = params.maxPriorityFeePerGas || 0; blob.gasLimit = params.gasLimit || "200000"; blob.type = 2; } }; async function createEthersTransaction(context, params) { const nonce = "nonce" in params ? params.nonce : await context.viem().getTransactionCount({ address: ALITH_ADDRESS }); const blob = { nonce, ...params }; const handler = transactionHandlers[params.txnType || "legacy"]; if (!handler) { throw new Error("Unknown transaction type, update createRawEthersTxn fn"); } handler(blob, params); const signer = params.privateKey ? new Wallet(params.privateKey, context.ethers().provider) : context.ethers(); const txn = await signer.populateTransaction(blob); return await signer.signTransaction(txn); } // src/functions/extrinsics.ts var signAndSend = async (tx, account = alith, nonce = -1) => new Promise( (resolve) => tx.signAndSend(account, { nonce }, ({ status }) => { if (status.isInBlock) { process.stdout.write( "Extrinsic submitted and included in block, waiting for finalization..." ); } if (status.isFinalized) { process.stdout.write("\u2705\n"); resolve(true); } }) ); // src/functions/jumping.ts import WebSocket from "ws"; async function jumpBlocksDev(polkadotJsApi, blocks) { let blockCount = blocks; while (blockCount > 0) { await polkadotJsApi.rpc.engine.createBlock(true, true); blockCount--; } } async function jumpRoundsDev(polkadotJsApi, count) { const round = (await polkadotJsApi.query.parachainStaking.round()).current.addn(count.valueOf()).toNumber(); return jumpToRoundDev(polkadotJsApi, round); } async function jumpToRoundDev(polkadotJsApi, round) { let lastBlockHash = ""; for (; ; ) { const currentRound = (await polkadotJsApi.query.parachainStaking.round()).current.toNumber(); if (currentRound === round) { return lastBlockHash; } if (currentRound > round) { return null; } lastBlockHash = (await polkadotJsApi.rpc.engine.createBlock(true, true)).blockHash.toString(); } } async function calculateBlocks(polkadotJsApi, targetRound) { const roundInfo = await polkadotJsApi.query.parachainStaking.round(); if (roundInfo.current.toNumber() >= targetRound) { return 0; } const roundsToJump = targetRound - roundInfo.current.toNumber(); const heightToJump = roundInfo.first.toNumber() + roundsToJump * roundInfo.length.toNumber(); const currentBlock = (await polkadotJsApi.rpc.chain.getHeader()).number.toNumber(); return heightToJump - currentBlock; } async function jumpRoundsChopsticks(polkadotJsApi, port, count) { const round = (await polkadotJsApi.query.parachainStaking.round()).current.addn(count.valueOf()).toNumber(); return jumpToRoundChopsticks(polkadotJsApi, port, round); } async function jumpToRoundChopsticks(polkadotJsApi, port, round) { const blockToJump = await calculateBlocks(polkadotJsApi, round); return jumpBlocksChopsticks(port, blockToJump); } async function jumpBlocksChopsticks(port, blockCount) { return await sendNewBlockCmd(port, blockCount); } var sendNewBlockCmd = async (port, count = 1) => { const websocketUrl = `ws://127.0.0.1:${port}`; const socket = new WebSocket(websocketUrl); const result = await new Promise((resolve) => { socket.on("open", () => { socket.send( JSON.stringify({ jsonrpc: "2.0", id: 1, method: "dev_newBlock", params: [{ count }] }) ); }); socket.on("message", (chunk) => { const data = JSON.parse(chunk.toString()); resolve(data.result); socket.close(); }); }); return result; }; // src/functions/logging.ts var setupLogger2 = setupLogger; function log(...msg) { if (process.argv?.[2] && process.argv[2] === "--printlogs") { console.log(...msg); } } var printTokens = (api, tokens, decimals = 2, pad = 9) => { if (!api.registry.chainDecimals[0]) { throw new Error("Chain decimals not found for system token"); } return `${(Math.ceil(Number(tokens / 10n ** BigInt(api.registry.chainDecimals[0] - decimals))) / 10 ** decimals).toString().padStart(pad)} ${api.registry.chainTokens[0]}`; }; var printEvents = async (api, hash) => { const blockHash = hash || (await api.rpc.chain.getBlockHash()).toString(); const apiAt = await api.at(blockHash); const { block } = await api.rpc.chain.getBlock(blockHash); const allRecords = await apiAt.query.system.events(); const txsWithEvents = mapExtrinsics(block.extrinsics, allRecords); console.log(`===== Block #${block.header.number.toString()}: ${blockHash}`); console.log(block.header.toHuman()); console.log( txsWithEvents.map( ({ extrinsic, events }, i) => ` [${i}]: ${extrinsic.method.section.toString()}. ${extrinsic.method.method.toString()} - 0x${Buffer.from(extrinsic.data).toString("hex")} ${events.map( (event) => ` * ${event.section.toString()}.${event.method.toString()}: ${event.data.map((datum) => ` - ${datum.toHex()}`).join("\n")}` ).join("\n")}` ).join("\n") ); }; // src/functions/providers.ts async function customWeb3Request(web3, method, params) { return new Promise((resolve, reject) => { web3.eth.currentProvider.send( { jsonrpc: "2.0", id: 1, method, params }, (error, result) => { if (error) { reject( `Failed to send custom request (${method} (${params.map((p) => { const str = p.toString(); return str.length > 128 ? `${str.slice(0, 96)}...${str.slice(-28)}` : str; }).join(",")})): ${error.message || error.toString()}` ); } resolve(result); } ); }); } async function web3EthCall(web3, options) { return await customWeb3Request(web3, "eth_call", [ { from: options.from === void 0 ? options.from : alith.address, value: options.value, gas: options.gas === void 0 ? options.gas : 256e3, gasPrice: options.gas === void 0 ? options.gas : `0x${MIN_GAS_PRICE}`, to: options.to, data: options.data } ]); } // src/functions/viem.ts import { createWalletClient, hexToNumber, http } from "viem"; import { setTimeout as timer } from "timers/promises"; import { privateKeyToAccount } from "viem/accounts"; async function getDevChain(url) { const httpUrl = normalizeUrlToHttps(url); const block = { http: [httpUrl] }; return { id: 1281, name: "Moonbeam Dev", nativeCurrency: { decimals: 18, name: "Glimmer", symbol: "GLMR" }, rpcUrls: { public: block, default: block } }; } async function deriveViemChain(endpoint, maxRetries = 3) { const httpEndpoint = normalizeUrlToHttps(endpoint); const block = { http: [httpEndpoint] }; let lastError; for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const id = hexToNumber(await directRpcRequest(httpEndpoint, "eth_chainId", [])); const name = await directRpcRequest(httpEndpoint, "system_chain", []); const { tokenSymbol, tokenDecimals } = await directRpcRequest( httpEndpoint, "system_properties", [] ); return { id, name, nativeCurrency: { decimals: tokenDecimals, name: tokenSymbol, symbol: tokenSymbol }, rpcUrls: { public: block, default: block } }; } catch (error) { lastError = error; if (attempt < maxRetries) { console.warn( `Failed to derive viem chain on attempt ${attempt}/${maxRetries} from ${httpEndpoint}: ${error.message}. Retrying...` ); await timer(1e3 * attempt); } } } throw new Error( `Failed to derive viem chain after ${maxRetries} attempts from ${httpEndpoint}: ${lastError?.message || "Unknown error"}` ); } async function deployViemContract(context, abi, bytecode, options) { const url = context.viem().transport.url; const { privateKey = ALITH_PRIVATE_KEY, ...rest } = options || {}; const blob = { ...rest, abi, bytecode, account: privateKeyToAccount(privateKey) }; const account = privateKeyToAccount(ALITH_PRIVATE_KEY); const client = createWalletClient({ transport: http(url), account, chain: await deriveViemChain(url) }); const hash = await client.deployContract(blob); await context.createBlock(); for (let i = 0; i < 5; i++) { try { const { contractAddress, status, logs } = await context.viem().getTransactionReceipt({ hash }); return { contractAddress, status, logs, hash }; } catch (e) { console.log(e.message); console.log("Contract deployment query, retrying..."); await timer(100); } } throw new Error("Contract deployment query failed after 5 retries"); } async function createRawTransfer(context, to, value, options) { const transferAmount = typeof value === "bigint" ? value : BigInt(value); return await createViemTransaction(context, { ...options, to, value: transferAmount }); } async function createViemTransaction(context, options) { const type = !!options && !!options.txnType ? options.txnType : "eip1559"; const privateKey = !!options && !!options.privateKey ? options.privateKey : ALITH_PRIVATE_KEY; const account = privateKeyToAccount(privateKey); const value = options?.value ? options.value : 0n; const to = options?.to ? options.to : "0x0000000000000000000000000000000000000000"; const chainId = await context.viem().getChainId(); const txnCount = await context.viem().getTransactionCount({ address: account.address }); const gasPrice = await context.viem().getGasPrice(); const data = options?.data ? options.data : "0x"; const estimatedGas = options.skipEstimation || options.gas !== void 0 ? 1500000n : await context.viem().estimateGas({ account: account.address, to, value, data }); const accessList = options?.accessList ? options.accessList : []; const txnBlob = type === "eip1559" ? { to, value, maxFeePerGas: options.maxFeePerGas !== void 0 ? options.maxFeePerGas : gasPrice, maxPriorityFeePerGas: options.maxPriorityFeePerGas !== void 0 ? options.maxPriorityFeePerGas : gasPrice, gas: options.gas !== void 0 ? options.gas : estimatedGas, nonce: options.nonce !== void 0 ? options.nonce : txnCount, data, chainId, type } : type === "legacy" ? { to, value, gasPrice: options.gasPrice !== void 0 ? options.gasPrice : gasPrice, gas: options.gas !== void 0 ? options.gas : estimatedGas, nonce: options.nonce !== void 0 ? options.nonce : txnCount, data } : type === "eip2930" ? { to, value, gasPrice: options.gasPrice !== void 0 ? options.gasPrice : gasPrice, gas: options.gas !== void 0 ? options.gas : estimatedGas, nonce: options.nonce !== void 0 ? options.nonce : txnCount, data, chainId, type } : {}; if (type === "eip1559" && accessList.length > 0 || type === "eip2930" && accessList.length > 0) { txnBlob.accessList = accessList; } return await account.signTransaction(txnBlob); } async function checkBalance(context, account = ALITH_ADDRESS, block = "latest") { return typeof block === "string" ? await context.viem().getBalance({ address: account, blockTag: block }) : typeof block === "bigint" ? await context.viem().getBalance({ address: account, blockNumber: block }) : await context.viem().getBalance({ address: account }); } async function sendRawTransaction(context, rawTx) { return await context.viem().request({ method: "eth_sendRawTransaction", params: [rawTx] }); } export { Perbill, Percent, calculateFeePortions, checkBalance, checkBlockFinalized, checkTimeSliceForUpgrades, clearLoggers, createAndFinalizeBlock, createEthersTransaction, createLogger, createRawTransfer, createViemTransaction, customWeb3Request, deployViemContract, deriveViemChain, directRpcRequest, extractError, extractFee, extractInfo, extractPreimageDeposit, extractWeight, fetchHistoricBlockNum, filterAndApply, getAllCompiledContracts, getBlockArray, getBlockExtrinsic, getBlockTime, getCompiled, getDevChain, getDispatchError, getLogger, getObjectMethods, isExtrinsicSuccessful, jumpBlocksChopsticks, jumpBlocksDev, jumpRoundsChopsticks, jumpRoundsDev, jumpToRoundChopsticks, jumpToRoundDev, log, mapExtrinsics, normalizeUrlToHttps, printEvents, printTokens, sendRawTransaction, setLoggerEnabled, setupLogger2 as setupLogger, signAndSend, sortObjectByKeys, web3EthCall }; //# sourceMappingURL=index.js.map