From 83c874ca777b9c048ce55e2b37c3aa71ab8f9060 Mon Sep 17 00:00:00 2001 From: Nikolai Dorofeev Date: Sat, 22 Jun 2024 15:09:00 +0400 Subject: [PATCH 01/13] Exctract paser methods --- src/client/TonClient.ts | 1 + src/parser/index.ts | 12 ++++++++++++ src/parser/parseObject.ts | 22 ++++++++++++++++++++++ src/parser/parseStackItem.ts | 25 +++++++++++++++++++++++++ 4 files changed, 60 insertions(+) create mode 100644 src/parser/index.ts create mode 100644 src/parser/parseObject.ts create mode 100644 src/parser/parseStackItem.ts diff --git a/src/client/TonClient.ts b/src/client/TonClient.ts index 34cc3af2..86071993 100644 --- a/src/client/TonClient.ts +++ b/src/client/TonClient.ts @@ -29,6 +29,7 @@ import { OpenedContract } from '@ton/core'; import { Maybe } from "../utils/maybe"; +import { parseStack } from "../parser"; export type TonClientParameters = { /** diff --git a/src/parser/index.ts b/src/parser/index.ts new file mode 100644 index 00000000..3463564e --- /dev/null +++ b/src/parser/index.ts @@ -0,0 +1,12 @@ +import { TupleItem, TupleReader } from '@ton/core'; +import { parseStackItem } from './parseStackItem'; + +export function parseStack(src: any[]) { + let stack: TupleItem[] = []; + + for (let s of src) { + stack.push(parseStackItem(s)); + } + + return new TupleReader(stack); +} \ No newline at end of file diff --git a/src/parser/parseObject.ts b/src/parser/parseObject.ts new file mode 100644 index 00000000..0889d2d6 --- /dev/null +++ b/src/parser/parseObject.ts @@ -0,0 +1,22 @@ +import { Cell } from '@ton/core'; + +export function parseObject(x: any): any { + const typeName = x['@type']; + switch(typeName) { + case 'tvm.list': + case 'tvm.tuple': + return x.elements.map(parseObject); + case 'tvm.cell': + return Cell.fromBoc(Buffer.from(x.bytes, 'base64'))[0]; + case 'tvm.stackEntryCell': + return parseObject(x.cell); + case 'tvm.stackEntryTuple': + return parseObject(x.tuple); + case 'tvm.stackEntryNumber': + return parseObject(x.number); + case 'tvm.numberDecimal': + return BigInt(x.number); + default: + throw Error('Unsupported item type: ' + typeName); + } +} \ No newline at end of file diff --git a/src/parser/parseStackItem.ts b/src/parser/parseStackItem.ts new file mode 100644 index 00000000..e071783f --- /dev/null +++ b/src/parser/parseStackItem.ts @@ -0,0 +1,25 @@ +import { Cell, TupleItem } from '@ton/core'; +import { parseObject } from './parseObject'; + +export function parseStackItem(s: any): TupleItem { + if (s[0] === 'num') { + let val = s[1] as string; + if (val.startsWith('-')) { + return { type: 'int', value: -BigInt(val.slice(1)) }; + } else { + return { type: 'int', value: BigInt(val) }; + } + } else if (s[0] === 'null') { + return { type: 'null' }; + } else if (s[0] === 'cell') { + return { type: 'cell', cell: Cell.fromBoc(Buffer.from(s[1].bytes, 'base64'))[0] }; + } else if (s[0] === 'slice') { + return { type: 'slice', cell: Cell.fromBoc(Buffer.from(s[1].bytes, 'base64'))[0] }; + } else if (s[0] === 'builder') { + return { type: 'builder', cell: Cell.fromBoc(Buffer.from(s[1].bytes, 'base64'))[0] }; + } else if (s[0] === 'tuple' || s[0] === 'list') { + return { type: 'tuple', items: s[1].elements.map(parseObject) }; + } else { + throw Error('Unsupported stack item type: ' + s[0]) + } +} \ No newline at end of file From bb86be20a7f2d1115aeceb01d8a8dbdd77cefcfc Mon Sep 17 00:00:00 2001 From: Nikolai Dorofeev Date: Sat, 22 Jun 2024 16:06:21 +0400 Subject: [PATCH 02/13] Cover with types --- src/parser/parseObject.ts | 20 +++++++++++++++----- src/parser/parseStackItem.ts | 3 ++- src/parser/types.ts | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 src/parser/types.ts diff --git a/src/parser/parseObject.ts b/src/parser/parseObject.ts index 0889d2d6..317d29bd 100644 --- a/src/parser/parseObject.ts +++ b/src/parser/parseObject.ts @@ -1,13 +1,20 @@ -import { Cell } from '@ton/core'; +import { Cell, type TupleItem } from '@ton/core'; +import type { TvmType } from './types'; -export function parseObject(x: any): any { +export function parseObject(x: TvmType): TupleItem { const typeName = x['@type']; switch(typeName) { case 'tvm.list': case 'tvm.tuple': - return x.elements.map(parseObject); + return { + type: 'tuple', + items: x.elements.map(parseObject) + }; case 'tvm.cell': - return Cell.fromBoc(Buffer.from(x.bytes, 'base64'))[0]; + return { + type: 'cell', + cell: Cell.fromBoc(Buffer.from(x.bytes, 'base64'))[0] + }; case 'tvm.stackEntryCell': return parseObject(x.cell); case 'tvm.stackEntryTuple': @@ -15,7 +22,10 @@ export function parseObject(x: any): any { case 'tvm.stackEntryNumber': return parseObject(x.number); case 'tvm.numberDecimal': - return BigInt(x.number); + return { + type: 'int', + value: BigInt(x.number) + }; default: throw Error('Unsupported item type: ' + typeName); } diff --git a/src/parser/parseStackItem.ts b/src/parser/parseStackItem.ts index e071783f..fe7cf8ff 100644 --- a/src/parser/parseStackItem.ts +++ b/src/parser/parseStackItem.ts @@ -1,7 +1,8 @@ import { Cell, TupleItem } from '@ton/core'; +import { StackItem } from './types'; import { parseObject } from './parseObject'; -export function parseStackItem(s: any): TupleItem { +export function parseStackItem(s: StackItem): TupleItem { if (s[0] === 'num') { let val = s[1] as string; if (val.startsWith('-')) { diff --git a/src/parser/types.ts b/src/parser/types.ts new file mode 100644 index 00000000..10360dd4 --- /dev/null +++ b/src/parser/types.ts @@ -0,0 +1,32 @@ +type TvmNumberDecimal = { '@type': 'tvm.numberDecimal', number: string }; +type TvmStackEntryNumber = { '@type': 'tvm.stackEntryNumber', number: TvmNumberDecimal }; + +type TvmSlice = { '@type': 'tvm.slice', bytes: string }; +type TvmStackEntrySlice = { '@type': 'tvm.stackEntrySlice', slice: TvmSlice }; + +type TvmCell = { '@type': 'tvm.cell', bytes: string }; +type TvmStackEntryCell = { '@type': 'tvm.stackEntryCell', cell: TvmCell }; + +export type TvmType = TvmNumberDecimal | TvmCell | TvmSlice | TvmList | TvmTuple + | TvmStackEntryCell | TvmStackEntryNumber | TvmStackEntrySlice | TvmStackEntryTuple; + +// TODO: It doesn't seem lists are used +type TvmList = { '@type': 'tvm.list', elements: any[] }; + +type TvmTuple = { '@type': 'tvm.tuple', elements: TvmType[] }; +type TvmStackEntryTuple = { '@type': 'tvm.stackEntryTuple', tuple: TvmTuple }; + +type SerializedCell = { bytes: string }; + +type NullStackItem = ['null']; +type NumStackItem = ['num', string]; +type CellStackItem = ['cell', SerializedCell]; +type SliceStackItem = ['slice', SerializedCell]; +type BuilderStackItem = ['builder', SerializedCell]; + +type TupleStackItem = ['tuple', TvmTuple]; +type ListStackItem = ['list', TvmList]; + +export type StackItem = NullStackItem | NumStackItem + | CellStackItem | SliceStackItem | BuilderStackItem + | TupleStackItem | ListStackItem; \ No newline at end of file From 7d04192ec6741342cd4c4f0615dc262e66bace9e Mon Sep 17 00:00:00 2001 From: Nikolai Dorofeev Date: Sat, 22 Jun 2024 16:08:41 +0400 Subject: [PATCH 03/13] Add slice support --- src/parser/parseObject.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/parser/parseObject.ts b/src/parser/parseObject.ts index 317d29bd..5f92642b 100644 --- a/src/parser/parseObject.ts +++ b/src/parser/parseObject.ts @@ -26,6 +26,13 @@ export function parseObject(x: TvmType): TupleItem { type: 'int', value: BigInt(x.number) }; + case 'tvm.slice': + return { + type: 'slice', + cell: Cell.fromBoc(Buffer.from(x.bytes, 'base64'))[0] + }; + case 'tvm.stackEntrySlice': + return parseObject(x.slice); default: throw Error('Unsupported item type: ' + typeName); } From 8d2e09c909d70759c1d12a0bb1fa764c4ec081a3 Mon Sep 17 00:00:00 2001 From: Nikolai Dorofeev Date: Sun, 23 Jun 2024 08:31:50 +0400 Subject: [PATCH 04/13] Rename TvmType --- src/parser/parseObject.ts | 4 ++-- src/parser/types.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/parser/parseObject.ts b/src/parser/parseObject.ts index 5f92642b..738510f3 100644 --- a/src/parser/parseObject.ts +++ b/src/parser/parseObject.ts @@ -1,7 +1,7 @@ import { Cell, type TupleItem } from '@ton/core'; -import type { TvmType } from './types'; +import type { TvmValue } from './types'; -export function parseObject(x: TvmType): TupleItem { +export function parseObject(x: TvmValue): TupleItem { const typeName = x['@type']; switch(typeName) { case 'tvm.list': diff --git a/src/parser/types.ts b/src/parser/types.ts index 10360dd4..fb66bcf5 100644 --- a/src/parser/types.ts +++ b/src/parser/types.ts @@ -7,13 +7,13 @@ type TvmStackEntrySlice = { '@type': 'tvm.stackEntrySlice', slice: TvmSlice }; type TvmCell = { '@type': 'tvm.cell', bytes: string }; type TvmStackEntryCell = { '@type': 'tvm.stackEntryCell', cell: TvmCell }; -export type TvmType = TvmNumberDecimal | TvmCell | TvmSlice | TvmList | TvmTuple +export type TvmValue = TvmNumberDecimal | TvmCell | TvmSlice | TvmList | TvmTuple | TvmStackEntryCell | TvmStackEntryNumber | TvmStackEntrySlice | TvmStackEntryTuple; // TODO: It doesn't seem lists are used type TvmList = { '@type': 'tvm.list', elements: any[] }; -type TvmTuple = { '@type': 'tvm.tuple', elements: TvmType[] }; +type TvmTuple = { '@type': 'tvm.tuple', elements: TvmValue[] }; type TvmStackEntryTuple = { '@type': 'tvm.stackEntryTuple', tuple: TvmTuple }; type SerializedCell = { bytes: string }; From 46f11a35ccf77ba77306b1ca75249eacbdc70867 Mon Sep 17 00:00:00 2001 From: Nikolai Dorofeev Date: Sun, 23 Jun 2024 08:51:53 +0400 Subject: [PATCH 05/13] Regroup types --- src/parser/index.ts | 7 ++++--- src/parser/parseObject.ts | 2 +- src/parser/parseStackItem.ts | 2 +- src/parser/types.ts | 32 -------------------------------- src/parser/types/index.ts | 2 ++ src/parser/types/stackItems.ts | 16 ++++++++++++++++ src/parser/types/tvmValues.ts | 21 +++++++++++++++++++++ 7 files changed, 45 insertions(+), 37 deletions(-) delete mode 100644 src/parser/types.ts create mode 100644 src/parser/types/index.ts create mode 100644 src/parser/types/stackItems.ts create mode 100644 src/parser/types/tvmValues.ts diff --git a/src/parser/index.ts b/src/parser/index.ts index 3463564e..7dda24c8 100644 --- a/src/parser/index.ts +++ b/src/parser/index.ts @@ -1,12 +1,13 @@ import { TupleItem, TupleReader } from '@ton/core'; +import type { StackItem } from './types'; import { parseStackItem } from './parseStackItem'; -export function parseStack(src: any[]) { +export function parseStack(src: unknown[]) { let stack: TupleItem[] = []; for (let s of src) { - stack.push(parseStackItem(s)); + stack.push(parseStackItem(s as StackItem)); } return new TupleReader(stack); -} \ No newline at end of file +} diff --git a/src/parser/parseObject.ts b/src/parser/parseObject.ts index 738510f3..399a31fa 100644 --- a/src/parser/parseObject.ts +++ b/src/parser/parseObject.ts @@ -36,4 +36,4 @@ export function parseObject(x: TvmValue): TupleItem { default: throw Error('Unsupported item type: ' + typeName); } -} \ No newline at end of file +} diff --git a/src/parser/parseStackItem.ts b/src/parser/parseStackItem.ts index fe7cf8ff..27630931 100644 --- a/src/parser/parseStackItem.ts +++ b/src/parser/parseStackItem.ts @@ -23,4 +23,4 @@ export function parseStackItem(s: StackItem): TupleItem { } else { throw Error('Unsupported stack item type: ' + s[0]) } -} \ No newline at end of file +} diff --git a/src/parser/types.ts b/src/parser/types.ts deleted file mode 100644 index fb66bcf5..00000000 --- a/src/parser/types.ts +++ /dev/null @@ -1,32 +0,0 @@ -type TvmNumberDecimal = { '@type': 'tvm.numberDecimal', number: string }; -type TvmStackEntryNumber = { '@type': 'tvm.stackEntryNumber', number: TvmNumberDecimal }; - -type TvmSlice = { '@type': 'tvm.slice', bytes: string }; -type TvmStackEntrySlice = { '@type': 'tvm.stackEntrySlice', slice: TvmSlice }; - -type TvmCell = { '@type': 'tvm.cell', bytes: string }; -type TvmStackEntryCell = { '@type': 'tvm.stackEntryCell', cell: TvmCell }; - -export type TvmValue = TvmNumberDecimal | TvmCell | TvmSlice | TvmList | TvmTuple - | TvmStackEntryCell | TvmStackEntryNumber | TvmStackEntrySlice | TvmStackEntryTuple; - -// TODO: It doesn't seem lists are used -type TvmList = { '@type': 'tvm.list', elements: any[] }; - -type TvmTuple = { '@type': 'tvm.tuple', elements: TvmValue[] }; -type TvmStackEntryTuple = { '@type': 'tvm.stackEntryTuple', tuple: TvmTuple }; - -type SerializedCell = { bytes: string }; - -type NullStackItem = ['null']; -type NumStackItem = ['num', string]; -type CellStackItem = ['cell', SerializedCell]; -type SliceStackItem = ['slice', SerializedCell]; -type BuilderStackItem = ['builder', SerializedCell]; - -type TupleStackItem = ['tuple', TvmTuple]; -type ListStackItem = ['list', TvmList]; - -export type StackItem = NullStackItem | NumStackItem - | CellStackItem | SliceStackItem | BuilderStackItem - | TupleStackItem | ListStackItem; \ No newline at end of file diff --git a/src/parser/types/index.ts b/src/parser/types/index.ts new file mode 100644 index 00000000..1c337914 --- /dev/null +++ b/src/parser/types/index.ts @@ -0,0 +1,2 @@ +export * from './stackItems'; +export * from './tvmValues'; \ No newline at end of file diff --git a/src/parser/types/stackItems.ts b/src/parser/types/stackItems.ts new file mode 100644 index 00000000..52a9db33 --- /dev/null +++ b/src/parser/types/stackItems.ts @@ -0,0 +1,16 @@ +import type { TvmTuple, TvmList } from './tvmValues'; + +type SerializedCell = { bytes: string }; + +type NullStackItem = ['null']; +type NumStackItem = ['num', string]; +type CellStackItem = ['cell', SerializedCell]; +type SliceStackItem = ['slice', SerializedCell]; +type BuilderStackItem = ['builder', SerializedCell]; + +type TupleStackItem = ['tuple', TvmTuple]; +type ListStackItem = ['list', TvmList]; + +export type StackItem = NullStackItem | NumStackItem + | CellStackItem | SliceStackItem | BuilderStackItem + | TupleStackItem | ListStackItem; \ No newline at end of file diff --git a/src/parser/types/tvmValues.ts b/src/parser/types/tvmValues.ts new file mode 100644 index 00000000..394811aa --- /dev/null +++ b/src/parser/types/tvmValues.ts @@ -0,0 +1,21 @@ +// Plain types for TON Virtual Machine values +type TvmNumberDecimal = { '@type': 'tvm.numberDecimal', number: string }; +type TvmStackEntryNumber = { '@type': 'tvm.stackEntryNumber', number: TvmNumberDecimal }; + +type TvmSlice = { '@type': 'tvm.slice', bytes: string }; +type TvmStackEntrySlice = { '@type': 'tvm.stackEntrySlice', slice: TvmSlice }; + +type TvmCell = { '@type': 'tvm.cell', bytes: string }; +type TvmStackEntryCell = { '@type': 'tvm.stackEntryCell', cell: TvmCell }; + +// Structured types for TON Virtual Machine values +export type TvmList = { '@type': 'tvm.list', elements: TvmValue[] }; +type TvmStackEntryList = { '@type': 'tvm.stackEntryList', list: TvmList }; + +export type TvmTuple = { '@type': 'tvm.tuple', elements: TvmValue[] }; +type TvmStackEntryTuple = { '@type': 'tvm.stackEntryTuple', tuple: TvmTuple }; + +// Union of all TON Virtual Machine values +type TvmCommonValue = TvmNumberDecimal | TvmCell | TvmSlice | TvmList | TvmTuple; +type TvmStackEntryValue = TvmStackEntryCell | TvmStackEntryNumber | TvmStackEntrySlice | TvmStackEntryList | TvmStackEntryTuple; +export type TvmValue = TvmCommonValue | TvmStackEntryValue; From b87c3a99684ed4b5efca2a43bfbd1cb222b0e6e2 Mon Sep 17 00:00:00 2001 From: Nikolai Dorofeev Date: Sun, 23 Jun 2024 08:54:49 +0400 Subject: [PATCH 06/13] Format parseObject --- src/parser/parseObject.ts | 39 ------------------------------------ src/parser/parseStackItem.ts | 4 ++-- src/parser/parseTvmValue.ts | 27 +++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 41 deletions(-) delete mode 100644 src/parser/parseObject.ts create mode 100644 src/parser/parseTvmValue.ts diff --git a/src/parser/parseObject.ts b/src/parser/parseObject.ts deleted file mode 100644 index 399a31fa..00000000 --- a/src/parser/parseObject.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Cell, type TupleItem } from '@ton/core'; -import type { TvmValue } from './types'; - -export function parseObject(x: TvmValue): TupleItem { - const typeName = x['@type']; - switch(typeName) { - case 'tvm.list': - case 'tvm.tuple': - return { - type: 'tuple', - items: x.elements.map(parseObject) - }; - case 'tvm.cell': - return { - type: 'cell', - cell: Cell.fromBoc(Buffer.from(x.bytes, 'base64'))[0] - }; - case 'tvm.stackEntryCell': - return parseObject(x.cell); - case 'tvm.stackEntryTuple': - return parseObject(x.tuple); - case 'tvm.stackEntryNumber': - return parseObject(x.number); - case 'tvm.numberDecimal': - return { - type: 'int', - value: BigInt(x.number) - }; - case 'tvm.slice': - return { - type: 'slice', - cell: Cell.fromBoc(Buffer.from(x.bytes, 'base64'))[0] - }; - case 'tvm.stackEntrySlice': - return parseObject(x.slice); - default: - throw Error('Unsupported item type: ' + typeName); - } -} diff --git a/src/parser/parseStackItem.ts b/src/parser/parseStackItem.ts index 27630931..d8561b4e 100644 --- a/src/parser/parseStackItem.ts +++ b/src/parser/parseStackItem.ts @@ -1,6 +1,6 @@ import { Cell, TupleItem } from '@ton/core'; import { StackItem } from './types'; -import { parseObject } from './parseObject'; +import { parseTvmValue } from './parseTvmValue'; export function parseStackItem(s: StackItem): TupleItem { if (s[0] === 'num') { @@ -19,7 +19,7 @@ export function parseStackItem(s: StackItem): TupleItem { } else if (s[0] === 'builder') { return { type: 'builder', cell: Cell.fromBoc(Buffer.from(s[1].bytes, 'base64'))[0] }; } else if (s[0] === 'tuple' || s[0] === 'list') { - return { type: 'tuple', items: s[1].elements.map(parseObject) }; + return { type: 'tuple', items: s[1].elements.map(parseTvmValue) }; } else { throw Error('Unsupported stack item type: ' + s[0]) } diff --git a/src/parser/parseTvmValue.ts b/src/parser/parseTvmValue.ts new file mode 100644 index 00000000..a26731c7 --- /dev/null +++ b/src/parser/parseTvmValue.ts @@ -0,0 +1,27 @@ +import { Cell, type TupleItem } from '@ton/core' +import type { TvmValue } from './types' + +export function parseTvmValue(x: TvmValue): TupleItem { + const typeName = x['@type'] + switch (typeName) { + case 'tvm.list': + case 'tvm.tuple': + return { type: 'tuple', items: x.elements.map(parseTvmValue) } + case 'tvm.cell': + return { type: 'cell', cell: Cell.fromBoc(Buffer.from(x.bytes, 'base64'))[0] } + case 'tvm.stackEntryCell': + return parseTvmValue(x.cell) + case 'tvm.stackEntryTuple': + return parseTvmValue(x.tuple) + case 'tvm.stackEntryNumber': + return parseTvmValue(x.number) + case 'tvm.numberDecimal': + return { type: 'int', value: BigInt(x.number) } + case 'tvm.slice': + return { type: 'slice', cell: Cell.fromBoc(Buffer.from(x.bytes, 'base64'))[0] } + case 'tvm.stackEntrySlice': + return parseTvmValue(x.slice) + default: + throw Error('Unsupported item type: ' + typeName) + } +} From fa3d11de64ae1ea8c9bc94cb855429676c7306d6 Mon Sep 17 00:00:00 2001 From: Nikolai Dorofeev Date: Sun, 23 Jun 2024 09:20:18 +0400 Subject: [PATCH 07/13] Create zod schemas for StackItem --- src/parser/index.ts | 4 +-- src/parser/types/stackItems.ts | 48 ++++++++++++++++++++++++++-------- src/parser/types/tvmValues.ts | 1 + 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/parser/index.ts b/src/parser/index.ts index 7dda24c8..d25450fa 100644 --- a/src/parser/index.ts +++ b/src/parser/index.ts @@ -1,12 +1,12 @@ import { TupleItem, TupleReader } from '@ton/core'; -import type { StackItem } from './types'; +import { stackItemSchema } from './types'; import { parseStackItem } from './parseStackItem'; export function parseStack(src: unknown[]) { let stack: TupleItem[] = []; for (let s of src) { - stack.push(parseStackItem(s as StackItem)); + stack.push(parseStackItem(stackItemSchema.parse(s))); } return new TupleReader(stack); diff --git a/src/parser/types/stackItems.ts b/src/parser/types/stackItems.ts index 52a9db33..47f091df 100644 --- a/src/parser/types/stackItems.ts +++ b/src/parser/types/stackItems.ts @@ -1,16 +1,42 @@ +import { z } from 'zod'; import type { TvmTuple, TvmList } from './tvmValues'; -type SerializedCell = { bytes: string }; +const serializedCellSchema = z.object({ + bytes: z.string(), +}); +type SerializedCell = z.infer; -type NullStackItem = ['null']; -type NumStackItem = ['num', string]; -type CellStackItem = ['cell', SerializedCell]; -type SliceStackItem = ['slice', SerializedCell]; -type BuilderStackItem = ['builder', SerializedCell]; +const nullSchema = z.union([ + z.tuple([z.literal('null')]), + z.tuple([z.literal('null'), z.null().optional()]), +]); +type NullStackItem = z.infer; -type TupleStackItem = ['tuple', TvmTuple]; -type ListStackItem = ['list', TvmList]; +const numSchema = z.tuple([z.literal('num'), z.string()]); +type NumStackItem = z.infer; -export type StackItem = NullStackItem | NumStackItem - | CellStackItem | SliceStackItem | BuilderStackItem - | TupleStackItem | ListStackItem; \ No newline at end of file +const cellSchema = z.tuple([z.literal('cell'), serializedCellSchema]); +type CellStackItem = z.infer; + +const sliceSchema = z.tuple([z.literal('slice'), serializedCellSchema]); +type SliceStackItem = z.infer; + +const builderSchema = z.tuple([z.literal('builder'), serializedCellSchema]); +type BuilderStackItem = z.infer; + +const tupleSchema = z.tuple([z.literal('tuple'), z.unknown() as z.ZodType]); +type TupleStackItem = z.infer; + +const listSchema = z.tuple([z.literal('list'), z.unknown() as z.ZodType]); +type ListStackItem = z.infer; + +export const stackItemSchema = z.union([ + nullSchema, + numSchema, + cellSchema, + sliceSchema, + builderSchema, + tupleSchema, + listSchema, +]); +export type StackItem = NullStackItem | NumStackItem | CellStackItem | SliceStackItem | BuilderStackItem | TupleStackItem | ListStackItem; diff --git a/src/parser/types/tvmValues.ts b/src/parser/types/tvmValues.ts index 394811aa..4dec20f3 100644 --- a/src/parser/types/tvmValues.ts +++ b/src/parser/types/tvmValues.ts @@ -9,6 +9,7 @@ type TvmCell = { '@type': 'tvm.cell', bytes: string }; type TvmStackEntryCell = { '@type': 'tvm.stackEntryCell', cell: TvmCell }; // Structured types for TON Virtual Machine values +// TODO: Check how list parsing works export type TvmList = { '@type': 'tvm.list', elements: TvmValue[] }; type TvmStackEntryList = { '@type': 'tvm.stackEntryList', list: TvmList }; From 185b469b0d0907672edd0b5851a3256d5e9066dd Mon Sep 17 00:00:00 2001 From: Nikolai Dorofeev Date: Sun, 23 Jun 2024 09:30:40 +0400 Subject: [PATCH 08/13] Add list support --- src/parser/parseTvmValue.ts | 12 ++++++++---- src/parser/types/tvmValues.ts | 1 - 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/parser/parseTvmValue.ts b/src/parser/parseTvmValue.ts index a26731c7..22c715be 100644 --- a/src/parser/parseTvmValue.ts +++ b/src/parser/parseTvmValue.ts @@ -4,19 +4,23 @@ import type { TvmValue } from './types' export function parseTvmValue(x: TvmValue): TupleItem { const typeName = x['@type'] switch (typeName) { + // TODO: Check how list parsing works case 'tvm.list': + return { type: 'tuple', items: x.elements.map(parseTvmValue) } + case 'tvm.stackEntryList': + return parseTvmValue(x.list) case 'tvm.tuple': return { type: 'tuple', items: x.elements.map(parseTvmValue) } + case 'tvm.stackEntryTuple': + return parseTvmValue(x.tuple) case 'tvm.cell': return { type: 'cell', cell: Cell.fromBoc(Buffer.from(x.bytes, 'base64'))[0] } case 'tvm.stackEntryCell': return parseTvmValue(x.cell) - case 'tvm.stackEntryTuple': - return parseTvmValue(x.tuple) - case 'tvm.stackEntryNumber': - return parseTvmValue(x.number) case 'tvm.numberDecimal': return { type: 'int', value: BigInt(x.number) } + case 'tvm.stackEntryNumber': + return parseTvmValue(x.number) case 'tvm.slice': return { type: 'slice', cell: Cell.fromBoc(Buffer.from(x.bytes, 'base64'))[0] } case 'tvm.stackEntrySlice': diff --git a/src/parser/types/tvmValues.ts b/src/parser/types/tvmValues.ts index 4dec20f3..394811aa 100644 --- a/src/parser/types/tvmValues.ts +++ b/src/parser/types/tvmValues.ts @@ -9,7 +9,6 @@ type TvmCell = { '@type': 'tvm.cell', bytes: string }; type TvmStackEntryCell = { '@type': 'tvm.stackEntryCell', cell: TvmCell }; // Structured types for TON Virtual Machine values -// TODO: Check how list parsing works export type TvmList = { '@type': 'tvm.list', elements: TvmValue[] }; type TvmStackEntryList = { '@type': 'tvm.stackEntryList', list: TvmList }; From cca727fb616d44068d5930c696f1f326f26fbf47 Mon Sep 17 00:00:00 2001 From: Nikolai Dorofeev Date: Fri, 5 Jul 2024 14:56:48 +0400 Subject: [PATCH 09/13] Handle null replies --- src/parser/parseStackItem.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/parser/parseStackItem.ts b/src/parser/parseStackItem.ts index d8561b4e..6dcf69b1 100644 --- a/src/parser/parseStackItem.ts +++ b/src/parser/parseStackItem.ts @@ -18,8 +18,15 @@ export function parseStackItem(s: StackItem): TupleItem { return { type: 'slice', cell: Cell.fromBoc(Buffer.from(s[1].bytes, 'base64'))[0] }; } else if (s[0] === 'builder') { return { type: 'builder', cell: Cell.fromBoc(Buffer.from(s[1].bytes, 'base64'))[0] }; - } else if (s[0] === 'tuple' || s[0] === 'list') { + } else if (s[0] === 'tuple') { return { type: 'tuple', items: s[1].elements.map(parseTvmValue) }; + } else if (s[0] === 'list') { + // Empty list is a null value + if (s[1].elements.length === 0) { + return { type: 'null' }; + } + // FIXME: possibly it is not used + return { type: 'tuple', items: s[1].elements.map(parseTvmValue) }; } else { throw Error('Unsupported stack item type: ' + s[0]) } From 137b6f92a0cc862e01be40bd5d7fc06d873dc903 Mon Sep 17 00:00:00 2001 From: Nikolai Dorofeev - d0rich Date: Sun, 1 Dec 2024 12:55:46 +0400 Subject: [PATCH 10/13] Return TonClient to master state --- src/client/TonClient.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/client/TonClient.ts b/src/client/TonClient.ts index 86071993..de749ff8 100644 --- a/src/client/TonClient.ts +++ b/src/client/TonClient.ts @@ -29,7 +29,6 @@ import { OpenedContract } from '@ton/core'; import { Maybe } from "../utils/maybe"; -import { parseStack } from "../parser"; export type TonClientParameters = { /** @@ -524,4 +523,4 @@ function createProvider(client: TonClient, address: Address, init: StateInit | n return client.getTransactions(address, { limit: limit ?? 100, lt: lt.toString(), hash: hash.toString('base64'), inclusive: true }); } } -} +} \ No newline at end of file From 889bd50831481c0f0efa0201177308fd6ff68c29 Mon Sep 17 00:00:00 2001 From: Nikolai Dorofeev - d0rich Date: Sun, 1 Dec 2024 13:04:33 +0400 Subject: [PATCH 11/13] Move types to a single util file --- src/parser/types/index.ts | 2 -- src/parser/types/tvmValues.ts | 21 --------------- .../types/stackItems.ts => utils/stack.ts} | 26 ++++++++++++++++++- 3 files changed, 25 insertions(+), 24 deletions(-) delete mode 100644 src/parser/types/index.ts delete mode 100644 src/parser/types/tvmValues.ts rename src/{parser/types/stackItems.ts => utils/stack.ts} (54%) diff --git a/src/parser/types/index.ts b/src/parser/types/index.ts deleted file mode 100644 index 1c337914..00000000 --- a/src/parser/types/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './stackItems'; -export * from './tvmValues'; \ No newline at end of file diff --git a/src/parser/types/tvmValues.ts b/src/parser/types/tvmValues.ts deleted file mode 100644 index 394811aa..00000000 --- a/src/parser/types/tvmValues.ts +++ /dev/null @@ -1,21 +0,0 @@ -// Plain types for TON Virtual Machine values -type TvmNumberDecimal = { '@type': 'tvm.numberDecimal', number: string }; -type TvmStackEntryNumber = { '@type': 'tvm.stackEntryNumber', number: TvmNumberDecimal }; - -type TvmSlice = { '@type': 'tvm.slice', bytes: string }; -type TvmStackEntrySlice = { '@type': 'tvm.stackEntrySlice', slice: TvmSlice }; - -type TvmCell = { '@type': 'tvm.cell', bytes: string }; -type TvmStackEntryCell = { '@type': 'tvm.stackEntryCell', cell: TvmCell }; - -// Structured types for TON Virtual Machine values -export type TvmList = { '@type': 'tvm.list', elements: TvmValue[] }; -type TvmStackEntryList = { '@type': 'tvm.stackEntryList', list: TvmList }; - -export type TvmTuple = { '@type': 'tvm.tuple', elements: TvmValue[] }; -type TvmStackEntryTuple = { '@type': 'tvm.stackEntryTuple', tuple: TvmTuple }; - -// Union of all TON Virtual Machine values -type TvmCommonValue = TvmNumberDecimal | TvmCell | TvmSlice | TvmList | TvmTuple; -type TvmStackEntryValue = TvmStackEntryCell | TvmStackEntryNumber | TvmStackEntrySlice | TvmStackEntryList | TvmStackEntryTuple; -export type TvmValue = TvmCommonValue | TvmStackEntryValue; diff --git a/src/parser/types/stackItems.ts b/src/utils/stack.ts similarity index 54% rename from src/parser/types/stackItems.ts rename to src/utils/stack.ts index 47f091df..f112d490 100644 --- a/src/parser/types/stackItems.ts +++ b/src/utils/stack.ts @@ -1,5 +1,29 @@ import { z } from 'zod'; -import type { TvmTuple, TvmList } from './tvmValues'; + +// Plain types for TON Virtual Machine values +type TvmNumberDecimal = { '@type': 'tvm.numberDecimal', number: string }; +type TvmStackEntryNumber = { '@type': 'tvm.stackEntryNumber', number: TvmNumberDecimal }; + +type TvmSlice = { '@type': 'tvm.slice', bytes: string }; +type TvmStackEntrySlice = { '@type': 'tvm.stackEntrySlice', slice: TvmSlice }; + +type TvmCell = { '@type': 'tvm.cell', bytes: string }; +type TvmStackEntryCell = { '@type': 'tvm.stackEntryCell', cell: TvmCell }; + +// Structured types for TON Virtual Machine values +export type TvmList = { '@type': 'tvm.list', elements: TvmValue[] }; +type TvmStackEntryList = { '@type': 'tvm.stackEntryList', list: TvmList }; + +export type TvmTuple = { '@type': 'tvm.tuple', elements: TvmValue[] }; +type TvmStackEntryTuple = { '@type': 'tvm.stackEntryTuple', tuple: TvmTuple }; + +// Union of all TON Virtual Machine values +type TvmCommonValue = TvmNumberDecimal | TvmCell | TvmSlice | TvmList | TvmTuple; +type TvmStackEntryValue = TvmStackEntryCell | TvmStackEntryNumber | TvmStackEntrySlice | TvmStackEntryList | TvmStackEntryTuple; +export type TvmValue = TvmCommonValue | TvmStackEntryValue; + + +// zod definitions const serializedCellSchema = z.object({ bytes: z.string(), From d1289794a892cd0176ac03c487f7f7293b7e79a7 Mon Sep 17 00:00:00 2001 From: Nikolai Dorofeev - d0rich Date: Sun, 1 Dec 2024 13:06:07 +0400 Subject: [PATCH 12/13] Remove Tvm from the types names --- src/utils/stack.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/utils/stack.ts b/src/utils/stack.ts index f112d490..29a4f1f0 100644 --- a/src/utils/stack.ts +++ b/src/utils/stack.ts @@ -1,26 +1,26 @@ import { z } from 'zod'; // Plain types for TON Virtual Machine values -type TvmNumberDecimal = { '@type': 'tvm.numberDecimal', number: string }; -type TvmStackEntryNumber = { '@type': 'tvm.stackEntryNumber', number: TvmNumberDecimal }; +type NumberDecimal = { '@type': 'tvm.numberDecimal', number: string }; +type StackEntryNumber = { '@type': 'tvm.stackEntryNumber', number: NumberDecimal }; -type TvmSlice = { '@type': 'tvm.slice', bytes: string }; -type TvmStackEntrySlice = { '@type': 'tvm.stackEntrySlice', slice: TvmSlice }; +type Slice = { '@type': 'tvm.slice', bytes: string }; +type StackEntrySlice = { '@type': 'tvm.stackEntrySlice', slice: Slice }; -type TvmCell = { '@type': 'tvm.cell', bytes: string }; -type TvmStackEntryCell = { '@type': 'tvm.stackEntryCell', cell: TvmCell }; +type Cell = { '@type': 'tvm.cell', bytes: string }; +type StackEntryCell = { '@type': 'tvm.stackEntryCell', cell: Cell }; // Structured types for TON Virtual Machine values -export type TvmList = { '@type': 'tvm.list', elements: TvmValue[] }; -type TvmStackEntryList = { '@type': 'tvm.stackEntryList', list: TvmList }; +export type List = { '@type': 'tvm.list', elements: Value[] }; +type StackEntryList = { '@type': 'tvm.stackEntryList', list: List }; -export type TvmTuple = { '@type': 'tvm.tuple', elements: TvmValue[] }; -type TvmStackEntryTuple = { '@type': 'tvm.stackEntryTuple', tuple: TvmTuple }; +export type Tuple = { '@type': 'tvm.tuple', elements: Value[] }; +type StackEntryTuple = { '@type': 'tvm.stackEntryTuple', tuple: Tuple }; // Union of all TON Virtual Machine values -type TvmCommonValue = TvmNumberDecimal | TvmCell | TvmSlice | TvmList | TvmTuple; -type TvmStackEntryValue = TvmStackEntryCell | TvmStackEntryNumber | TvmStackEntrySlice | TvmStackEntryList | TvmStackEntryTuple; -export type TvmValue = TvmCommonValue | TvmStackEntryValue; +type CommonValue = NumberDecimal | Cell | Slice | List | Tuple; +type StackEntryValue = StackEntryCell | StackEntryNumber | StackEntrySlice | StackEntryList | StackEntryTuple; +export type Value = CommonValue | StackEntryValue; // zod definitions @@ -48,10 +48,10 @@ type SliceStackItem = z.infer; const builderSchema = z.tuple([z.literal('builder'), serializedCellSchema]); type BuilderStackItem = z.infer; -const tupleSchema = z.tuple([z.literal('tuple'), z.unknown() as z.ZodType]); +const tupleSchema = z.tuple([z.literal('tuple'), z.unknown() as z.ZodType]); type TupleStackItem = z.infer; -const listSchema = z.tuple([z.literal('list'), z.unknown() as z.ZodType]); +const listSchema = z.tuple([z.literal('list'), z.unknown() as z.ZodType]); type ListStackItem = z.infer; export const stackItemSchema = z.union([ From b675b92d68b57dae5270a3097f97c4ba3bbce260 Mon Sep 17 00:00:00 2001 From: Nikolai Dorofeev - d0rich Date: Sun, 1 Dec 2024 13:16:32 +0400 Subject: [PATCH 13/13] Insert parser changes to the original file --- src/client/TonClient.ts | 18 ++++++++++-------- src/parser/index.ts | 13 ------------- src/parser/parseStackItem.ts | 33 --------------------------------- src/parser/parseTvmValue.ts | 31 ------------------------------- 4 files changed, 10 insertions(+), 85 deletions(-) delete mode 100644 src/parser/index.ts delete mode 100644 src/parser/parseStackItem.ts delete mode 100644 src/parser/parseTvmValue.ts diff --git a/src/client/TonClient.ts b/src/client/TonClient.ts index de749ff8..87bd7855 100644 --- a/src/client/TonClient.ts +++ b/src/client/TonClient.ts @@ -29,6 +29,7 @@ import { OpenedContract } from '@ton/core'; import { Maybe } from "../utils/maybe"; +import { StackItem, Value } from "../utils/stack"; export type TonClientParameters = { /** @@ -338,16 +339,17 @@ export class TonClient { } } -function parseStackEntry(x: any): any { +function parseStackEntry(x: Value): TupleItem { const typeName = x['@type']; switch(typeName) { case 'tvm.list': + return { type: 'tuple', items: x.elements.map(parseStackEntry) } case 'tvm.tuple': - return x.elements.map(parseStackEntry); + return { type: 'tuple', items: x.elements.map(parseStackEntry) }; case 'tvm.cell': - return Cell.fromBoc(Buffer.from(x.bytes, 'base64'))[0]; + return { type: 'cell', cell: Cell.fromBoc(Buffer.from(x.bytes, 'base64'))[0] } case 'tvm.slice': - return Cell.fromBoc(Buffer.from(x.bytes, 'base64'))[0]; + return { type: 'slice', cell: Cell.fromBoc(Buffer.from(x.bytes, 'base64'))[0] } case 'tvm.stackEntryCell': return parseStackEntry(x.cell); case 'tvm.stackEntrySlice': @@ -359,13 +361,13 @@ function parseStackEntry(x: any): any { case 'tvm.stackEntryNumber': return parseStackEntry(x.number); case 'tvm.numberDecimal': - return BigInt(x.number); + return { type: 'int', value: BigInt(x.number) } default: throw Error('Unsupported item type: ' + typeName); } } -function parseStackItem(s: any): TupleItem { +function parseStackItem(s: StackItem): TupleItem { if (s[0] === 'num') { let val = s[1] as string; if (val.startsWith('-')) { @@ -392,11 +394,11 @@ function parseStackItem(s: any): TupleItem { } } -function parseStack(src: any[]) { +function parseStack(src: unknown[]) { let stack: TupleItem[] = []; for (let s of src) { - stack.push(parseStackItem(s)); + stack.push(parseStackItem(s as StackItem)); } return new TupleReader(stack); diff --git a/src/parser/index.ts b/src/parser/index.ts deleted file mode 100644 index d25450fa..00000000 --- a/src/parser/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { TupleItem, TupleReader } from '@ton/core'; -import { stackItemSchema } from './types'; -import { parseStackItem } from './parseStackItem'; - -export function parseStack(src: unknown[]) { - let stack: TupleItem[] = []; - - for (let s of src) { - stack.push(parseStackItem(stackItemSchema.parse(s))); - } - - return new TupleReader(stack); -} diff --git a/src/parser/parseStackItem.ts b/src/parser/parseStackItem.ts deleted file mode 100644 index 6dcf69b1..00000000 --- a/src/parser/parseStackItem.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Cell, TupleItem } from '@ton/core'; -import { StackItem } from './types'; -import { parseTvmValue } from './parseTvmValue'; - -export function parseStackItem(s: StackItem): TupleItem { - if (s[0] === 'num') { - let val = s[1] as string; - if (val.startsWith('-')) { - return { type: 'int', value: -BigInt(val.slice(1)) }; - } else { - return { type: 'int', value: BigInt(val) }; - } - } else if (s[0] === 'null') { - return { type: 'null' }; - } else if (s[0] === 'cell') { - return { type: 'cell', cell: Cell.fromBoc(Buffer.from(s[1].bytes, 'base64'))[0] }; - } else if (s[0] === 'slice') { - return { type: 'slice', cell: Cell.fromBoc(Buffer.from(s[1].bytes, 'base64'))[0] }; - } else if (s[0] === 'builder') { - return { type: 'builder', cell: Cell.fromBoc(Buffer.from(s[1].bytes, 'base64'))[0] }; - } else if (s[0] === 'tuple') { - return { type: 'tuple', items: s[1].elements.map(parseTvmValue) }; - } else if (s[0] === 'list') { - // Empty list is a null value - if (s[1].elements.length === 0) { - return { type: 'null' }; - } - // FIXME: possibly it is not used - return { type: 'tuple', items: s[1].elements.map(parseTvmValue) }; - } else { - throw Error('Unsupported stack item type: ' + s[0]) - } -} diff --git a/src/parser/parseTvmValue.ts b/src/parser/parseTvmValue.ts deleted file mode 100644 index 22c715be..00000000 --- a/src/parser/parseTvmValue.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Cell, type TupleItem } from '@ton/core' -import type { TvmValue } from './types' - -export function parseTvmValue(x: TvmValue): TupleItem { - const typeName = x['@type'] - switch (typeName) { - // TODO: Check how list parsing works - case 'tvm.list': - return { type: 'tuple', items: x.elements.map(parseTvmValue) } - case 'tvm.stackEntryList': - return parseTvmValue(x.list) - case 'tvm.tuple': - return { type: 'tuple', items: x.elements.map(parseTvmValue) } - case 'tvm.stackEntryTuple': - return parseTvmValue(x.tuple) - case 'tvm.cell': - return { type: 'cell', cell: Cell.fromBoc(Buffer.from(x.bytes, 'base64'))[0] } - case 'tvm.stackEntryCell': - return parseTvmValue(x.cell) - case 'tvm.numberDecimal': - return { type: 'int', value: BigInt(x.number) } - case 'tvm.stackEntryNumber': - return parseTvmValue(x.number) - case 'tvm.slice': - return { type: 'slice', cell: Cell.fromBoc(Buffer.from(x.bytes, 'base64'))[0] } - case 'tvm.stackEntrySlice': - return parseTvmValue(x.slice) - default: - throw Error('Unsupported item type: ' + typeName) - } -}