From 19adca7da23e6c8e707b4c27f82cec823ab6b91b Mon Sep 17 00:00:00 2001 From: hui-an-yang <106410553+hui-an-yang@users.noreply.github.com> Date: Tue, 28 May 2024 14:43:03 -0700 Subject: [PATCH] 2922 michel codec doc (#2972) * docs: adding michel-codec page to website * docs: address review comments * docs: addressed review comments --- docs/michel_codec.md | 123 ++++++++++++++++++ website/package-lock.json | 102 +++++++-------- website/sidebars.js | 1 + website/src/theme/CodeBlock/index.js | 4 +- .../version-19.2.0/michel_codec.md | 123 ++++++++++++++++++ .../version-19.2.0-sidebars.json | 1 + 6 files changed, 302 insertions(+), 52 deletions(-) create mode 100644 docs/michel_codec.md create mode 100644 website/versioned_docs/version-19.2.0/michel_codec.md diff --git a/docs/michel_codec.md b/docs/michel_codec.md new file mode 100644 index 0000000000..1deda205f2 --- /dev/null +++ b/docs/michel_codec.md @@ -0,0 +1,123 @@ +--- +title: Michel Codec +author: Hui-An Yang +--- + +The `taquito/michel-codec` package converts and validates Michelson expressions between JSON-based Michelson and Micheline. It also comes with various functions like `packData`, `packDataBytes`, `unpackData` and `unpackDataBytes` to serialize any value of packable type to its optimized binary representation locally and vice versa, like Michelson instructions `PACK` and `UNPACK`. + +## Parser class +To use the parser class, import and initialize it as follows. + +```ts +import { Parser } from '@taquito/michel-codec' +const p = new Parser() +``` +### Configuration +You can configure the parser class by passing `ParserOptions` through initialization with `expandMacros` and `expandGlobalConstant` properties. + +* `expandMacros` - defaults to true unless you don't want `Parser` class to expand them; you can pass `{ expandMacros: false }` to disable it. ref: Expand [Michelson macros](https://tezos.gitlab.io/whitedoc/michelson.html#macros) during parsing +* `expandGlobalConstant` - expects an object where the keys are global constant hashes and the values are the corresponding JSON Micheline expressions. + +for example + +```ts +import { Parser } from '@taquito/michel-codec' + +const parserOptions: ParserOptions = { + expandMacros: true, + expandGlobalConstant: { + 'expr...': { prim: 'DROP', args: [{ int: '2' }] } + } +} +const p = new Parser(parserOptions); +``` + +### parseJSON & emitMicheline - Parse JSON Michelson and convert it to Micheline +* `parseJSON` - takes a JSON-encoded Michelson, validates it, strips away unneeded properties and expands macros based on your configuration. +* `emitMicheline` takes a parsed JSON Michelson object and converts it to a Micheline expression with formatting options. + +```js live noInline +// import { Parser, emitMicheline } from '@taquito/michel-codec' + +const p = new Parser(); +Tezos.contract + .at("KT1BJadpDyLCACMH7Tt9xtpx4dQZVKw9cDF7") + .then(contract => { + const code = p.parseJSON(contract.script.code); + println("Pretty print Michelson smart contract:"); + println(emitMicheline(code, {indent:" ", newline: "\n",})); + + const storage = p.parseJSON(contract.script.storage); + println("Pretty print Storage:"); + println(emitMicheline(storage, {indent:" ", newline: "\n",})); + }) + .catch((error) => println(`Error: ${JSON.stringify(error, null, 2)}`)); +``` + +### parseMichelineExpression - Parse Micheline and convert it to JSON Michelson +Takes a Micheline expression in the form of script or data and converts it to JSON Michelson + +```js live noInline +// import { Parser } from '@taquito/michel-codec' + +const p = new Parser(); + +const michelineScript = `{parameter unit; storage unit; code {CDR; NIL operation; PAIR};}` +const script = p.parseMichelineExpression(michelineScript); +println('JSON Michelson script: ' + JSON.stringify(script) + '\n'); + +const michelineData = `(IF_LEFT { IF_LEFT { SWAP ; SUB } { ADD } })`; +const data = p.parseMichelineExpression(michelineData); +println('JSON Michelson data: ' + JSON.stringify(data)); +``` + +## PACK and UNPACK locally + +### packData & packDataBytes - Pack Michelson data +Serialize any value of packable type to its optimized binary representation identical to the one used by PACK Michelson instructions. +Without a type definition (not recommended), `packData` and `packDataBytes` will encode the data as a binary form of a generic Michelson expression. +Type definition allows types like `timestamp`, `address` and other base58 representable types to be encoded to corresponding optimized binary forms borrowed from the Tezos protocol. + +```ts +// import { packData, packDataBytes } from '@taquito/michel-codec' + +const data: MichelsonData = { string: 'KT1RvkwF4F7pz1gCoxkyZrG1RkrxQy3gmFTv%foo' }; +const typ: MichelsonType = { prim: 'address' }; + +const packed = packData(data, typ); +// 050a0000001901be41ee922ddd2cf33201e49d32da0afec571dce300666f6f + +const packedBytes = packDataBytes(data, typ); +// { bytes: "050a0000001901be41ee922ddd2cf33201e49d32da0afec571dce300666f6f" } +``` + +### unpackData & unpackDataBytes - Unpack Michelson data +Deserialize a byte array into its corresponding Michelson value. +Without a type definition (not recommended), the binary data will be treated as a binary form of a generic Michelson expression and returned as is. +Type definition allows some types, like `timestamp` and `address` and others, usually encoded in optimized binary forms, to be transformed back to their string representations like base58 and ISO timestamps. + +```ts +// import { unpackData, unpackDataBytes } from '@taquito/michel-codec' +const type: MichelsonType = { prim: 'timestamp' }; + +const src1 = [0x05, 0x00, 0xa7, 0xe8, 0xe4, 0xd8, 0x0b]; +const data1 = unpackData(src1, type); +// { string: "2019-09-26T10:59:51Z" } + +const src2 = { bytes: '0500a7e8e4d80b' }; +const data2 = unpackDataBytes(src2, type); +// { string: "2019-09-26T10:59:51Z" } +``` + +Alternatively, the same binary data without passing a type definition to `unpackData`, `unpackDataBytes` will not be deserialized correctly +```ts +// import { unpackData, unpackDataBytes } from '@taquito/michel-codec' + +const src1 = [0x05, 0x00, 0xa7, 0xe8, 0xe4, 0xd8, 0x0b]; +const data1 = unpackData(src1); +// { int: "1569495591" } + +const src2 = { bytes: '0500a7e8e4d80b' }; +const data2 = unpackDataBytes(src2); +// { int: "1569495591" } +``` diff --git a/website/package-lock.json b/website/package-lock.json index bfdbed87e5..b94521acff 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -69,17 +69,17 @@ }, "../packages/taquito": { "name": "@taquito/taquito", - "version": "19.2.1-beta.2", + "version": "19.2.1", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@taquito/core": "^19.2.1-beta.2", - "@taquito/http-utils": "^19.2.1-beta.2", - "@taquito/local-forging": "^19.2.1-beta.2", - "@taquito/michel-codec": "^19.2.1-beta.2", - "@taquito/michelson-encoder": "^19.2.1-beta.2", - "@taquito/rpc": "^19.2.1-beta.2", - "@taquito/utils": "^19.2.1-beta.2", + "@taquito/core": "^19.2.1", + "@taquito/http-utils": "^19.2.1", + "@taquito/local-forging": "^19.2.1", + "@taquito/michel-codec": "^19.2.1", + "@taquito/michelson-encoder": "^19.2.1", + "@taquito/rpc": "^19.2.1", + "@taquito/utils": "^19.2.1", "bignumber.js": "^9.1.2", "rxjs": "^7.8.1" }, @@ -127,12 +127,12 @@ }, "../packages/taquito-beacon-wallet": { "name": "@taquito/beacon-wallet", - "version": "19.2.1-beta.2", + "version": "19.2.1", "license": "Apache-2.0", "dependencies": { "@airgap/beacon-dapp": "^4.2.2", - "@taquito/core": "^19.2.1-beta.2", - "@taquito/taquito": "^19.2.1-beta.2" + "@taquito/core": "^19.2.1", + "@taquito/taquito": "^19.2.1" }, "devDependencies": { "@types/bluebird": "^3.5.40", @@ -172,7 +172,7 @@ }, "../packages/taquito-core": { "name": "@taquito/core", - "version": "19.2.1-beta.2", + "version": "19.2.1", "license": "Apache-2.0", "dependencies": { "json-stringify-safe": "^5.0.1" @@ -188,10 +188,10 @@ }, "../packages/taquito-http-utils": { "name": "@taquito/http-utils", - "version": "19.2.1-beta.2", + "version": "19.2.1", "license": "Apache-2.0", "dependencies": { - "@taquito/core": "^19.2.1-beta.2", + "@taquito/core": "^19.2.1", "node-fetch": "^2.7.0" }, "devDependencies": { @@ -229,14 +229,14 @@ }, "../packages/taquito-ledger-signer": { "name": "@taquito/ledger-signer", - "version": "19.2.1-beta.2", + "version": "19.2.1", "license": "Apache-2.0", "dependencies": { "@ledgerhq/hw-transport": "^6.30.5", "@stablelib/blake2b": "^1.0.1", - "@taquito/core": "^19.2.1-beta.2", - "@taquito/taquito": "^19.2.1-beta.2", - "@taquito/utils": "^19.2.1-beta.2", + "@taquito/core": "^19.2.1", + "@taquito/taquito": "^19.2.1", + "@taquito/utils": "^19.2.1", "buffer": "^6.0.3" }, "devDependencies": { @@ -273,10 +273,10 @@ }, "../packages/taquito-michel-codec": { "name": "@taquito/michel-codec", - "version": "19.2.1-beta.2", + "version": "19.2.1", "license": "MIT", "dependencies": { - "@taquito/core": "^19.2.1-beta.2" + "@taquito/core": "^19.2.1" }, "devDependencies": { "@types/bluebird": "^3.5.40", @@ -306,12 +306,12 @@ }, "../packages/taquito-michelson-encoder": { "name": "@taquito/michelson-encoder", - "version": "19.2.1-beta.2", + "version": "19.2.1", "license": "Apache-2.0", "dependencies": { - "@taquito/core": "^19.2.1-beta.2", - "@taquito/rpc": "^19.2.1-beta.2", - "@taquito/utils": "^19.2.1-beta.2", + "@taquito/core": "^19.2.1", + "@taquito/rpc": "^19.2.1", + "@taquito/utils": "^19.2.1", "bignumber.js": "^9.1.2", "fast-json-stable-stringify": "^2.1.0" }, @@ -348,15 +348,15 @@ }, "../packages/taquito-remote-signer": { "name": "@taquito/remote-signer", - "version": "19.2.1-beta.2", + "version": "19.2.1", "license": "Apache-2.0", "dependencies": { "@stablelib/blake2b": "^1.0.1", "@stablelib/ed25519": "^1.0.3", - "@taquito/core": "^19.2.1-beta.2", - "@taquito/http-utils": "^19.2.1-beta.2", - "@taquito/taquito": "^19.2.1-beta.2", - "@taquito/utils": "^19.2.1-beta.2", + "@taquito/core": "^19.2.1", + "@taquito/http-utils": "^19.2.1", + "@taquito/taquito": "^19.2.1", + "@taquito/utils": "^19.2.1", "typedarray-to-buffer": "^4.0.0" }, "devDependencies": { @@ -393,12 +393,12 @@ }, "../packages/taquito-rpc": { "name": "@taquito/rpc", - "version": "19.2.1-beta.2", + "version": "19.2.1", "license": "Apache-2.0", "dependencies": { - "@taquito/core": "^19.2.1-beta.2", - "@taquito/http-utils": "^19.2.1-beta.2", - "@taquito/utils": "^19.2.1-beta.2", + "@taquito/core": "^19.2.1", + "@taquito/http-utils": "^19.2.1", + "@taquito/utils": "^19.2.1", "bignumber.js": "^9.1.2" }, "devDependencies": { @@ -434,7 +434,7 @@ }, "../packages/taquito-signer": { "name": "@taquito/signer", - "version": "19.2.1-beta.2", + "version": "19.2.1", "license": "Apache-2.0", "dependencies": { "@stablelib/blake2b": "^1.0.1", @@ -443,9 +443,9 @@ "@stablelib/nacl": "^1.0.4", "@stablelib/pbkdf2": "^1.0.1", "@stablelib/sha512": "^1.0.1", - "@taquito/core": "^19.2.1-beta.2", - "@taquito/taquito": "^19.2.1-beta.2", - "@taquito/utils": "^19.2.1-beta.2", + "@taquito/core": "^19.2.1", + "@taquito/taquito": "^19.2.1", + "@taquito/utils": "^19.2.1", "@types/bn.js": "^5.1.2", "bip39": "3.1.0", "elliptic": "^6.5.4", @@ -488,13 +488,13 @@ }, "../packages/taquito-tzip12": { "name": "@taquito/tzip12", - "version": "19.2.1-beta.2", + "version": "19.2.1", "license": "Apache-2.0", "dependencies": { - "@taquito/core": "^19.2.1-beta.2", - "@taquito/michelson-encoder": "^19.2.1-beta.2", - "@taquito/taquito": "^19.2.1-beta.2", - "@taquito/tzip16": "^19.2.1-beta.2" + "@taquito/core": "^19.2.1", + "@taquito/michelson-encoder": "^19.2.1", + "@taquito/taquito": "^19.2.1", + "@taquito/tzip16": "^19.2.1" }, "devDependencies": { "@types/bluebird": "^3.5.40", @@ -530,15 +530,15 @@ }, "../packages/taquito-tzip16": { "name": "@taquito/tzip16", - "version": "19.2.1-beta.2", + "version": "19.2.1", "license": "Apache-2.0", "dependencies": { - "@taquito/core": "^19.2.1-beta.2", - "@taquito/http-utils": "^19.2.1-beta.2", - "@taquito/michelson-encoder": "^19.2.1-beta.2", - "@taquito/rpc": "^19.2.1-beta.2", - "@taquito/taquito": "^19.2.1-beta.2", - "@taquito/utils": "^19.2.1-beta.2", + "@taquito/core": "^19.2.1", + "@taquito/http-utils": "^19.2.1", + "@taquito/michelson-encoder": "^19.2.1", + "@taquito/rpc": "^19.2.1", + "@taquito/taquito": "^19.2.1", + "@taquito/utils": "^19.2.1", "bignumber.js": "^9.1.2", "crypto-js": "^4.2.0" }, @@ -577,12 +577,12 @@ }, "../packages/taquito-utils": { "name": "@taquito/utils", - "version": "19.2.1-beta.2", + "version": "19.2.1", "license": "Apache-2.0", "dependencies": { "@stablelib/blake2b": "^1.0.1", "@stablelib/ed25519": "^1.0.3", - "@taquito/core": "^19.2.1-beta.2", + "@taquito/core": "^19.2.1", "@types/bs58check": "^2.1.0", "bignumber.js": "^9.1.2", "blakejs": "^1.2.1", diff --git a/website/sidebars.js b/website/sidebars.js index 164d24d0d1..14e86b6717 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -141,6 +141,7 @@ const sidebars = { items: [ 'rpc_package', 'michelson_encoder', + 'michel_codec', 'contracts-library', 'timelock', 'taquito_utils', diff --git a/website/src/theme/CodeBlock/index.js b/website/src/theme/CodeBlock/index.js index d6d7492f5f..cd93b05172 100755 --- a/website/src/theme/CodeBlock/index.js +++ b/website/src/theme/CodeBlock/index.js @@ -84,7 +84,7 @@ export default ({ const { Tzip16Module, tzip16, MichelsonStorageView } = await import('@taquito/tzip16') const { Tzip12Module, tzip12 } = await import("@taquito/tzip12"); const { Schema, ParameterSchema } = await import("@taquito/michelson-encoder"); - const { Parser, packDataBytes } = await import('@taquito/michel-codec'); + const { Parser, packDataBytes, emitMicheline } = await import('@taquito/michel-codec'); const { RpcClient } = await import('@taquito/rpc'); const TransportWebHID = (await import("@ledgerhq/hw-transport-webhid")).default; @@ -132,6 +132,7 @@ export default ({ verifySignature, Parser, packDataBytes, + emitMicheline, RpcReadAdapter, RpcClient, Ed25519, @@ -188,6 +189,7 @@ export default ({ verifySignature: dependencies?.verifySignature, Parser: dependencies?.Parser, packDataBytes: dependencies?.packDataBytes, + emitMicheline: dependencies?.emitMicheline, RpcReadAdapter: dependencies?.RpcReadAdapter, RpcClient: dependencies?.RpcClient, InMemorySpendingKey: dependencies?.InMemorySpendingKey, diff --git a/website/versioned_docs/version-19.2.0/michel_codec.md b/website/versioned_docs/version-19.2.0/michel_codec.md new file mode 100644 index 0000000000..1deda205f2 --- /dev/null +++ b/website/versioned_docs/version-19.2.0/michel_codec.md @@ -0,0 +1,123 @@ +--- +title: Michel Codec +author: Hui-An Yang +--- + +The `taquito/michel-codec` package converts and validates Michelson expressions between JSON-based Michelson and Micheline. It also comes with various functions like `packData`, `packDataBytes`, `unpackData` and `unpackDataBytes` to serialize any value of packable type to its optimized binary representation locally and vice versa, like Michelson instructions `PACK` and `UNPACK`. + +## Parser class +To use the parser class, import and initialize it as follows. + +```ts +import { Parser } from '@taquito/michel-codec' +const p = new Parser() +``` +### Configuration +You can configure the parser class by passing `ParserOptions` through initialization with `expandMacros` and `expandGlobalConstant` properties. + +* `expandMacros` - defaults to true unless you don't want `Parser` class to expand them; you can pass `{ expandMacros: false }` to disable it. ref: Expand [Michelson macros](https://tezos.gitlab.io/whitedoc/michelson.html#macros) during parsing +* `expandGlobalConstant` - expects an object where the keys are global constant hashes and the values are the corresponding JSON Micheline expressions. + +for example + +```ts +import { Parser } from '@taquito/michel-codec' + +const parserOptions: ParserOptions = { + expandMacros: true, + expandGlobalConstant: { + 'expr...': { prim: 'DROP', args: [{ int: '2' }] } + } +} +const p = new Parser(parserOptions); +``` + +### parseJSON & emitMicheline - Parse JSON Michelson and convert it to Micheline +* `parseJSON` - takes a JSON-encoded Michelson, validates it, strips away unneeded properties and expands macros based on your configuration. +* `emitMicheline` takes a parsed JSON Michelson object and converts it to a Micheline expression with formatting options. + +```js live noInline +// import { Parser, emitMicheline } from '@taquito/michel-codec' + +const p = new Parser(); +Tezos.contract + .at("KT1BJadpDyLCACMH7Tt9xtpx4dQZVKw9cDF7") + .then(contract => { + const code = p.parseJSON(contract.script.code); + println("Pretty print Michelson smart contract:"); + println(emitMicheline(code, {indent:" ", newline: "\n",})); + + const storage = p.parseJSON(contract.script.storage); + println("Pretty print Storage:"); + println(emitMicheline(storage, {indent:" ", newline: "\n",})); + }) + .catch((error) => println(`Error: ${JSON.stringify(error, null, 2)}`)); +``` + +### parseMichelineExpression - Parse Micheline and convert it to JSON Michelson +Takes a Micheline expression in the form of script or data and converts it to JSON Michelson + +```js live noInline +// import { Parser } from '@taquito/michel-codec' + +const p = new Parser(); + +const michelineScript = `{parameter unit; storage unit; code {CDR; NIL operation; PAIR};}` +const script = p.parseMichelineExpression(michelineScript); +println('JSON Michelson script: ' + JSON.stringify(script) + '\n'); + +const michelineData = `(IF_LEFT { IF_LEFT { SWAP ; SUB } { ADD } })`; +const data = p.parseMichelineExpression(michelineData); +println('JSON Michelson data: ' + JSON.stringify(data)); +``` + +## PACK and UNPACK locally + +### packData & packDataBytes - Pack Michelson data +Serialize any value of packable type to its optimized binary representation identical to the one used by PACK Michelson instructions. +Without a type definition (not recommended), `packData` and `packDataBytes` will encode the data as a binary form of a generic Michelson expression. +Type definition allows types like `timestamp`, `address` and other base58 representable types to be encoded to corresponding optimized binary forms borrowed from the Tezos protocol. + +```ts +// import { packData, packDataBytes } from '@taquito/michel-codec' + +const data: MichelsonData = { string: 'KT1RvkwF4F7pz1gCoxkyZrG1RkrxQy3gmFTv%foo' }; +const typ: MichelsonType = { prim: 'address' }; + +const packed = packData(data, typ); +// 050a0000001901be41ee922ddd2cf33201e49d32da0afec571dce300666f6f + +const packedBytes = packDataBytes(data, typ); +// { bytes: "050a0000001901be41ee922ddd2cf33201e49d32da0afec571dce300666f6f" } +``` + +### unpackData & unpackDataBytes - Unpack Michelson data +Deserialize a byte array into its corresponding Michelson value. +Without a type definition (not recommended), the binary data will be treated as a binary form of a generic Michelson expression and returned as is. +Type definition allows some types, like `timestamp` and `address` and others, usually encoded in optimized binary forms, to be transformed back to their string representations like base58 and ISO timestamps. + +```ts +// import { unpackData, unpackDataBytes } from '@taquito/michel-codec' +const type: MichelsonType = { prim: 'timestamp' }; + +const src1 = [0x05, 0x00, 0xa7, 0xe8, 0xe4, 0xd8, 0x0b]; +const data1 = unpackData(src1, type); +// { string: "2019-09-26T10:59:51Z" } + +const src2 = { bytes: '0500a7e8e4d80b' }; +const data2 = unpackDataBytes(src2, type); +// { string: "2019-09-26T10:59:51Z" } +``` + +Alternatively, the same binary data without passing a type definition to `unpackData`, `unpackDataBytes` will not be deserialized correctly +```ts +// import { unpackData, unpackDataBytes } from '@taquito/michel-codec' + +const src1 = [0x05, 0x00, 0xa7, 0xe8, 0xe4, 0xd8, 0x0b]; +const data1 = unpackData(src1); +// { int: "1569495591" } + +const src2 = { bytes: '0500a7e8e4d80b' }; +const data2 = unpackDataBytes(src2); +// { int: "1569495591" } +``` diff --git a/website/versioned_sidebars/version-19.2.0-sidebars.json b/website/versioned_sidebars/version-19.2.0-sidebars.json index dffc42746d..6c70b912cf 100644 --- a/website/versioned_sidebars/version-19.2.0-sidebars.json +++ b/website/versioned_sidebars/version-19.2.0-sidebars.json @@ -125,6 +125,7 @@ "items": [ "rpc_package", "michelson_encoder", + "michel_codec", "contracts-library", "timelock", "taquito_utils",