UNPKG

@kiroboio/fct-core

Version:

Kirobo.io FCT Core library

144 lines 4.99 kB
import { ethers } from "ethers"; import _ from "lodash"; const isAddress = ethers.utils.isAddress; export const mustBeInteger = [ "validFrom", "expiresAt", "maxGasPrice", "recurrency.maxRepeats", "recurrency.chillTime", "multisig.minimumApprovals", "payableGasLimit", ]; export const mustBeAddress = ["builder.address"]; export const mustBeBoolean = ["purgeable", "blockable", "authEnabled", "dryRun", "recurrency.accumetable"]; export const mustBeObject = ["app", "builder", "recurrency", "multisig"]; export function validateOptionsValues({ value, initOptions, parentKeys = [], }) { Object.keys(value).forEach((key) => { const keyId = [...parentKeys, key].join("."); const objKey = key; // If value is undefined, skip it if (value[objKey] === undefined) return; OptionsValidator.validate({ keyId, key: objKey, parentKeys, value, initOptions }); }); } class OptionsValidator { keyId; key; parentKeys; value; initOptions; type; constructor({ keyId, key, parentKeys, value, initOptions, }) { this.keyId = keyId; this.key = key; this.parentKeys = parentKeys; this.value = value; this.initOptions = initOptions; if (mustBeObject.includes(keyId)) { this.type = "object"; } else if (mustBeBoolean.includes(keyId)) { this.type = "boolean"; } else if (mustBeInteger.includes(keyId)) { this.type = "integer"; } else if (mustBeAddress.includes(keyId)) { this.type = "address"; } else { this.type = "string"; } } static validate = (data) => { return new OptionsValidator(data).validate(); }; validate() { switch (this.type) { case "object": return this._checkObject(); case "boolean": return this._checkBoolean(); case "integer": return this._checkInteger(); case "address": return this._checkAddress(); case "string": return this._checkString(); } } _checkObject() { if (typeof this.value[this.key] === "object") { validateOptionsValues({ value: this.value[this.key], initOptions: this.initOptions, parentKeys: [...this.parentKeys, this.key], }); return; } else { throw new Error(`Options: ${this.keyId} must be an object`); } } _checkBoolean() { if (typeof this.value[this.key] !== "boolean") { throw new Error(`Options: ${this.keyId} must be a boolean`); } return; } _checkInteger() { const value = this._checkString({ canBeNumber: true }); if (!value) { return; } if (typeof value !== "string") return; if (value.includes(".")) { throw new Error(`Options: ${this.keyId} cannot be a decimal`); } if (value.startsWith("-")) { throw new Error(`Options: ${this.keyId} cannot be negative`); } if (this.keyId === "recurrency.maxRepeats" && +value < 0) { throw new Error(`Options: ${this.keyId} should be at least 0. If value is 0 or 1, recurrency will not be enabled in order to save gas`); } if (this.key === "expiresAt") { const expiresAt = Number(value); const now = Number(new Date().getTime() / 1000).toFixed(); const validFrom = this.value.validFrom; if (BigInt(expiresAt) <= BigInt(now)) { throw new Error(`Options: expiresAt must be in the future`); } if (validFrom && BigInt(expiresAt) <= BigInt(validFrom)) { throw new Error(`Options: expiresAt must be greater than validFrom`); } } } _checkAddress() { const value = this._checkString(); if (!value) { throw new Error(`Options: ${this.keyId} is required, it's undefined`); } if (!isAddress(value)) { throw new Error(`Options: ${this.keyId} is not a valid address`); } } _checkString({ canBeNumber = false } = {}) { const realVal = _.get(this.initOptions, this.keyId); const value = this.value[this.key]; if (typeof value !== typeof realVal) { if (canBeNumber && typeof value === "number") { return value.toString(); } if (typeof realVal === "undefined") { return value; } const mustBeString = canBeNumber ? `number or ${typeof realVal}` : typeof realVal; throw new Error(`Options: ${this.keyId} must be a ${mustBeString}`); } return value; } } //# sourceMappingURL=helpers.js.map