@ton/sandbox 0.21.0-debugger.3 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.22.0] - 2024-09-17
9
+
10
+ ### Added
11
+
12
+ - Added `blockchain.recordStorage` flag. If set to `true`, `BlockchainTransaction` will have `oldStorage` and `newStorage` fields. Note that enabling this flag will disable a certain optimization, which will slow down contract emulation
13
+
14
+ ## [0.21.0] - 2024-09-16
15
+
16
+ ### Added
17
+
18
+ - `SandboxContract` now wraps methods starting with `is` (having the same semantics as `get`) as well as `send` and `get`
19
+
20
+ ### Changed
21
+
22
+ - Updated dependencies
23
+
8
24
  ## [0.20.0] - 2024-05-31
9
25
 
10
26
  ### Added
@@ -26,6 +26,8 @@ export type BlockchainTransaction = Transaction & {
26
26
  parent?: BlockchainTransaction;
27
27
  children: BlockchainTransaction[];
28
28
  externals: ExternalOut[];
29
+ oldStorage?: Cell;
30
+ newStorage?: Cell;
29
31
  };
30
32
  /**
31
33
  * @type SendMessageResult Represents the result of sending a message.
@@ -45,7 +47,7 @@ export declare const SANDBOX_CONTRACT_SYMBOL: unique symbol;
45
47
  * @template F Type parameter representing the original contract object.
46
48
  */
