@simbachain/simbats
Version:
TypeScript SDK for SIMBA Chain
300 lines • 12.3 kB
JavaScript
;
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