diff --git a/src/client/TonClient.ts b/src/client/TonClient.ts index 34cc3af2..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); @@ -523,4 +525,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 diff --git a/src/utils/stack.ts b/src/utils/stack.ts new file mode 100644 index 00000000..29a4f1f0 --- /dev/null +++ b/src/utils/stack.ts @@ -0,0 +1,66 @@ +import { z } from 'zod'; + +// Plain types for TON Virtual Machine values +type NumberDecimal = { '@type': 'tvm.numberDecimal', number: string }; +type StackEntryNumber = { '@type': 'tvm.stackEntryNumber', number: NumberDecimal }; + +type Slice = { '@type': 'tvm.slice', bytes: string }; +type StackEntrySlice = { '@type': 'tvm.stackEntrySlice', slice: Slice }; + +type Cell = { '@type': 'tvm.cell', bytes: string }; +type StackEntryCell = { '@type': 'tvm.stackEntryCell', cell: Cell }; + +// Structured types for TON Virtual Machine values +export type List = { '@type': 'tvm.list', elements: Value[] }; +type StackEntryList = { '@type': 'tvm.stackEntryList', list: List }; + +export type Tuple = { '@type': 'tvm.tuple', elements: Value[] }; +type StackEntryTuple = { '@type': 'tvm.stackEntryTuple', tuple: Tuple }; + +// Union of all TON Virtual Machine values +type CommonValue = NumberDecimal | Cell | Slice | List | Tuple; +type StackEntryValue = StackEntryCell | StackEntryNumber | StackEntrySlice | StackEntryList | StackEntryTuple; +export type Value = CommonValue | StackEntryValue; + + +// zod definitions + +const serializedCellSchema = z.object({ + bytes: z.string(), +}); +type SerializedCell = z.infer; + +const nullSchema = z.union([ + z.tuple([z.literal('null')]), + z.tuple([z.literal('null'), z.null().optional()]), +]); +type NullStackItem = z.infer; + +const numSchema = z.tuple([z.literal('num'), z.string()]); +type NumStackItem = z.infer; + +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;