47
49
  export type SandboxContract<F> = {
48
- [P in keyof F]: P extends `get${string}` ? (F[P] extends (x: infer CP, ...args: infer P) => infer R ? (ExtendsContractProvider<CP> extends true ? (...args: P) => R : never) : never) : (P extends `send${string}` ? (F[P] extends (x: infer CP, ...args: infer P) => infer R ? (ExtendsContractProvider<CP> extends true ? (...args: P) => Promise<SendMessageResult & {
50
+ [P in keyof F]: P extends `${'get' | 'is'}${string}` ? (F[P] extends (x: infer CP, ...args: infer P) => infer R ? (ExtendsContractProvider<CP> extends true ? (...args: P) => R : never) : never) : (P extends `send${string}` ? (F[P] extends (x: infer CP, ...args: infer P) => infer R ? (ExtendsContractProvider<CP> extends true ? (...args: P) => Promise<SendMessageResult & {
49
51
  result: R extends Promise<infer PR> ? PR : R;
50
52
  }> : never) : never) : F[P]);
51
53
  };
@@ -98,7 +100,7 @@ export declare class Blockchain {
98
100
  protected lock: AsyncLock;
99
101
  protected contractFetches: Map<string, Promise<SmartContract>>;
100
102
  protected nextCreateWalletIndex: number;
101
- protected shouldDebug: boolean;
103
+ protected shouldRecordStorage: boolean;
102
104
  readonly executor: IExecutor;
103
105
  /**
104
106
  * Saves snapshot of current blockchain.
@@ -116,8 +118,8 @@ export declare class Blockchain {
116
118
  * @param snapshot Snapshot of blockchain
117
119
  */
118
120
  loadFrom(snapshot: BlockchainSnapshot): Promise<void>;
119
- get debug(): boolean;
120
- set debug(value: boolean);
121
+ get recordStorage(): boolean;
122
+ set recordStorage(v: boolean);
121
123
  /**
122
124
  * @returns Current time in blockchain
123
125
  */
@@ -81,11 +81,11 @@ class Blockchain {
81
81
  this.globalLibs = snapshot.libs;
82
82
  this.nextCreateWalletIndex = snapshot.nextCreateWalletIndex;
83
83
  }
84
- get debug() {
85
- return this.shouldDebug;
84
+ get recordStorage() {
85
+ return this.shouldRecordStorage;
86
86
  }
87
- set debug(value) {
88
- this.shouldDebug = value;
87
+ set recordStorage(v) {
88
+ this.shouldRecordStorage = v;
89
89
  }
90
90
  /**
91
91
  * @returns Current time in blockchain
@@ -118,7 +118,7 @@ class Blockchain {
118
118
  this.lock = new AsyncLock_1.AsyncLock();
119
119
  this.contractFetches = new Map();
120
120
  this.nextCreateWalletIndex = 0;
121
- this.shouldDebug = false;
121
+ this.shouldRecordStorage = false;
122
122
  this.networkConfig = blockchainConfigToBase64(opts.config);
123
123
  this.executor = opts.executor;
124
124
  this.storage = opts.storage;
@@ -21,6 +21,8 @@ export type SmartContractTransaction = Transaction & {
21
21
  blockchainLogs: string;
22
22
  vmLogs: string;
23
23
  debugLogs: string;
24
+ oldStorage?: Cell;
25
+ newStorage?: Cell;
24
26
  };
25
27
  export type MessageParams = Partial<{
26
28
  now: number;
@@ -98,6 +100,4 @@ export declare class SmartContract {
98
100
  get verbosity(): LogsVerbosity;
99
101
  set verbosity(value: LogsVerbosity);
100
102
  setVerbosity(verbosity: Partial<LogsVerbosity> | Verbosity | undefined): void;
101
- get debug(): boolean;
102
- setDebug(debug: boolean | undefined): void;
103
103
  }
@@ -10,13 +10,11 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
11
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
12
  };
13
- var _SmartContract_account, _SmartContract_parsedAccount, _SmartContract_lastTxTime, _SmartContract_verbosity, _SmartContract_debug;
13
+ var _SmartContract_account, _SmartContract_parsedAccount, _SmartContract_lastTxTime, _SmartContract_verbosity;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.SmartContract = exports.EmulationError = exports.TimeError = exports.GetMethodError = exports.createEmptyShardAccount = exports.createShardAccount = void 0;
16
16
  const core_1 = require("@ton/core");
17
17
  const selector_1 = require("../utils/selector");
18
- const debug_1 = require("../debugger/debug");
19
- const DebugInfoCache_1 = require("../debugger/DebugInfoCache");
20
18
  function createShardAccount(args) {
21
19
  let wc = args.workchain ?? 0;
22
20
  let address = args.address ?? (0, core_1.contractAddress)(wc, { code: args.code, data: args.data });
@@ -130,7 +128,6 @@ class SmartContract {
130
128
  _SmartContract_parsedAccount.set(this, void 0);
131
129
  _SmartContract_lastTxTime.set(this, void 0);
132
130
  _SmartContract_verbosity.set(this, void 0);
133
- _SmartContract_debug.set(this, void 0);
134
131
  this.address = shardAccount.account.addr;
135
132
  __classPrivateFieldSet(this, _SmartContract_account, (0, core_1.beginCell)().store((0, core_1.storeShardAccount)(shardAccount)).endCell().toBoc().toString('base64'), "f");
136
133
  __classPrivateFieldSet(this, _SmartContract_parsedAccount, shardAccount, "f");
@@ -213,34 +210,10 @@ class SmartContract {
213
210
  now: this.blockchain.now,
214
211
  ...params,
215
212
  };
216
- const args = {
213
+ return await this.runCommon(() => this.blockchain.executor.runTransaction({
217
214
  ...this.createCommonArgs(params),
218
215
  message: (0, core_1.beginCell)().store((0, core_1.storeMessage)(message)).endCell(),
219
- };
220
- if (this.debug) {
221
- const acc = this.account;
222
- if (acc.account === undefined || acc.account === null) {
223
- console.log('Debugging uninitialized accounts is unsupported in debugger beta');
224
- return await this.runCommon(() => this.blockchain.executor.runTransaction(args));
225
- }
226
- if (acc.account.storage.state.type !== 'active') {
227
- console.log('Debugging uninitialized accounts is unsupported in debugger beta');
228
- return await this.runCommon(() => this.blockchain.executor.runTransaction(args));
229
- }
230
- const code = acc.account.storage.state.state.code;
231
- if (code === undefined || code === null) {
232
- console.log('Debugging uninitialized accounts is unsupported in debugger beta');
233
- return await this.runCommon(() => this.blockchain.executor.runTransaction(args));
234
- }
235
- const sm = DebugInfoCache_1.defaultDebugInfoCache.get(code.hash().toString('base64'));
236
- if (sm === undefined) {
237
- return await this.runCommon(() => this.blockchain.executor.runTransaction(args));
238
- }
239
- return await this.runCommon(() => (0, debug_1.debugTransaction)(this.blockchain.executor, args, code, sm));
240
- }
241
- else {
242
- return await this.runCommon(() => this.blockchain.executor.runTransaction(args));
243
- }
216
+ }));
244
217
  }
245
218
  async runTickTock(which, params) {
246
219
  return await this.runCommon(() => this.blockchain.executor.runTickTock({
@@ -249,6 +222,10 @@ class SmartContract {
249
222
  }));
250
223
  }
251
224
  async runCommon(run) {
225
+ let oldStorage = undefined;
226
+ if (this.blockchain.recordStorage && this.account.account?.storage.state.type === 'active') {
227
+ oldStorage = this.account.account?.storage.state.state.data ?? undefined;
228
+ }
252
229
  const res = await run();
253
230
  if (this.verbosity.print && this.verbosity.blockchainLogs && res.logs.length > 0) {
254
231
  console.log(res.logs);
@@ -266,18 +243,24 @@ class SmartContract {
266
243
  __classPrivateFieldSet(this, _SmartContract_account, res.result.shardAccount, "f");
267
244
  __classPrivateFieldSet(this, _SmartContract_parsedAccount, undefined, "f");
268
245
  __classPrivateFieldSet(this, _SmartContract_lastTxTime, tx.now, "f");
246
+ let newStorage = undefined;
247
+ if (this.blockchain.recordStorage && this.account.account?.storage.state.type === 'active') {
248
+ newStorage = this.account.account?.storage.state.state.data ?? undefined;
249
+ }
269
250
  return {
270
251
  ...tx,
271
252
  blockchainLogs: res.logs,
272
253
  vmLogs: res.result.vmLog,
273
254
  debugLogs: res.debugLogs,
255
+ oldStorage,
256
+ newStorage,
274
257
  };
275
258
  }
276
259
  async get(method, stack = [], params) {
277
260
  if (this.account.account?.storage.state.type !== 'active') {
278
261
  throw new Error('Trying to run get method on non-active contract');
279
262
  }
280
- const args = {
263
+ const res = await this.blockchain.executor.runGetMethod({
281
264
  code: this.account.account?.storage.state.state.code,
282
265
  data: this.account.account?.storage.state.state.data,
283
266
  methodId: typeof method === 'string' ? (0, selector_1.getSelectorForMethod)(method) : method,
@@ -291,20 +274,7 @@ class SmartContract {
291
274
  randomSeed: params?.randomSeed ?? Buffer.alloc(32),
292
275
  gasLimit: params?.gasLimit ?? 10000000n,
293
276
  debugEnabled: this.verbosity.debugLogs,
294
- };
295
- let res;
296
- if (this.debug) {
297
- const di = DebugInfoCache_1.defaultDebugInfoCache.get(args.code.hash().toString('base64'));
298
- if (di === undefined) {
299
- res = await this.blockchain.executor.runGetMethod(args);
300
- }
301
- else {
302
- res = await (0, debug_1.debugGetMethod)(this.blockchain.executor, args, di);
303
- }
304
- }
305
- else {
306
- res = await this.blockchain.executor.runGetMethod(args);
307
- }
277
+ });
308
278
  if (this.verbosity.print && this.verbosity.blockchainLogs && res.logs.length > 0) {
309
279
  console.log(res.logs);
310
280
  }
@@ -352,12 +322,6 @@ class SmartContract {
352
322
  __classPrivateFieldSet(this, _SmartContract_verbosity, verbosity, "f");
353
323
  }
354
324
  }
355
- get debug() {
356
- return __classPrivateFieldGet(this, _SmartContract_debug, "f") ?? this.blockchain.debug;
357
- }
358
- setDebug(debug) {
359
- __classPrivateFieldSet(this, _SmartContract_debug, debug, "f");
360
- }
361
325
  }
362
326
  exports.SmartContract = SmartContract;
363
- _SmartContract_account = new WeakMap(), _SmartContract_parsedAccount = new WeakMap(), _SmartContract_lastTxTime = new WeakMap(), _SmartContract_verbosity = new WeakMap(), _SmartContract_debug = new WeakMap();
327
+ _SmartContract_account = new WeakMap(), _SmartContract_parsedAccount = new WeakMap(), _SmartContract_lastTxTime = new WeakMap(), _SmartContract_verbosity = new WeakMap();
@@ -82,9 +82,7 @@ export declare class Executor implements IExecutor {
82
82
  private heap;
83
83
  private emulator?;
84
84
  private debugLogs;
85
- debugLogFunc: (s: string) => void;
86
85
  private constructor();
87
- private handleLog;
88
86
  static create(): Promise<Executor>;
89
87
  runGetMethod(args: GetMethodArgs): Promise<GetMethodResult>;
90
88
  private runCommon;
@@ -94,31 +92,4 @@ export declare class Executor implements IExecutor {
94
92
  private getEmulatorPointer;
95
93
  invoke(method: string, args: (number | string)[]): number;
96
94
  private extractString;
97
- sbsGetMethodSetup(args: GetMethodArgs): number;
98
- destroyTvmEmulator(ptr: number): void;
99
- sbsGetMethodStep(ptr: number): boolean;
100
- sbsGetMethodStack(ptr: number): TupleItem[];
101
- sbsGetMethodC7(ptr: number): TupleItem;
102
- sbsGetMethodGetContParam(ptr: number): number;
103
- sbsGetMethodSetContParam(ptr: number, param: number): number;
104
- sbsGetMethodCodePos(ptr: number): {
105
- hash: string;
106
- offset: number;
107
- };
108
- sbsGetMethodResult(ptr: number): GetMethodResult;
109
- sbsTransactionSetup(args: RunTransactionArgs): {
110
- res: number;
111
- emptr: number;
112
- };
113
- destroyEmulator(ptr: number): void;
114
- sbsTransactionStep(ptr: number): boolean;
115
- sbsTransactionCodePos(ptr: number): {
116
- hash: string;
117
- offset: number;
118
- };
119
- sbsTransactionStack(ptr: number): TupleItem[];
120
- sbsTransactionC7(ptr: number): TupleItem;
121
- sbsTransactionGetContParam(ptr: number): number;
122
- sbsTransactionSetContParam(ptr: number, param: number): number;
123
- sbsTransactionResult(ptr: number): EmulationResult;
124
95
  }
@@ -69,18 +69,13 @@ class Heap {
69
69
  class Executor {
70
70
  constructor(module) {
71
71
  this.debugLogs = [];
72
- this.debugLogFunc = () => { };
73
72
  this.module = module;
74
73
  this.heap = new Heap(module);
75
74
  }
76
- handleLog(text) {
77
- this.debugLogs.push(text);
78
- this.debugLogFunc(text);
79
- }
80
75
  static async create() {
81
76
  const ex = new Executor(await EmulatorModule({
82
77
  wasmBinary: (0, base64_1.base64Decode)(require('./emulator-emscripten.wasm.js').EmulatorEmscriptenWasm),
83
- printErr: (text) => ex.handleLog(text),
78
+ printErr: (text) => ex.debugLogs.push(text),
84
79
  }));
85
80
  return ex;
86
81
  }
@@ -209,159 +204,5 @@ class Executor {
209
204
  this.module._free(ptr);
210
205
  return str;
211
206
  }
212
- sbsGetMethodSetup(args) {
213
- const params = {
214
- code: args.code.toBoc().toString('base64'),
215
- data: args.data.toBoc().toString('base64'),
216
- verbosity: verbosityToNum[args.verbosity],
217
- libs: args.libs?.toBoc().toString('base64') ?? '',
218
- address: args.address.toString(),
219
- unixtime: args.unixTime,
220
- balance: args.balance.toString(),
221
- rand_seed: args.randomSeed.toString('hex'),
222
- gas_limit: args.gasLimit.toString(),
223
- method_id: args.methodId,
224
- debug_enabled: args.debugEnabled,
225
- };
226
- let stack = (0, core_1.serializeTuple)(args.stack);
227
- this.debugLogs = [];
228
- const res = this.invoke('_setup_sbs_get_method', [
229
- JSON.stringify(params),
230
- stack.toBoc().toString('base64'),
231
- args.config,
232
- ]);
233
- return res;
234
- }
235
- destroyTvmEmulator(ptr) {
236
- this.invoke('_destroy_tvm_emulator', [ptr]);
237
- }
238
- sbsGetMethodStep(ptr) {
239
- const res = this.invoke('_sbs_step', [
240
- ptr,
241
- ]);
242
- return res !== 0;
243
- }
244
- sbsGetMethodStack(ptr) {
245
- const resp = this.extractString(this.invoke('_sbs_get_stack', [
246
- ptr
247
- ]));
248
- return (0, core_1.parseTuple)(core_1.Cell.fromBase64(resp));
249
- }
250
- sbsGetMethodC7(ptr) {
251
- const resp = this.extractString(this.invoke('_sbs_get_c7', [
252
- ptr
253
- ]));
254
- return (0, core_1.parseTuple)((0, core_1.beginCell)().storeUint(1, 24).storeRef(core_1.Cell.EMPTY).storeSlice(core_1.Cell.fromBase64(resp).beginParse()).endCell())[0];
255
- }
256
- sbsGetMethodGetContParam(ptr) {
257
- return this.invoke('_sbs_get_cont_param', [
258
- ptr
259
- ]);
260
- }
261
- sbsGetMethodSetContParam(ptr, param) {
262
- return this.invoke('_sbs_set_cont_param', [
263
- ptr,
264
- param
265
- ]);
266
- }
267
- sbsGetMethodCodePos(ptr) {
268
- const resp = this.extractString(this.invoke('_sbs_get_code_pos', [
269
- ptr
270
- ]));
271
- const parts = resp.split(':');
272
- return {
273
- hash: parts[0],
274
- offset: parseInt(parts[1]),
275
- };
276
- }
277
- sbsGetMethodResult(ptr) {
278
- const resp = JSON.parse(this.extractString(this.invoke('_sbs_get_method_result', [
279
- ptr
280
- ])));
281
- const debugLogs = this.debugLogs.join('\n');
282
- return {
283
- output: resp,
284
- logs: 'BLOCKCHAIN LOGS ARE NOT AVAILABLE IN DEBUGGER BETA',
285
- debugLogs,
286
- };
287
- }
288
- sbsTransactionSetup(args) {
289
- const emptr = this.invoke('_create_emulator', [
290
- args.config,
291
- verbosityToNum[args.verbosity],
292
- ]);
293
- const params = runCommonArgsToInternalParams(args);
294
- this.debugLogs = [];
295
- const res = this.invoke('_emulate_sbs', [
296
- emptr,
297
- args.libs?.toBoc().toString('base64') ?? 0,
298
- args.shardAccount,
299
- args.message.toBoc().toString('base64'),
300
- JSON.stringify(params),
301
- ]);
302
- return { res, emptr };
303
- }
304
- destroyEmulator(ptr) {
305
- this.invoke('_destroy_emulator', [ptr]);
306
- }
307
- sbsTransactionStep(ptr) {
308
- const res = this.invoke('_em_sbs_step', [ptr]);
309
- return res !== 0;
310
- }
311
- sbsTransactionCodePos(ptr) {
312
- const resp = this.extractString(this.invoke('_em_sbs_code_pos', [ptr]));
313
- const parts = resp.split(':');
314
- return {
315
- hash: parts[0],
316
- offset: parseInt(parts[1]),
317
- };
318
- }
319
- sbsTransactionStack(ptr) {
320
- const resp = this.extractString(this.invoke('_em_sbs_stack', [
321
- ptr
322
- ]));
323
- return (0, core_1.parseTuple)(core_1.Cell.fromBase64(resp));
324
- }
325
- sbsTransactionC7(ptr) {
326
- const resp = this.extractString(this.invoke('_em_sbs_c7', [
327
- ptr
328
- ]));
329
- return (0, core_1.parseTuple)((0, core_1.beginCell)().storeUint(1, 24).storeRef(core_1.Cell.EMPTY).storeSlice(core_1.Cell.fromBase64(resp).beginParse()).endCell())[0];
330
- }
331
- sbsTransactionGetContParam(ptr) {
332
- return this.invoke('_em_sbs_get_cont_param', [
333
- ptr
334
- ]);
335
- }
336
- sbsTransactionSetContParam(ptr, param) {
337
- return this.invoke('_em_sbs_set_cont_param', [
338
- ptr,
339
- param
340
- ]);
341
- }
342
- sbsTransactionResult(ptr) {
343
- const result = JSON.parse(this.extractString(this.invoke('_em_sbs_result', [
344
- ptr
345
- ])));
346
- const debugLogs = this.debugLogs.join('\n');
347
- return {
348
- result: result.success ? {
349
- success: true,
350
- transaction: result.transaction,
351
- shardAccount: result.shard_account,
352
- vmLog: result.vm_log,
353
- actions: result.actions,
354
- } : {
355
- success: false,
356
- error: result.error,
357
- vmResults: 'vm_log' in result ? {
358
- vmLog: result.vm_log,
359
- vmExitCode: result.vm_exit_code,
360
- } : undefined,
361
- },
362
- logs: 'BLOCKCHAIN LOGS ARE NOT AVAILABLE IN DEBUGGER BETA',
363
- debugLogs,
364
- };
365
- }
366
207
  }
367
208
  exports.Executor = Executor;