Skip to content
Open
20 changes: 11 additions & 9 deletions src/client/TonClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
OpenedContract
} from '@ton/core';
import { Maybe } from "../utils/maybe";
import { StackItem, Value } from "../utils/stack";

export type TonClientParameters = {
/**
Expand Down Expand Up @@ -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':
Expand All @@ -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('-')) {
Expand All @@ -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);
Expand Down Expand Up @@ -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 });
}
}
}
}
66 changes: 66 additions & 0 deletions src/utils/stack.ts
Original file line number Diff line number Diff line change
@@ -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<typeof serializedCellSchema>;

const nullSchema = z.union([
z.tuple([z.literal('null')]),
z.tuple([z.literal('null'), z.null().optional()]),
]);
type NullStackItem = z.infer<typeof nullSchema>;

const numSchema = z.tuple([z.literal('num'), z.string()]);
type NumStackItem = z.infer<typeof numSchema>;

const cellSchema = z.tuple([z.literal('cell'), serializedCellSchema]);
type CellStackItem = z.infer<typeof cellSchema>;

const sliceSchema = z.tuple([z.literal('slice'), serializedCellSchema]);
type SliceStackItem = z.infer<typeof sliceSchema>;

const builderSchema = z.tuple([z.literal('builder'), serializedCellSchema]);
type BuilderStackItem = z.infer<typeof builderSchema>;

const tupleSchema = z.tuple([z.literal('tuple'), z.unknown() as z.ZodType<Tuple>]);
type TupleStackItem = z.infer<typeof tupleSchema>;

const listSchema = z.tuple([z.literal('list'), z.unknown() as z.ZodType<List>]);
type ListStackItem = z.infer<typeof listSchema>;

export const stackItemSchema = z.union([
nullSchema,
numSchema,
cellSchema,
sliceSchema,
builderSchema,
tupleSchema,
listSchema,
]);
export type StackItem = NullStackItem | NumStackItem | CellStackItem | SliceStackItem | BuilderStackItem | TupleStackItem | ListStackItem;