UNPKG

@simbachain/simbats

Version:
300 lines 12.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ParamCheckingContract = void 0; const axios_1 = __importDefault(require("axios")); const config_1 = require("./config"); const request_handler_1 = require("./request_handler"); /** * this class is used to check param types before making HTTP call for contract methods */ class ParamCheckingContract { constructor(appName, contractName, baseApiUrl, metadata) { this.paramsRestricted = null; this.appName = appName; this.contractName = contractName; this.baseApiUrl = baseApiUrl; this.requestHandler = new request_handler_1.RequestHandler(this.baseApiUrl); if (metadata) { this.metadata = metadata; } } /** * gives the true type of a variable, since pretty much everything in JS is just an "object" * @param someObject * @returns {string} */ trueType(someObject) { return Object.prototype.toString.call(someObject).slice(8, -1).toLowerCase(); } /** * retrieves metadata for this.contractName * @returns {Promise<Record<any, any>>} */ async getMetadata() { config_1.SimbaConfig.log.debug(`:: SIMBA : ENTER :`); if (this.metadata) { config_1.SimbaConfig.log.debug(`:: SIMBA : EXIT :`); return this.metadata; } const url = this.requestHandler.buildURL(this.baseApiUrl, `/v2/apps/${this.appName}/contract/${this.contractName}/`); const options = await this.requestHandler.getAuthAndOptions(); try { const res = await this.requestHandler.doGetRequest(url, options, false); if (!res.data) { const message = "unable to retrieve metadata"; config_1.SimbaConfig.log.error(`:: SIMBA : EXIT : ${message}`); throw (message); } const metadata = res.data["metadata"]; config_1.SimbaConfig.log.debug(`:: EXIT : contract metadata: ${JSON.stringify(metadata)}`); this.metadata = metadata; return metadata; } catch (error) { if (axios_1.default.isAxiosError(error) && error.response) { config_1.SimbaConfig.log.error(`${JSON.stringify(error.response.data)}`); } else { config_1.SimbaConfig.log.error(`${JSON.stringify(error)}`); } config_1.SimbaConfig.log.debug(`:: SIMBA : EXIT :`); throw (error); } } /** * specifies whether a param is an array * @param param * @returns {boolean} */ isArray(param) { return param.endsWith("]"); } /** * specifies what variables are permitted as elements of an array * @param arrString * @returns {Record<number | string, string>} */ arrayRestrictions(arrString) { config_1.SimbaConfig.log.debug(`:: ENTER : ${arrString}`); let reverseArray = ""; for (let i = arrString.length - 1; i >= 0; i--) { const char = arrString[i]; switch (char) { case ("["): { reverseArray += "]"; break; } case ("]"): { reverseArray += "["; break; } default: { reverseArray += char; break; } } } const arrLengths = {}; for (let i = 0; i < this.getDimensions(arrString); i++) { const arrLen = reverseArray.slice(reverseArray.indexOf("[") + 1, reverseArray.indexOf("]")); arrLengths[i] = arrLen ? Number(arrLen.split("").reverse().join("")) : null; reverseArray = reverseArray.slice(reverseArray.indexOf("]") + 1); } config_1.SimbaConfig.log.debug(`:: EXIT : ${JSON.stringify(arrLengths)}`); return arrLengths; } /** * returns number of dimensions for an array * @param param * @param dims * @returns {number} */ getDimensions(param, dims = 0) { if (!param.includes("[")) { return dims; } param = param.slice(param.indexOf("[") + 1); dims += 1; return this.getDimensions(param, dims); } /** * specifies all param restrictions for all contract methods * @returns {Promise<Record<any, any> | void>} */ async paramRestrictions() { config_1.SimbaConfig.log.debug(`:: SIMBA : ENTER :`); const metadata = this.metadata ? this.metadata : await this.getMetadata(); const methods = metadata.contract.methods; const paramRest = {}; for (let method in methods) { if (method) { const methodKeys = methods[method]; const methodParams = methodKeys.params; for (let param in methodParams) { if (param) { const paramName = methodParams[param].name; const rawType = methodParams[param].type; const containsOrIsUint = rawType.startsWith("uint"); if (!containsOrIsUint && !this.isArray(paramName)) { continue; } if (paramRest[method] === undefined) { paramRest[method] = {}; } if (containsOrIsUint && !this.isArray(rawType)) { if (paramRest[method].uintParams === undefined) { paramRest[method].uintParams = [paramName]; } else { paramRest[method].uintParams.push(paramName); } } else { if (this.isArray(rawType)) { if (paramRest[method]["arrayParams"] === undefined) { paramRest[method]["arrayParams"] = {}; } const _arrayRestrictions = this.arrayRestrictions(rawType); _arrayRestrictions["containsUint"] = containsOrIsUint; paramRest[method]["arrayParams"][paramName] = _arrayRestrictions; } } } } } } config_1.SimbaConfig.log.debug(`:: EXIT : ${JSON.stringify(paramRest)}`); return paramRest; } /** * returns true if passed array argument meets param restrictions * @param arr * @param paramName * @param paramRestrictionsObj * @param level * @returns {boolean | Error} */ checkArrayRestrictions(arr, paramName, paramRestrictionsObj, level = 0) { const funcParams = { arr, paramName, paramRestrictionsObj, level, }; config_1.SimbaConfig.log.debug(`:: ENTER : ${JSON.stringify(funcParams)}`); if (!Array.isArray(arr)) { const message = `${arr} is not an array`; config_1.SimbaConfig.log.error(`:: SIMBA : EXIT : ${message}`); throw (message); } let levelRestriction; const strLevel = String(level); config_1.SimbaConfig.log.debug(`strLevel: ${strLevel}`); if (strLevel in paramRestrictionsObj[paramName]) { levelRestriction = paramRestrictionsObj[paramName][strLevel]; // SimbaConfig.log.debug(`:: levelRestriction : ${levelRestriction}`); } else { const message = `Passed array contains too many dimensions for param ${paramName}`; config_1.SimbaConfig.log.error(message); throw (message); } if (levelRestriction !== null) { if (arr.length !== Number(levelRestriction)) { const message = `Array length error for param ${paramName}. param "${paramName}" should have length ${Number(levelRestriction)}, but had length ${arr.length}`; config_1.SimbaConfig.log.error(`:: EXIT : ${message}`); throw (message); } } level += 1; for (const [i, element] of arr.entries()) { if (i > 0 && (this.trueType(arr[i]) !== this.trueType(arr[i - 1]))) { const message = `Array element types ${arr[i]} and ${arr[i - 1]} do not match`; config_1.SimbaConfig.log.error(`:: EXIT : ${message}`); throw (message); } if (Array.isArray(element)) { this.checkArrayRestrictions(element, paramName, paramRestrictionsObj, level); } else { if (paramRestrictionsObj[paramName]["containsUint"]) { if (!Number.isInteger(element)) { const message = `array elements for param "${paramName}" must be int, but element is ${this.trueType(element)}`; config_1.SimbaConfig.log.error(`:: EXIT : ${message}`); throw (message); } if (element < 0) { const message = `array elements for param "${paramName}" must be uint (>0), but element's value is ${element}`; config_1.SimbaConfig.log.error(`:: EXIT : ${message}`); throw (message); } } } } config_1.SimbaConfig.log.debug(`:: EXIT : ${true}`); return true; } /** * returns true if a passed uint argument meets param restrictions * @param paramValue * @returns {boolean | Error} */ checkUintRestriction(paramValue) { config_1.SimbaConfig.log.debug(`:: ENTER : ${JSON.stringify(paramValue)}`); if (paramValue < 0) { const message = `parameter must be positive, but parameter value is ${paramValue}`; config_1.SimbaConfig.log.error(`:: EXIT : ${message}`); throw (message); } if (!Number.isInteger(paramValue)) { const message = `parameter must be an integer, but parameter value is ${paramValue}`; config_1.SimbaConfig.log.error(`:: EXIT : ${message}`); throw (message); } config_1.SimbaConfig.log.debug(`:: EXIT : ${true}`); return true; } /** * validates all passed arguments for a method call * @param methodName * @param inputs * @returns {Promise<boolean | Error>} */ async validateParams(methodName, inputs) { const par = { methodName, inputs, }; config_1.SimbaConfig.log.debug(`:: ENTER : ${JSON.stringify(par)}`); const paramRest = await this.paramRestrictions(); const methodRestrictions = paramRest[methodName] || null; if (!methodRestrictions) { config_1.SimbaConfig.log.debug(`:: EXIT : valid`); return true; } const uintParams = methodRestrictions["uintParams"] || {}; const arrayParams = methodRestrictions["arrayParams"] || {}; for (const paramName in inputs) { config_1.SimbaConfig.log.debug(`paramName: ${paramName}`); if (paramName !== undefined) { if (paramName in uintParams) { const paramValue = inputs[paramName]; this.checkUintRestriction(paramValue); } if (paramName in arrayParams) { const paramValue = inputs[paramName]; this.checkArrayRestrictions(paramValue, paramName, arrayParams); } } } config_1.SimbaConfig.log.debug(`:: EXIT : passed validateParams, valid`); return true; } } exports.ParamCheckingContract = ParamCheckingContract; //# sourceMappingURL=param_checking_contract.js.map