@taquito/michelson-encoder
Version:
converts michelson data and types into convenient JS/TS objects
266 lines (265 loc) • 10.3 kB
JavaScript
"use strict";
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Schema = void 0;
exports.deepEqual = deepEqual;
const bigmap_1 = require("../tokens/bigmap");
const createToken_1 = require("../tokens/createToken");
const map_1 = require("../tokens/map");
const or_1 = require("../tokens/or");
const pair_1 = require("../tokens/pair");
const ticket_1 = require("../tokens/ticket");
const ticket_deprecated_1 = require("../tokens/ticket-deprecated");
const token_1 = require("../tokens/token");
const errors_1 = require("./errors");
const schemaTypeSymbol = Symbol.for('taquito-schema-type-symbol');
// collapse comb pair
function collapse(val, prim = pair_1.PairToken.prim) {
var _b, _c;
if (Array.isArray(val)) {
return collapse({
prim: prim,
args: val,
}, prim);
}
const extended = val;
if (extended.prim === prim && extended.args && extended.args.length > 2) {
return Object.assign(Object.assign({}, extended), { args: [
(_b = extended.args) === null || _b === void 0 ? void 0 : _b[0],
{
prim: prim,
args: (_c = extended.args) === null || _c === void 0 ? void 0 : _c.slice(1),
},
] });
}
return extended;
}
function deepEqual(a, b) {
if (a === undefined || b === undefined) {
return a === b;
}
const ac = collapse(a);
const bc = collapse(b);
return (ac.prim === bc.prim &&
((ac.args === undefined && bc.args === undefined) ||
(ac.args !== undefined &&
bc.args !== undefined &&
ac.args.length === bc.args.length &&
ac.args.every((v, i) => { var _b, _c; return deepEqual(v, (_c = (_b = bc.args) === null || _b === void 0 ? void 0 : _b[i]) !== null && _c !== void 0 ? _c : {}); }))) &&
((ac.annots === undefined && bc.annots === undefined) ||
(ac.annots !== undefined &&
bc.annots !== undefined &&
ac.annots.length === bc.annots.length &&
ac.annots.every((v, i) => { var _b; return v === ((_b = bc.annots) === null || _b === void 0 ? void 0 : _b[i]); }))));
}
/**
* @warn Our current smart contract abstraction feature is currently in preview. Its API is not final, and it may not cover every use case (yet). We will greatly appreciate any feedback on this feature.
*/
class Schema {
static isSchema(obj) {
return obj && obj[schemaTypeSymbol] === true;
}
/**
* @throws {@link InvalidRpcResponseError}
*/
static fromRPCResponse(val) {
if (!val) {
throw new errors_1.InvalidRpcResponseError(val, 'the RPC response is empty');
}
if (!val.script) {
throw new errors_1.InvalidRpcResponseError(val, 'the RPC response has no script');
}
if (!Array.isArray(val.script.code)) {
throw new errors_1.InvalidRpcResponseError(val, 'The response.script.code should be an array');
}
let code = val.script.code;
while (code.length === 1 && Array.isArray(code[0])) {
code = code[0];
}
const storage = code.find((x) => 'prim' in x && x.prim === 'storage');
if (!storage || !Array.isArray(storage.args)) {
throw new errors_1.InvalidRpcResponseError(val, 'The response.script.code has an element of type {prim: "storage"}, but its args is not an array');
}
return new Schema(storage.args[0]);
}
isExpressionExtended(val) {
return 'prim' in val && Array.isArray(val.args);
}
constructor(val) {
this.val = val;
this[_a] = true;
this.root = (0, createToken_1.createToken)(val, 0);
if (this.root instanceof bigmap_1.BigMapToken) {
this.bigMap = this.root;
}
else if (this.isExpressionExtended(val) && val.prim === 'pair') {
const exp = val.args[0];
if (this.isExpressionExtended(exp) && exp.prim === 'big_map') {
this.bigMap = new bigmap_1.BigMapToken(exp, 0, createToken_1.createToken);
}
}
}
removeTopLevelAnnotation(obj) {
// PairToken and OrToken can have redundant top level annotation in their storage
if (this.root instanceof pair_1.PairToken || this.root instanceof or_1.OrToken) {
if (this.root.hasAnnotations() && typeof obj === 'object' && Object.keys(obj).length === 1) {
return obj[Object.keys(obj)[0]];
}
}
return obj;
}
Execute(val, semantics) {
const storage = this.root.Execute(val, semantics);
return this.removeTopLevelAnnotation(storage);
}
Typecheck(val) {
if (this.root instanceof bigmap_1.BigMapToken && Number.isInteger(Number(val))) {
return;
}
if (this.root instanceof ticket_1.TicketToken && val.ticketer && val.value && val.amount) {
return;
}
if (this.root instanceof ticket_deprecated_1.TicketDeprecatedToken && val.ticketer && val.value && val.amount) {
return;
}
if (this.root instanceof map_1.MapToken && this.root.ValueSchema instanceof bigmap_1.BigMapToken) {
return;
}
this.root.EncodeObject(val);
}
/**
* @throws {@link InvalidBigMapSchemaError}
* @throws {@link InvalidBigMapDiffError}
*/
ExecuteOnBigMapDiff(diff, semantics) {
if (!this.bigMap) {
throw new errors_1.InvalidBigMapSchemaError('Big map schema is undefined');
}
if (!Array.isArray(diff)) {
throw new errors_1.InvalidBigMapDiffError(`Big map diff must be an array, got: ${JSON.stringify(diff)}`, diff);
}
const eltFormat = diff.map(({ key, value }) => ({ args: [key, value] }));
return this.bigMap.Execute(eltFormat, semantics);
}
/**
* @throws {@link InvalidBigMapSchemaError}
*/
ExecuteOnBigMapValue(key, semantics) {
if (!this.bigMap) {
throw new errors_1.InvalidBigMapSchemaError('Big map schema is undefined');
}
return this.bigMap.ValueSchema.Execute(key, semantics);
}
/**
* @throws {@link InvalidBigMapSchemaError}
* @throws {@link BigMapEncodingError}
*/
EncodeBigMapKey(key) {
if (!this.bigMap) {
throw new errors_1.InvalidBigMapSchemaError('Big map schema is undefined');
}
try {
return this.bigMap.KeySchema.ToBigMapKey(key);
}
catch (ex) {
throw new errors_1.BigMapEncodingError('key', ex, this.bigMap.KeySchema, key);
}
}
/**
* @throws {@link TokenValidationError}
* @throws {@link StorageEncodingError}
*/
Encode(value, semantics) {
try {
return this.root.EncodeObject(value, semantics);
}
catch (ex) {
if (ex instanceof token_1.TokenValidationError) {
throw ex;
}
throw new errors_1.StorageEncodingError('storage object', ex, this.root, value, semantics);
}
}
/**
* @deprecated ExtractSchema has been deprecated in favor of generateSchema
*
*/
ExtractSchema() {
return this.removeTopLevelAnnotation(this.root.ExtractSchema());
}
/**
* @description Produce a representation of the storage schema.
* Note: Provide guidance on how to write the storage object for the origination operation with Taquito.
*/
generateSchema() {
return this.removeTopLevelAnnotation(this.root.generateSchema());
}
/**
* @deprecated
* @throws {@link InvalidBigMapSchemaError}
*/
ComputeState(tx, state) {
if (!this.bigMap) {
throw new errors_1.InvalidBigMapSchemaError('Big map schema is undefined');
}
const bigMap = tx.reduce((prev, current) => {
return Object.assign(Object.assign({}, prev), this.ExecuteOnBigMapDiff(current.contents[0].metadata.operation_result.big_map_diff));
}, {});
return Object.assign(Object.assign({}, this.Execute(state)), { [this.bigMap.annot()]: bigMap });
}
/**
* @description Look up in top-level pairs of the storage to find a value matching the specified type
*
* @returns The first value found that match the type or `undefined` if no value is found
*
* @param storage storage to parse to find the value
* @param valueType type of value to look for
*
*/
FindFirstInTopLevelPair(storage, valueType) {
return this.findValue(this.root['val'], storage, valueType);
}
// TODO check these type casts
/**
* @throws {@link MissingArgumentError}
*/
findValue(schema, storage, valueToFind) {
if (deepEqual(valueToFind, schema)) {
return storage;
}
if (Array.isArray(schema) || schema.prim === 'pair') {
const sch = collapse(schema);
const strg = collapse(storage, 'Pair');
if (sch.args === undefined || strg.args === undefined) {
throw new errors_1.MissingArgumentError('Tokens have no arguments'); // unlikely
}
if (sch.args[0])
return (
// unsafe
this.findValue(sch.args[0], strg.args[0], valueToFind) ||
this.findValue(sch.args[1], strg.args[1], valueToFind));
}
}
/**
* @description Look up the schema to find any occurrence of a particular token.
*
* @returns an array of tokens of the specified kind or an empty array if no token was found
*
* @param tokenToFind string representing the prim property of the token to find
*
* @example
* ```
* Useful to find all global constants in a script, an array of GlobalConstantToken is returned:
*
* const schema = new Schema(script);
* const allGlobalConstantTokens = schema.findToken('constant');
* ```
*
*/
findToken(tokenToFind) {
const tokens = [];
return this.root.findAndReturnTokens(tokenToFind, tokens);
}
}
exports.Schema = Schema;
_a = schemaTypeSymbol;