UNPKG

@pendulum-chain/api-solang

Version:

Interface to interact with smart contracts compiled via Solang

175 lines (174 loc) 7.14 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.signAndSubmitExtrinsic = exports.submitExtrinsic = exports.signExtrinsic = void 0; exports.deployContract = deployContract; exports.executeMessage = executeMessage; exports.createExecuteMessageExtrinsic = createExecuteMessageExtrinsic; exports.submitExecuteMessageExtrinsic = submitExecuteMessageExtrinsic; exports.readMessage = readMessage; const util_1 = require("@polkadot/util"); const api_contract_1 = require("@polkadot/api-contract"); const contractRpc_js_1 = require("./contractRpc.js"); const submitExtrinsic_js_1 = require("./submitExtrinsic.js"); Object.defineProperty(exports, "signAndSubmitExtrinsic", { enumerable: true, get: function () { return submitExtrinsic_js_1.signAndSubmitExtrinsic; } }); Object.defineProperty(exports, "submitExtrinsic", { enumerable: true, get: function () { return submitExtrinsic_js_1.submitExtrinsic; } }); Object.defineProperty(exports, "signExtrinsic", { enumerable: true, get: function () { return submitExtrinsic_js_1.signExtrinsic; } }); const util_crypto_1 = require("@polkadot/util-crypto"); const deployContract_js_1 = require("./deployContract.js"); function decodeContractEvents(eventRecords, lookupAbi) { return eventRecords .filter(({ event: { section, method } }) => section === "contracts" && method === "ContractEmitted") .map((eventRecord) => { const dataJson = eventRecord.event.data.toHuman(); const emittingContractAddress = dataJson.contract; let dataHexString = dataJson.data; if (dataHexString.startsWith("0x")) dataHexString = dataHexString.slice(2); const data = new Uint8Array(dataHexString.length / 2); for (let i = 0; i * 2 < dataHexString.length; i += 1) { data[i] = parseInt(dataHexString.slice(i * 2, i * 2 + 2), 16); } const abi = lookupAbi?.(emittingContractAddress); if (abi === undefined) { return { emittingContractAddress, data, }; } const decodedEvent = abi.decodeEvent(eventRecord); return { emittingContractAddress, data, decoded: { args: decodedEvent.event.args.map((arg, index) => ({ name: arg.name, value: decodedEvent.args[index].toHuman(), })), eventIdentifier: decodedEvent.event.identifier, }, }; }); } async function deployContract({ signer, api, abi, constructorArguments, constructorName, limits, skipDryRunning, modifyExtrinsic, lookupAbi, }) { const result = await (0, deployContract_js_1.basicDeployContract)({ api, abi, constructorArguments, constructorName, limits, signer, skipDryRunning, modifyExtrinsic, }); switch (result.type) { case "panic": case "reverted": case "error": return result; } const extendedLookupAbi = (contractAddress) => { if ((0, util_crypto_1.addressEq)(contractAddress, result.deploymentAddress)) { return abi; } return lookupAbi?.(contractAddress); }; return { ...result, events: decodeContractEvents(result.eventRecords, extendedLookupAbi), }; } async function executeMessage(options) { const { getSigner, modifyExtrinsic, lookupAbi } = options; const { execution, result: readMessageResult } = await createExecuteMessageExtrinsic(options); if (execution.type === "onlyRpc") { return { execution, result: readMessageResult, }; } let { extrinsic } = execution; if (modifyExtrinsic) { extrinsic = modifyExtrinsic(extrinsic); } const signer = await getSigner(); const { eventRecords, status, transactionFee, txIndex, txHash } = await (0, submitExtrinsic_js_1.signAndSubmitExtrinsic)(extrinsic, signer); return { execution: { type: "extrinsic", contractEvents: decodeContractEvents(eventRecords, lookupAbi), transactionFee, txIndex, txHash, }, result: status.type === "success" || readMessageResult === undefined ? readMessageResult : { ...status, gasMetrics: readMessageResult.gasMetrics }, }; } async function createExecuteMessageExtrinsic({ abi, api, contractDeploymentAddress, messageArguments, messageName, limits, callerAddress, gasLimitTolerancePercentage = 10, skipDryRunning, }) { const contract = new api_contract_1.ContractPromise(api, abi, contractDeploymentAddress); let gasRequired; let readMessageResult; if (skipDryRunning === true) { gasRequired = api.createType("WeightV2", limits.gas); } else { readMessageResult = await readMessage({ api, abi, contractDeploymentAddress, callerAddress, messageName, messageArguments, limits, }); if (readMessageResult.type !== "success") { return { execution: { type: "onlyRpc" }, result: readMessageResult }; } gasRequired = readMessageResult.gasMetrics.gasRequired; if (gasLimitTolerancePercentage > 0) { gasRequired = api.createType("WeightV2", { refTime: (gasRequired.refTime.toBigInt() * (100n + BigInt(gasLimitTolerancePercentage))) / 100n, proofSize: (gasRequired.proofSize.toBigInt() * (100n + BigInt(gasLimitTolerancePercentage))) / 100n, }); } } const typesAddress = api.registry.createType("AccountId", contractDeploymentAddress); let extrinsic = api.tx.contracts.call(typesAddress, util_1.BN_ZERO, gasRequired, limits.storageDeposit, contract.abi.findMessage(messageName).toU8a(messageArguments)); return { execution: { type: "extrinsic", extrinsic }, result: readMessageResult, }; } async function submitExecuteMessageExtrinsic(extrinsic, lookupAbi) { const { eventRecords, status, transactionFee } = await (0, submitExtrinsic_js_1.submitExtrinsic)(extrinsic); return { contractEvents: decodeContractEvents(eventRecords, lookupAbi), transactionFee, status, }; } async function readMessage({ abi, api, contractDeploymentAddress, callerAddress, messageName, messageArguments, limits, }) { const { gasRequired, gasConsumed, output } = await (0, contractRpc_js_1.rpcCall)({ api, abi, contractAddress: contractDeploymentAddress, callerAddress, limits, messageName, messageArguments, }); const gasMetrics = { gasRequired, gasConsumed }; switch (output.type) { case "success": case "reverted": case "panic": return { ...output, gasMetrics }; case "error": return { type: "error", error: output.description ?? "unknown", gasMetrics, }; } }