UNPKG

@taquito/michelson-encoder

Version:

converts michelson data and types into convenient JS/TS objects

1,431 lines (1,420 loc) 142 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('fast-json-stable-stringify'), require('@taquito/core'), require('bignumber.js'), require('@taquito/utils')) : typeof define === 'function' && define.amd ? define(['exports', 'fast-json-stable-stringify', '@taquito/core', 'bignumber.js', '@taquito/utils'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.taquitoMichelsonEncoder = {}, global.stringify, global.core, global.BigNumber, global.utils)); })(this, (function (exports, stringify, core, BigNumber, utils) { 'use strict'; var _a$1; /** * @category Error * @description Error that indicates an invalid map type being passed or used */ class InvalidMapTypeError extends core.TaquitoError { constructor(mapType, reason) { super(); this.mapType = mapType; this.reason = reason; this.message = `The map type '${JSON.stringify(mapType)}' is invalid. Reason: ${reason}.`; this.name = 'InvalidMapTypeError'; } } // Retrieve a unique symbol associated with the key from the environment // Used in order to identify all object that are of type MichelsonMap even if they come from different module const michelsonMapTypeSymbol = Symbol.for('taquito-michelson-map-type-symbol'); /** * * @throws {@link InvalidMapTypeError} when the argument passed to mapType is not a valid map type */ function validateMapType(value) { if (!('prim' in value)) { throw new InvalidMapTypeError(value, `Missing 'prim' field`); } if (!['map', 'big_map'].includes(value.prim)) { throw new InvalidMapTypeError(value, `The prim field should be 'map' or 'big_map'`); } if (!('args' in value)) { throw new InvalidMapTypeError(value, `Missing 'args' field`); } if (!Array.isArray(value.args)) { throw new InvalidMapTypeError(value, `The 'args' field should be an array`); } if (value.args.length !== 2) { throw new InvalidMapTypeError(value, `The 'args' field should have 2 elements`); } } /** * @category Error * @description Error that indicates a map type mismatch, where an attempt to set a key or value in a Map doesn't match the defined type of the Map */ class MapTypecheckError extends core.TaquitoError { constructor(value, type, objectType, reason) { super(); this.value = value; this.type = type; this.reason = reason; this.name = 'MapTypecheckError'; this.message = `The ${objectType} provided: ${JSON.stringify(value)} is not compatible with the expected michelson type: ${JSON.stringify(type)}. Reason: ${JSON.stringify(reason)}.`; this.name = 'MapTypecheckError'; } } /** * @description Michelson Map is an abstraction over the michelson native map. It supports complex Pair as key */ class MichelsonMap { // Used to check if an object is a michelson map. // Using instanceof was not working for project that had multiple instance of taquito dependencies // as the class constructor is different static isMichelsonMap(obj) { return obj && obj[michelsonMapTypeSymbol] === true; } /** * @param mapType If specified key and value will be type-checked before being added to the map * * @example new MichelsonMap({ prim: "map", args: [{prim: "string"}, {prim: "int"}]}) */ constructor(mapType) { this.valueMap = new Map(); this.keyMap = new Map(); this[_a$1] = true; if (mapType) { this.setType(mapType); } } setType(mapType) { validateMapType(mapType); this.keySchema = new Schema(mapType.args[0]); this.valueSchema = new Schema(mapType.args[1]); } removeType() { this.keySchema = undefined; this.valueSchema = undefined; } static fromLiteral(obj, mapType) { const map = new MichelsonMap(mapType); Object.keys(obj).forEach((key) => { map.set(key, obj[key]); }); return map; } typecheckKey(key) { if (!this.keySchema) { return; } this.keySchema.Typecheck(key); } typecheckValue(value) { if (!this.valueSchema) { return; } this.valueSchema.Typecheck(value); } /** * @throws {@link MapTypecheckError} when the argument passed does not match the expected schema for value */ assertTypecheckValue(value) { try { this.typecheckValue(value); } catch (e) { throw new MapTypecheckError(value, this.valueSchema, 'value', e); } } /** * @throws {@link MapTypecheckError} when the argument passed does not match the expected schema for key */ assertTypecheckKey(key) { try { this.typecheckKey(key); } catch (e) { throw new MapTypecheckError(key, this.keySchema, 'key', e); } } serializeDeterministically(key) { return stringify(key); } *keys() { for (const [key] of this.entries()) { yield key; } } *values() { for (const [, value] of this.entries()) { yield value; } } *entries() { for (const key of this.valueMap.keys()) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion yield [this.keyMap.get(key), this.valueMap.get(key)]; } } get(key) { this.assertTypecheckKey(key); const strKey = this.serializeDeterministically(key); return this.valueMap.get(strKey); } /** * * @description Set a key and a value in the MichelsonMap. If the key already exists, override the current value. * * @example map.set("myKey", "myValue") // Using a string as key * * @example map.set({0: "test", 1: "test1"}, "myValue") // Using a pair as key * * @warn The same key can be represented in multiple ways, depending on the type of the key. This duplicate key situation will cause a runtime error (duplicate key) when sending the map data to the Tezos RPC node. * * For example, consider a contract with a map whose key is of type boolean. If you set the following values in MichelsonMap: map.set(false, "myValue") and map.set(null, "myValue"). * * You will get two unique entries in the MichelsonMap. These values will both be evaluated as falsy by the MichelsonEncoder and ultimately rejected by the Tezos RPC. */ set(key, value) { this.assertTypecheckKey(key); this.assertTypecheckValue(value); const strKey = this.serializeDeterministically(key); this.keyMap.set(strKey, key); this.valueMap.set(strKey, value); } delete(key) { this.assertTypecheckKey(key); this.keyMap.delete(this.serializeDeterministically(key)); this.valueMap.delete(this.serializeDeterministically(key)); } has(key) { this.assertTypecheckKey(key); const strKey = this.serializeDeterministically(key); return this.keyMap.has(strKey) && this.valueMap.has(strKey); } clear() { this.keyMap.clear(); this.valueMap.clear(); } get size() { return this.keyMap.size; } forEach(cb) { for (const [key, value] of this.entries()) { cb(value, key, this); } } } _a$1 = michelsonMapTypeSymbol; /** * @category Error * @description Error that indicates a failure when encoding invalid or incorrect data (e.g. if an address is expected but a number is received) */ class TokenValidationError extends core.TaquitoError { constructor(value, token, baseMessage) { super(); this.value = value; this.token = token; this.name = 'TokenValidationError'; const annot = this.token.annot(); const annotText = annot ? `[${annot}] ` : ''; this.message = `${annotText}${baseMessage}`; } } class Token { /** * @description Gets the strategy used for field numbering in Token execute/encode/decode to convert Michelson values to/from javascript objects, returns a value of type {@link FieldNumberingStrategy} that controls how field numbers are calculated */ static get fieldNumberingStrategy() { return Token._fieldNumberingStrategy; } /** * @description Sets the strategy used for field numbering in Token execute/encode/decode to convert Michelson values to/from javascript objects, accepts a value of type {@link FieldNumberingStrategy} that controls how field numbers are calculated */ static set fieldNumberingStrategy(value) { Token._fieldNumberingStrategy = value; } constructor(val, idx, fac, parentTokenType) { this.val = val; this.idx = idx; this.fac = fac; this.parentTokenType = parentTokenType; this.createToken = this.fac; } typeWithoutAnnotations() { const handleMichelsonExpression = (val) => { if (typeof val === 'object') { if (Array.isArray(val)) { const array = val; return array.map((item) => handleMichelsonExpression(item)); } const extended = val; if (extended.args) { return { prim: extended.prim, args: extended.args.map((x) => handleMichelsonExpression(x)), }; } else { return { prim: extended.prim, }; } } return val; }; const handleMichelsonExtended = (val) => { if (val.args) { return { prim: val.prim, args: val.args.map((x) => handleMichelsonExpression(x)), }; } else { return { prim: val.prim, }; } }; return handleMichelsonExtended(this.val); } annot() { return (Array.isArray(this.val.annots) && this.val.annots.length > 0 ? this.val.annots[0] : String(this.idx)).replace(/(%|:)(_Liq_entry_)?/, ''); } hasAnnotations() { return Array.isArray(this.val.annots) && this.val.annots.length; } get tokenVal() { return this.val; } ExtractSignature() { return [[this.ExtractSchema()]]; } } Token._fieldNumberingStrategy = 'Latest'; class ComparableToken extends Token { compare(o1, o2) { if (o1 === o2) { return 0; } return o1 < o2 ? -1 : 1; } } /** * @category Error * @description Error that indicates a failure happening when parsing encoding/executing Big Map types */ class BigMapValidationError extends TokenValidationError { constructor(value, token, message) { super(value, token, message); this.value = value; this.token = token; this.name = 'BigMapValidationError'; } } class BigMapToken extends Token { constructor(val, idx, fac) { super(val, idx, fac); this.val = val; this.idx = idx; this.fac = fac; } get ValueSchema() { return this.createToken(this.val.args[1], 0); } get KeySchema() { return this.createToken(this.val.args[0], 0); } /** * @deprecated ExtractSchema has been deprecated in favor of generateSchema * */ ExtractSchema() { return { big_map: { key: this.KeySchema.ExtractSchema(), value: this.ValueSchema.ExtractSchema(), }, }; } generateSchema() { return { __michelsonType: BigMapToken.prim, schema: { key: this.KeySchema.generateSchema(), value: this.ValueSchema.generateSchema(), }, }; } /** * @throws {@link BigMapValidationError} */ validate(value) { if (!MichelsonMap.isMichelsonMap(value)) { throw new BigMapValidationError(value, this, `Value ${JSON.stringify(value)} is not a MichelsonMap`); } } objLitToMichelsonMap(val) { if (val instanceof MichelsonMap) return val; if (typeof val === 'object') { if (Object.keys(val).length === 0) { return new MichelsonMap(); } else { return MichelsonMap.fromLiteral(val); } } return val; } /** * @throws {@link BigMapValidationError} */ Encode(args) { const val = this.objLitToMichelsonMap(args.pop()); this.validate(val); return Array.from(val.keys()) .sort((a, b) => this.KeySchema.compare(a, b)) .map((key) => { return { prim: 'Elt', args: [this.KeySchema.EncodeObject(key), this.ValueSchema.EncodeObject(val.get(key))], }; }); } /** * @throws {@link BigMapValidationError} */ EncodeObject(args, semantic) { const val = this.objLitToMichelsonMap(args); this.validate(val); if (semantic && semantic[BigMapToken.prim]) { return semantic[BigMapToken.prim](val, this.val); } return Array.from(val.keys()) .sort((a, b) => this.KeySchema.compare(a, b)) .map((key) => { return { prim: 'Elt', args: [this.KeySchema.EncodeObject(key), this.ValueSchema.EncodeObject(val.get(key))], }; }); } /** * @throws {@link InvalidMapTypeError} when the argument passed to val is an array but not a valid map type * @throws {@link BigMapValidationError} when the value is invalid */ Execute(val, semantic) { if (semantic && semantic[BigMapToken.prim]) { return semantic[BigMapToken.prim](val, this.val); } if (Array.isArray(val)) { // Athens is returning an empty array for big map in storage // Internal: In taquito v5 it is still used to decode big map diff (as if they were a regular map) const map = new MichelsonMap(this.val); val.forEach((current) => { map.set(this.KeySchema.ToKey(current.args[0]), this.ValueSchema.Execute(current.args[1])); }); return map; } else if ('int' in val) { // Babylon is returning an int with the big map id in contract storage return val.int; } else { throw new BigMapValidationError(val, this, `Big map is expecting either an array (Athens) or an object with an int property (Babylon). Got ${JSON.stringify(val)}`); } } findAndReturnTokens(tokenToFind, tokens) { if (BigMapToken.prim === tokenToFind) { tokens.push(this); } this.KeySchema.findAndReturnTokens(tokenToFind, tokens); this.ValueSchema.findAndReturnTokens(tokenToFind, tokens); return tokens; } } BigMapToken.prim = 'big_map'; /** * @category Error * @description Error that indicates a failure happening when parsing encoding/executing an OrToken */ class OrValidationError extends TokenValidationError { constructor(value, token, message) { super(value, token, message); this.value = value; this.token = token; this.name = 'OrValidationError'; } } class OrToken extends ComparableToken { constructor(val, idx, fac, parentTokenType) { super(val, idx, fac, parentTokenType); this.val = val; this.idx = idx; this.fac = fac; this.parentTokenType = parentTokenType; } Encode(args) { const label = args[args.length - 1]; const leftToken = this.createToken(this.val.args[0], this.getIdxForChildren(), 'Or'); let keyCount = 1; if (leftToken instanceof OrToken) { keyCount = Object.keys(leftToken.ExtractSchema()).length; } const rightToken = this.createToken(this.val.args[1], this.getIdxForChildren() + keyCount, 'Or'); if (String(leftToken.annot()) === String(label) && !(leftToken instanceof OrToken)) { args.pop(); return { prim: 'Left', args: [leftToken.Encode(args)] }; } else if (String(rightToken.annot()) === String(label) && !(rightToken instanceof OrToken)) { args.pop(); return { prim: 'Right', args: [rightToken.Encode(args)] }; } else { if (leftToken instanceof OrToken) { const val = leftToken.Encode(args); if (val) { return { prim: 'Left', args: [val] }; } } if (rightToken instanceof OrToken) { const val = rightToken.Encode(args); if (val) { return { prim: 'Right', args: [val] }; } } return null; } } ExtractSignature() { const leftToken = this.createToken(this.val.args[0], this.getIdxForChildren(), 'Or'); let keyCount = 1; if (leftToken instanceof OrToken) { keyCount = Object.keys(leftToken.ExtractSchema()).length; } const rightToken = this.createToken(this.val.args[1], this.getIdxForChildren() + keyCount, 'Or'); const newSig = []; if (leftToken instanceof OrToken) { newSig.push(...leftToken.ExtractSignature()); } else { for (const sig of leftToken.ExtractSignature()) { newSig.push([leftToken.annot(), ...sig]); } } if (rightToken instanceof OrToken) { newSig.push(...rightToken.ExtractSignature()); } else { for (const sig of rightToken.ExtractSignature()) { newSig.push([rightToken.annot(), ...sig]); } } return newSig; } /** * @throws {@link OrValidationError} */ EncodeObject(args, semantic) { this.validateJavascriptObject(args); const label = Object.keys(args)[0]; const leftToken = this.createToken(this.val.args[0], this.getIdxForChildren(), 'Or'); let keyCount = 1; if (leftToken instanceof OrToken) { keyCount = Object.keys(leftToken.ExtractSchema()).length; } const rightToken = this.createToken(this.val.args[1], this.getIdxForChildren() + keyCount, 'Or'); if (String(leftToken.annot()) === String(label) && !(leftToken instanceof OrToken)) { return { prim: 'Left', args: [leftToken.EncodeObject(args[label], semantic)] }; } else if (String(rightToken.annot()) === String(label) && !(rightToken instanceof OrToken)) { return { prim: 'Right', args: [rightToken.EncodeObject(args[label], semantic)] }; } else { if (leftToken instanceof OrToken) { const val = leftToken.EncodeObject(args, semantic); if (val) { return { prim: 'Left', args: [val] }; } } if (rightToken instanceof OrToken) { const val = rightToken.EncodeObject(args, semantic); if (val) { return { prim: 'Right', args: [val] }; } } return null; } } /** * @throws {@link OrValidationError} */ validateJavascriptObject(args) { if (typeof args !== 'object' || Array.isArray(args) || args === null || Object.keys(args).length !== 1) { throw new OrValidationError(args, this, `EncodeObject expects an object with a single key but got: ${JSON.stringify(args)}`); } } /** * @throws {@link OrValidationError} */ Execute(val, semantics) { const leftToken = this.createToken(this.val.args[0], this.getIdxForChildren(), 'Or'); let keyCount = 1; if (leftToken instanceof OrToken) { keyCount = Object.keys(leftToken.ExtractSchema()).length; } const rightToken = this.createToken(this.val.args[1], this.getIdxForChildren() + keyCount, 'Or'); if (val.prim === 'Right') { if (rightToken instanceof OrToken) { return rightToken.Execute(val.args[0], semantics); } else { return { [rightToken.annot()]: rightToken.Execute(val.args[0], semantics), }; } } else if (val.prim === 'Left') { if (leftToken instanceof OrToken) { return leftToken.Execute(val.args[0], semantics); } return { [leftToken.annot()]: leftToken.Execute(val.args[0], semantics), }; } else { throw new OrValidationError(val, this, `Was expecting Left or Right prim but got: ${JSON.stringify(val.prim)}`); } } traversal(getLeftValue, getRightValue, concat) { const leftToken = this.createToken(this.val.args[0], this.getIdxForChildren(), 'Or'); let keyCount = 1; let leftValue; if (leftToken instanceof OrToken) { leftValue = getLeftValue(leftToken); keyCount = Object.keys(leftToken.ExtractSchema()).length; } else { leftValue = { [leftToken.annot()]: getLeftValue(leftToken) }; } const rightToken = this.createToken(this.val.args[1], this.getIdxForChildren() + keyCount, 'Or'); let rightValue; if (rightToken instanceof OrToken) { rightValue = getRightValue(rightToken); } else { rightValue = { [rightToken.annot()]: getRightValue(rightToken) }; } const res = concat(leftValue, rightValue); return res; } /** * @deprecated ExtractSchema has been deprecated in favor of generateSchema * */ ExtractSchema() { return this.traversal((leftToken) => leftToken.ExtractSchema(), (rightToken) => rightToken.ExtractSchema(), (leftValue, rightValue) => (Object.assign(Object.assign({}, leftValue), rightValue))); } generateSchema() { return { __michelsonType: OrToken.prim, schema: this.traversal((leftToken) => { if (leftToken instanceof OrToken) { return leftToken.generateSchema().schema; } else { return leftToken.generateSchema(); } }, (rightToken) => { if (rightToken instanceof OrToken) { return rightToken.generateSchema().schema; } else { return rightToken.generateSchema(); } }, (leftValue, rightValue) => (Object.assign(Object.assign({}, leftValue), rightValue))), }; } findToken(label) { const leftToken = this.createToken(this.val.args[0], this.getIdxForChildren(), 'Or'); let keyCount = 1; if (leftToken instanceof OrToken) { keyCount = Object.keys(leftToken.ExtractSchema()).length; } const rightToken = this.createToken(this.val.args[1], this.getIdxForChildren() + keyCount, 'Or'); if (String(leftToken.annot()) === String(label) && !(leftToken instanceof OrToken) && leftToken instanceof ComparableToken) { return leftToken; } else if (String(rightToken.annot()) === String(label) && !(rightToken instanceof OrToken) && rightToken instanceof ComparableToken) { return rightToken; } else { if (leftToken instanceof OrToken) { const tok = leftToken.findToken(label); if (tok) { return tok; } } if (rightToken instanceof OrToken) { const tok = rightToken.findToken(label); if (tok) { return tok; } } return null; } } compare(val1, val2) { const labelVal1 = Object.keys(val1)[0]; const labelVal2 = Object.keys(val2)[0]; if (labelVal1 === labelVal2) { const token = this.findToken(labelVal1); if (token instanceof ComparableToken) { return token.compare(val1[labelVal1], val2[labelVal1]); } } else { const encoded1 = JSON.stringify(this.EncodeObject(val1)); const encoded2 = JSON.stringify(this.EncodeObject(val2)); return encoded1 < encoded2 ? -1 : 1; } } ToKey(val) { return this.Execute(val); } ToBigMapKey(val) { return { key: this.EncodeObject(val), type: this.typeWithoutAnnotations(), }; } findAndReturnTokens(tokenToFind, tokens) { if (OrToken.prim === tokenToFind) { tokens.push(this); } this.traversal((leftToken) => leftToken.findAndReturnTokens(tokenToFind, tokens), (rightToken) => rightToken.findAndReturnTokens(tokenToFind, tokens), (leftValue, rightValue) => (Object.assign(Object.assign({}, leftValue), rightValue))); return tokens; } getIdxForChildren() { if (Token.fieldNumberingStrategy === 'Legacy') { return this.idx; } return this.parentTokenType === 'Or' ? this.idx : 0; } } OrToken.prim = 'or'; /** * @category Error * @description Error that indicates in invalid token argument being passed */ class TokenArgumentValidationError extends core.TaquitoError { constructor(message) { super(message); this.message = message; this.name = 'TokenArgumentValidationError'; } } /** * @category Error * @description Error that indicates a failure occurring when doing a comparison of tokens */ class TokenComparisonError extends core.TaquitoError { constructor(val1, val2) { super(); this.val1 = val1; this.val2 = val2; this.name = 'TokenComparisonError'; this.message = `Tokens ${JSON.stringify(val1)} and ${JSON.stringify(val2)} are not comparable`; } } // collapse comb pair /** * @throws {@link TokenArgumentValidationError} */ function collapse$1(val, prim = PairToken.prim) { if (Array.isArray(val)) { return collapse$1({ prim: prim, args: val, }, prim); } if (val.args === undefined) { throw new TokenArgumentValidationError(`The value ${JSON.stringify(val)} is an invalid PairToken with no arguments, a pair must have two or more arguments.`); } if (val.args.length > 2) { return [ val.args[0], { prim: prim, args: val.args.slice(1), }, ]; } return [val.args[0], val.args[1]]; } class PairToken extends ComparableToken { constructor(val, idx, fac, parentTokenType) { super(Array.isArray(val) ? { prim: PairToken.prim, args: val, } : val.prim ? val : { prim: PairToken.prim, args: val, }, idx, fac, parentTokenType); } args() { // collapse comb pair return collapse$1(this.val); } tokens() { let cnt = 0; return this.args().map((a) => { const tok = this.createToken(a, this.getIdxForChildren() + cnt, 'Pair'); if (tok instanceof PairToken) { cnt += Object.keys(tok.ExtractSchema()).length; } else { cnt++; } return tok; }); } Encode(args) { return { prim: 'Pair', args: this.tokens().map((t) => t.Encode(args)), }; } ExtractSignature() { const args = this.args(); const leftToken = this.createToken(args[0], this.getIdxForChildren(), 'Pair'); let keyCount = 1; if (leftToken instanceof OrToken) { keyCount = Object.keys(leftToken.ExtractSchema()).length; } const rightToken = this.createToken(args[1], this.getIdxForChildren() + keyCount, 'Pair'); const newSig = []; for (const leftSig of leftToken.ExtractSignature()) { for (const rightSig of rightToken.ExtractSignature()) { newSig.push([...leftSig, ...rightSig]); } } return newSig; } ToBigMapKey(val) { return { key: this.EncodeObject(val), type: this.typeWithoutAnnotations(), }; } ToKey(val) { return this.Execute(val); } EncodeObject(args, semantic) { const [leftToken, rightToken] = this.tokens(); let leftValue; if (leftToken instanceof PairToken && !leftToken.hasAnnotations()) { leftValue = args; } else { leftValue = args[leftToken.annot()]; } let rightValue; if (rightToken instanceof PairToken && !rightToken.hasAnnotations()) { rightValue = args; } else { rightValue = args[rightToken.annot()]; } return { prim: 'Pair', args: [ leftToken.EncodeObject(leftValue, semantic), rightToken.EncodeObject(rightValue, semantic), ], }; } traversal(getLeftValue, getRightValue) { const args = this.args(); const leftToken = this.createToken(args[0], this.getIdxForChildren(), 'Pair'); let keyCount = 1; let leftValue; if (leftToken instanceof PairToken && !leftToken.hasAnnotations()) { leftValue = getLeftValue(leftToken); if (leftToken instanceof PairToken) { keyCount = Object.keys(leftToken.ExtractSchema()).length; } } else { leftValue = { [leftToken.annot()]: getLeftValue(leftToken) }; } const rightToken = this.createToken(args[1], this.getIdxForChildren() + keyCount, 'Pair'); let rightValue; if (rightToken instanceof PairToken && !rightToken.hasAnnotations()) { rightValue = getRightValue(rightToken); } else { rightValue = { [rightToken.annot()]: getRightValue(rightToken) }; } const res = Object.assign(Object.assign({}, leftValue), rightValue); return res; } Execute(val, semantics) { const args = collapse$1(val, 'Pair'); return this.traversal((leftToken) => leftToken.Execute(args[0], semantics), (rightToken) => rightToken.Execute(args[1], semantics)); } /** * @deprecated ExtractSchema has been deprecated in favor of generateSchema * */ ExtractSchema() { return this.traversal((leftToken) => leftToken.ExtractSchema(), (rightToken) => rightToken.ExtractSchema()); } generateSchema() { return { __michelsonType: PairToken.prim, schema: this.traversal((leftToken) => { if (leftToken instanceof PairToken && !leftToken.hasAnnotations()) { return leftToken.generateSchema().schema; } else { return leftToken.generateSchema(); } }, (rightToken) => { if (rightToken instanceof PairToken && !rightToken.hasAnnotations()) { return rightToken.generateSchema().schema; } else { return rightToken.generateSchema(); } }), }; } /** * @throws {@link TokenComparisonError} */ compare(val1, val2) { const [leftToken, rightToken] = this.tokens(); const getValue = (token, args) => { if (token instanceof PairToken && !token.hasAnnotations()) { return args; } else { return args[token.annot()]; } }; if (leftToken instanceof ComparableToken && rightToken instanceof ComparableToken) { const result = leftToken.compare(getValue(leftToken, val1), getValue(leftToken, val2)); if (result === 0) { return rightToken.compare(getValue(rightToken, val1), getValue(rightToken, val2)); } return result; } throw new TokenComparisonError(val1, val2); } findAndReturnTokens(tokenToFind, tokens) { if (PairToken.prim === tokenToFind) { tokens.push(this); } this.tokens().map((t) => t.findAndReturnTokens(tokenToFind, tokens)); return tokens; } getIdxForChildren() { if (Token.fieldNumberingStrategy === 'Legacy') { return this.idx; } return this.parentTokenType === 'Pair' ? this.idx : 0; } } PairToken.prim = 'pair'; /** * @category Error * @description Error that indicates a failure happening when parsing encoding/executing Nat */ class NatValidationError extends TokenValidationError { constructor(value, token, message) { super(value, token, message); this.value = value; this.token = token; this.name = 'NatValidationError'; } } class NatToken extends ComparableToken { constructor(val, idx, fac) { super(val, idx, fac); this.val = val; this.idx = idx; this.fac = fac; } Execute(val) { return new BigNumber(val[Object.keys(val)[0]]); } /** * @throws {@link NatValidationError} */ Encode(args) { const val = args.pop(); this.validate(val); return { int: new BigNumber(val).toFixed() }; } /** * @throws {@link NatValidationError} */ validate(val) { const bigNumber = new BigNumber(val); if (bigNumber.isNaN()) { throw new NatValidationError(val, this, `Value is not a number: ${JSON.stringify(val)}`); } if (bigNumber.isNegative()) { throw new NatValidationError(val, this, `Value cannot be negative: ${JSON.stringify(val)}`); } } /** * @throws {@link NatValidationError} */ EncodeObject(val, semantic) { this.validate(val); if (semantic && semantic[NatToken.prim]) { return semantic[NatToken.prim](val); } return { int: new BigNumber(val).toFixed() }; } /** * @deprecated ExtractSchema has been deprecated in favor of generateSchema * */ ExtractSchema() { return NatToken.prim; } generateSchema() { return { __michelsonType: NatToken.prim, schema: NatToken.prim, }; } ToBigMapKey(val) { return { key: { int: String(val) }, type: { prim: NatToken.prim }, }; } ToKey({ int }) { return new BigNumber(int); } compare(nat1, nat2) { const o1 = Number(nat1); const o2 = Number(nat2); if (o1 === o2) { return 0; } return o1 < o2 ? -1 : 1; } findAndReturnTokens(tokenToFind, tokens) { if (NatToken.prim === tokenToFind) { tokens.push(this); } return tokens; } } NatToken.prim = 'nat'; class StringToken extends ComparableToken { constructor(val, idx, fac) { super(val, idx, fac); this.val = val; this.idx = idx; this.fac = fac; } Execute(val) { return val[Object.keys(val)[0]]; } /** * @deprecated ExtractSchema has been deprecated in favor of generateSchema * */ ExtractSchema() { return StringToken.prim; } generateSchema() { return { __michelsonType: StringToken.prim, schema: StringToken.prim, }; } Encode(args) { const val = args.pop(); return { string: val }; } EncodeObject(val, semantic) { if (semantic && semantic[StringToken.prim]) { return semantic[StringToken.prim](val); } return { string: val }; } ToKey({ string }) { return string; } ToBigMapKey(val) { return { key: { string: val }, type: { prim: StringToken.prim }, }; } findAndReturnTokens(tokenToFind, tokens) { if (StringToken.prim === tokenToFind) { tokens.push(this); } return tokens; } } StringToken.prim = 'string'; /** * @category Error * @description Error that indicates a failure happening when parsing encoding/executing an Address */ class AddressValidationError extends TokenValidationError { constructor(value, token, message) { super(value, token, message); this.value = value; this.token = token; this.name = 'AddressValidationError'; } } class AddressToken extends ComparableToken { constructor(val, idx, fac) { super(val, idx, fac); this.val = val; this.idx = idx; this.fac = fac; } ToBigMapKey(val) { const decoded = utils.b58decode(val); return { key: { bytes: decoded }, type: { prim: 'bytes' }, }; } /** * @throws {@link AddressValidationError} */ validate(value) { if (utils.validateAddress(value) !== utils.ValidationResult.VALID) { throw new AddressValidationError(value, this, `Address is not valid: ${JSON.stringify(value)}`); } } /** * @throws {@link AddressValidationError} */ Encode(args) { const val = args.pop(); this.validate(val); return { string: val }; } /** * @throws {@link AddressValidationError} */ EncodeObject(val, semantic) { this.validate(val); if (semantic && semantic[AddressToken.prim]) { return semantic[AddressToken.prim](val); } return { string: val }; } /** * @throws {@link AddressValidationError} */ Execute(val) { if (val.string) { return val.string; } if (!val.bytes) { throw new AddressValidationError(val, this, `cannot be missing both string and bytes: ${JSON.stringify(val)}`); } return utils.encodeAddress(val.bytes); } /** * @deprecated ExtractSchema has been deprecated in favor of generateSchema * */ ExtractSchema() { return AddressToken.prim; } generateSchema() { return { __michelsonType: AddressToken.prim, schema: AddressToken.prim, }; } /** * @throws {@link AddressValidationError} */ ToKey({ bytes, string }) { if (string) { return string; } if (!bytes) { throw new AddressValidationError({ bytes, string }, this, `cannot be missing both string and bytes ${JSON.stringify({ string, bytes })}`); } return utils.encodeAddress(bytes); } compare(address1, address2) { const isImplicit = (address) => { return address.startsWith('tz'); }; const implicit1 = isImplicit(address1); const implicit2 = isImplicit(address2); if (implicit1 && !implicit2) { return -1; } else if (implicit2 && !implicit1) { return 1; } return super.compare(address1, address2); } findAndReturnTokens(tokenToFind, tokens) { if (AddressToken.prim === tokenToFind) { tokens.push(this); } return tokens; } } AddressToken.prim = 'address'; /** * @category Error * @description Error that indicates a failure happening when parsing encoding/executing a Map */ class MapValidationError extends TokenValidationError { constructor(value, token, message) { super(value, token, message); this.value = value; this.token = token; this.name = 'MapValidationError'; } } class MapToken extends Token { constructor(val, idx, fac) { super(val, idx, fac); this.val = val; this.idx = idx; this.fac = fac; } get ValueSchema() { return this.createToken(this.val.args[1], 0); } get KeySchema() { return this.createToken(this.val.args[0], 0); } /** * @throws {@link MapValidationError} */ validate(value) { if (!MichelsonMap.isMichelsonMap(value)) { throw new MapValidationError(value, this, `Value ${JSON.stringify(value)} is not a valid MichelsonMap`); } } Execute(val, semantics) { const map = new MichelsonMap(this.val); val.forEach((current) => { map.set(this.KeySchema.ToKey(current.args[0]), this.ValueSchema.Execute(current.args[1], semantics)); }); return map; } objLitToMichelsonMap(val) { if (val instanceof MichelsonMap) return val; if (typeof val === 'object') { if (Object.keys(val).length === 0) { return new MichelsonMap(); } else { return MichelsonMap.fromLiteral(val); } } return val; } /** * @throws {@link MapValidationError} */ Encode(args) { const val = this.objLitToMichelsonMap(args.pop()); this.validate(val); return Array.from(val.keys()) .sort((a, b) => this.KeySchema.compare(a, b)) .map((key) => { return { prim: 'Elt', args: [this.KeySchema.EncodeObject(key), this.ValueSchema.EncodeObject(val.get(key))], }; }); } /** * @throws {@link MapValidationError} */ EncodeObject(args, semantic) { const val = this.objLitToMichelsonMap(args); this.validate(val); if (semantic && semantic[MapToken.prim]) { return semantic[MapToken.prim](val); } return Array.from(val.keys()) .sort((a, b) => this.KeySchema.compare(a, b)) .map((key) => { return { prim: 'Elt', args: [this.KeySchema.EncodeObject(key), this.ValueSchema.EncodeObject(val.get(key))], }; }); } /** * @deprecated ExtractSchema has been deprecated in favor of generateSchema * */ ExtractSchema() { return { map: { key: this.KeySchema.ExtractSchema(), value: this.ValueSchema.ExtractSchema(), }, }; } generateSchema() { return { __michelsonType: MapToken.prim, schema: { key: this.KeySchema.generateSchema(), value: this.ValueSchema.generateSchema(), }, }; } findAndReturnTokens(tokenToFind, tokens) { if (MapToken.prim === tokenToFind) { tokens.push(this); } this.KeySchema.findAndReturnTokens(tokenToFind, tokens); this.ValueSchema.findAndReturnTokens(tokenToFind, tokens); return tokens; } } MapToken.prim = 'map'; class BoolToken extends ComparableToken { constructor(val, idx, fac) { super(val, idx, fac); this.val = val; this.idx = idx; this.fac = fac; } Execute(val) { return String(val.prim).toLowerCase() === 'true' ? true : false; } Encode(args) { const val = args.pop(); return { prim: val ? 'True' : 'False' }; } EncodeObject(val, semantic) { if (semantic && semantic[BoolToken.prim]) { return semantic[BoolToken.prim](val); } return { prim: val ? 'True' : 'False' }; } /** * @deprecated ExtractSchema has been deprecated in favor of generateSchema * */ ExtractSchema() { return BoolToken.prim; } generateSchema() { return { __michelsonType: BoolToken.prim, schema: BoolToken.prim, }; } ToBigMapKey(val) { return { key: this.EncodeObject(val), type: { prim: BoolToken.prim }, }; } ToKey(val) { return this.EncodeObject(val); } compare(val1, val2) { if ((val1 && val2) || (!val1 && !val2)) { return 0; } else if (val1) { return 1; } else { return -1; } } findAndReturnTokens(tokenToFind, tokens) { if (BoolToken.prim === tokenToFind) { tokens.push(this); } return tokens; } } BoolToken.prim = 'bool'; /** * @category Error * @description Error that indicates a failure happening when parsing encoding/executing a Contract */ class ContractValidationError extends TokenValidationError { constructor(value, token, message) { super(value, token, message); this.value = value; this.token = token; this.name = 'ContractValidationError'; } } class ContractToken extends Token { constructor(val, idx, fac) { super(val, idx, fac); this.val = val; this.idx = idx; this.fac = fac; } /** * @throws {@link ContractValidationError} */ validate(value) { // tz1,tz2 and tz3 seems to be valid contract values (for Unit contract) if (utils.validateAddress(value) !== utils.ValidationResult.VALID) { throw new ContractValidationError(value, this, `Value ${JSON.stringify(value)} is not a valid contract address.`); } return null; } /** * @throws {@link ContractValidationError} */ Execute(val) { if (val.string) { return val.string; } if (!val.bytes) { throw new ContractValidationError(val, this, `Value ${JSON.stringify(val)} is not a valid contract address. must contain bytes or string.`); } return utils.encodeAddress(val.bytes); } /** * @throws {@link ContractValidationError} */ Encode(args) { const val = args.pop(); this.validate(val); return { string: val }; } /** * @throws {@link ContractValidationError} */ EncodeObject(val, semantic) { this.validate(val); if (semantic && semantic[ContractToken.prim]) { return sem