UNPKG

@bandprotocol/bandchain.js

Version:

Library for interacting with BandChain in browser and Node.js environments

196 lines (195 loc) 6.09 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ObiSpec = exports.Obi = exports.ObiBytes = exports.ObiString = exports.ObiStruct = exports.ObiVector = exports.ObiBool = exports.ObiInteger = void 0; const error_1 = require("./error"); class ObiBase { } class ObiInteger extends ObiBase { constructor(schema) { super(); this.isSigned = schema[0] === 'i'; this.sizeInBytes = parseInt(schema.slice(1)) / 8; } encode(value) { let newValue = BigInt(value); return Buffer.from([...Array(this.sizeInBytes)] .map(() => { const dataByte = newValue % BigInt(1 << 8); newValue /= BigInt(1 << 8); return Number(dataByte); }) .reverse()); } decode(buff) { let value = BigInt(0); let multiplier = BigInt(1); for (let i = 0; i < this.sizeInBytes; i++) { value += BigInt(buff.readUInt8(this.sizeInBytes - i - 1)) * multiplier; multiplier *= BigInt(1 << 8); } return [value, buff.slice(this.sizeInBytes)]; } } ObiInteger.REGEX = /^(u|i)(8|16|32|64|128|256)$/; exports.ObiInteger = ObiInteger; class ObiBool extends ObiBase { encode(value) { return new ObiInteger('u8').encode(value ? BigInt(1) : BigInt(0)); } decode(buff) { let [u8, remaining] = new ObiInteger('u8').decode(buff); if (u8 === BigInt(1)) return [true, remaining]; else if (u8 === BigInt(0)) return [false, remaining]; else throw new error_1.DecodeError(`Boolean value must be 1 or 0 but got ${u8}`); } } ObiBool.REGEX = /^bool$/; exports.ObiBool = ObiBool; class ObiVector extends ObiBase { constructor(schema) { super(); this.internalObi = ObiSpec.fromSpec(schema.slice(1, -1)); } encode(value) { return Buffer.concat([ new ObiInteger('u32').encode(value.length), ...value.map((item) => this.internalObi.encode(item)), ]); } decode(buff) { let [length, remaining] = new ObiInteger('u32').decode(buff); let value = []; for (let i = 0; i < Number(length); i++) { const decodeInternalResult = this.internalObi.decode(remaining); value.push(decodeInternalResult[0]); remaining = decodeInternalResult[1]; } return [value, remaining]; } } ObiVector.REGEX = /^\[.*\]$/; exports.ObiVector = ObiVector; class ObiStruct extends ObiBase { constructor(schema) { super(); this.internalObiKvs = []; let curlyCount = 0; let kv = ['', ''], fill = 0; for (let c of schema.slice(1)) { if (c == '{') curlyCount++; else if (curlyCount && c == '}') curlyCount--; else if (!curlyCount && c === ':') { fill = 1; continue; } else if (!curlyCount && (c === ',' || c === '}')) { kv[1] = ObiSpec.fromSpec(kv[1]); this.internalObiKvs.push(kv); kv = ['', '']; fill = 0; continue; } kv[fill] += c; } } encode(value) { return Buffer.concat(this.internalObiKvs.map(([k, obi]) => obi.encode(value[k]))); } decode(buff) { let value = {}; let remaining = buff; for (let [k, obi] of this.internalObiKvs) { const decodeInternalResult = obi.decode(remaining); value[k] = decodeInternalResult[0]; remaining = decodeInternalResult[1]; } return [value, remaining]; } } ObiStruct.REGEX = /^{.*}$/; exports.ObiStruct = ObiStruct; class ObiString extends ObiBase { encode(value) { return Buffer.concat([ new ObiInteger('u32').encode(BigInt(value.length)), Buffer.from(value), ]); } decode(buff) { let [length, remaining] = new ObiInteger('u32').decode(buff); return [ remaining.slice(0, parseInt(length)).toString(), remaining.slice(parseInt(length)), ]; } } ObiString.REGEX = /^string$/; exports.ObiString = ObiString; class ObiBytes { encode(value) { return Buffer.concat([ new ObiInteger('u32').encode(value.length), Buffer.from(value), ]); } decode(buff) { let [length, remaining] = new ObiInteger('u32').decode(buff); return [ remaining.slice(0, parseInt(length)), remaining.slice(parseInt(length)), ]; } } ObiBytes.REGEX = /^bytes$/; exports.ObiBytes = ObiBytes; class Obi { constructor(schema) { const normalizedSchema = schema.replace(/\s+/g, ''); const tokens = normalizedSchema.split('/'); this.inputObi = ObiSpec.fromSpec(tokens[0]); this.outputObi = ObiSpec.fromSpec(tokens[1]); } encodeInput(value) { return this.inputObi.encode(value); } decodeInput(buff) { const [value, remaining] = this.inputObi.decode(buff); if (remaining.length != 0) throw new error_1.DecodeError('Not all data is consumed after decoding output'); return value; } encodeOutput(value) { return this.outputObi.encode(value); } decodeOutput(buff) { const [value, remaining] = this.outputObi.decode(buff); if (remaining.length != 0) throw new error_1.DecodeError('Not all data is consumed after decoding output'); return value; } } exports.Obi = Obi; class ObiSpec { static fromSpec(schema) { for (let impl of ObiSpec.impls) { if (schema.match(impl.REGEX)) { return new impl(schema); } } throw new error_1.SchemaError(`No schema matched: <${schema}>`); } } ObiSpec.impls = [ ObiInteger, ObiBool, ObiVector, ObiStruct, ObiString, ObiBytes, ]; exports.ObiSpec = ObiSpec;