import { Idl, Program } from "@project-serum/anchor"; import MALLOC_IDL from "../idls/malloc.json"; import { Action, ActionPIDToActionUID, BuildActionMap, ContractType, MallocIdl, NonSpecificConstructionFilled, NonSpecificConstructionFilledUnserializable, NonSpecificConstructionOpts, TransactionType, utils, } from "../index"; import { MallocActionBuilderError, MallocBuilderError, MallocCompilerError, MallocErrorTypes, MallocRunnerError, } from "../interfaces/errors"; import { freemem } from "os"; const MALLOC_CORE_ERROR_MARKER = 0x40000000; const MIN_MALLOC_ERROR_CODE = MALLOC_CORE_ERROR_MARKER; const MAX_MALLOC_ERROR_CODE = MALLOC_CORE_ERROR_MARKER + 200; const MALLOC_ACTION_UNEXPECTED_NUMBER_RETURNS = 420696; export const newActionBuilderError = ( actionTypeUID: string, actionName: string, actionIdx: number, message: string, filledConstruction?: NonSpecificConstructionFilled ): MallocActionBuilderError => { return { mallocErrorMarker: "MALLOC ERROR MARKER", type: MallocErrorTypes.MALLOC_ACTION_BUILDER, actionName, message: `An error occurred with ${actionName}: ${message}`, actionIndex: actionIdx, actionTypeUID, filledNonSpecificConstruction: filledConstruction, }; }; export const newBuilderError = (message: string): MallocBuilderError => { return { mallocErrorMarker: "MALLOC ERROR MARKER", type: MallocErrorTypes.MALLOC_BUILDER, message, }; }; export const newCompilerError = (message: string): MallocCompilerError => { return { mallocErrorMarker: "MALLOC ERROR MARKER", type: MallocErrorTypes.MALLOC_COMPILER, message, }; }; export const newCleanupError = ( e: any, skippedPreflight: boolean ): MallocRunnerError => { const eStr: string = e.toString(); const logInfo = skippedPreflight ? extractLogInfPreflight(eStr) : extractLogInfoPreflight(eStr); if (!logInfo) return { type: MallocErrorTypes.MALLOC_RUNNER, code: -1, message: `An error occurred in closing the ephemeral accounts: ${e} `, mallocErrorMarker: "MALLOC ERROR MARKER", contractType: "Other", }; const contractType = getContractType(logInfo.code); if (contractType === "Malloc Action") return { type: MallocErrorTypes.MALLOC_RUNNER, code: -1, message: `An error occurred in closing the ephemeral action result accounts: ${e} `, mallocErrorMarker: "MALLOC ERROR MARKER", contractType, }; else return { type: MallocErrorTypes.MALLOC_RUNNER, code: -1, message: `An error occurred in closing the ephemeral accounts: ${e} `, mallocErrorMarker: "MALLOC ERROR MARKER", contractType, }; }; /** * Extract error information from the contract * This should be able to this both when preflight is on and off */ export const newRunError = ( e: any, actionMap: BuildActionMap, skippedPreflight: boolean, actions: Action[], txType: TransactionType, actionIdx?: number, isMainActionCall?: boolean ): MallocRunnerError => { const actionName = actionIdx !== undefined ? actions[actionIdx].actionName : undefined; console.log("Error occurred with tx type", txType); const eStr: string = e?.err?.toString() ?? e.toString(); const logInfo = skippedPreflight ? extractLogInfPreflight(eStr) : extractLogInfoPreflight(eStr); if (!logInfo || txType === "unknown") { return { type: MallocErrorTypes.MALLOC_RUNNER, code: -1, message: `An error occurred: ${eStr}`, contractType: "Other", actionName, actionIdx, mallocErrorMarker: "MALLOC ERROR MARKER", }; } const { code } = logInfo; // This code is handled separately as it is caught outside of the idl generated errors if (code === MALLOC_ACTION_UNEXPECTED_NUMBER_RETURNS) { return { type: MallocErrorTypes.MALLOC_RUNNER, code: code, message: `The action returned an unexpected number of return tokens`, contractType: "Malloc Action", actionName, mallocErrorMarker: "MALLOC ERROR MARKER", actionIdx, name: "UnexpectedNumberReturns", }; } const contractType = getContractType(code, actionIdx); if (contractType === "Other") { const message = actionIdx ? `An unexpected error occurred from which originated from ${actions[actionIdx].actionName}` : `An unexpected error occured: ${eStr}`; return { type: MallocErrorTypes.MALLOC_RUNNER, code, message, contractType: "Other", mallocErrorMarker: "MALLOC ERROR MARKER", actionName, actionIdx, }; } else if (contractType === "Malloc Action") { let message: string; const idx = actionIdx; const uid = actions[idx].actionTypeUID; let idlError: Idl["errors"][number] | undefined; if (isMainActionCall) { idlError = actionMap[uid].idl?.errors.find((e) => e.code === code); message = idlError ? `An error occurred in action ${actions[idx].actionName}: ${idlError.msg}` : `An error occurred in action ${actions[idx].actionName}`; } else { message = `An error occurred in the setup to action ${actionIdx[idx].actionName}: ${eStr}`; } return { type: MallocErrorTypes.MALLOC_RUNNER, code: code, message, contractType: "Malloc Action", mallocErrorMarker: "MALLOC ERROR MARKER", actionIdx: idx, actionName, name: idlError?.name, }; } else if (contractType === "Malloc Core") { // The idl expects codes to start from 300 const idlCode = (~MALLOC_CORE_ERROR_MARKER & code) + 300; // remove the upper bit marker for malloc error const idlError = MallocIdl.errors.find((e) => e.code === idlCode); const message = idlError ? actionIdx ? `An error occurred when calling ${actions[actionIdx].actionName}: ${idlError.msg}` : `An error occurred: ${idlError.msg}` : actionIdx ? `An unexpected error occurred when calling ${actions[actionIdx].actionName}` : `An unexpected error occurred`; return { type: MallocErrorTypes.MALLOC_RUNNER, code: code, message, contractType: "Malloc Core", mallocErrorMarker: "MALLOC ERROR MARKER", actionIdx, actionName, name: idlError?.name, }; } else { throw "Error with error builder for new run error"; } }; interface LogInfo { code: number; } const getContractType = (code: number, actionIdx?: number): ContractType => { let contractType: ContractType; if ( (code < 400 && code >= 300) || code === MALLOC_ACTION_UNEXPECTED_NUMBER_RETURNS ) { contractType = "Malloc Action"; } else if (code >= MIN_MALLOC_ERROR_CODE && MAX_MALLOC_ERROR_CODE >= code) { contractType = "Malloc Core"; } else { contractType = "Other"; } return contractType; }; const extractLogInfoPreflight = (log: string): LogInfo | undefined => { const matchesCodePreflight = log.match(/custom\sprogram\serror:\s(\w+)/); let code: number; if (matchesCodePreflight && matchesCodePreflight.length >= 2) { const codeStr = matchesCodePreflight[1]; code = parseInt(codeStr, 16); } else { return undefined; } return { code, }; }; const extractLogInfPreflight = (log: string): LogInfo | undefined => { const matchesCodePreflight = log.match(/"Custom":([0-9]+)\}/); let code: number; if (matchesCodePreflight && matchesCodePreflight.length >= 2) { const codeStr = matchesCodePreflight[1]; code = parseInt(codeStr); } else { return undefined; } return { code, }; };