@ton/sandbox 0.11.1 → 0.13.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,24 @@ 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.13.0] - 2023-10-05
9
+
10
+ ### Changed
11
+
12
+ - On transaction emulation error, an `EmulationError` is now thrown that has an `error` string, `vmLogs`, and `exitCode` (the latter two being optional). The error is no longer being dumped into console
13
+
14
+ ## [0.12.0] - 2023-10-03
15
+
16
+ ### Added
17
+
18
+ - Step by step execution (`blockchain.sendMessageIter`)
19
+ - Better docs
20
+
21
+ ### Fixed
22
+
23
+ - `now` from `Blockchain` is now honored in `SmartContract.receiveMessage`
24
+ - Exit code 1 is now counted as success in get methods
25
+
8
26
  ## [0.11.1] - 2023-07-26
9
27
 
10
28
  ### Changed
package/README.md CHANGED
@@ -261,9 +261,12 @@ it('minter admin should be able to mint jettons', async () => {
261
261
  });
262
262
  ```
263
263
 
264
- ### Test examples
265
- You can typically find various tests for Sandbox-based project contracts in the `./tests` directory. Some examples can be found in the following [list](https://docs.ton.org/develop/smart-contracts/examples#examples-of-tests-for-smart-contracts).
264
+ ### Test Examples
265
+ You can typically find various tests for Sandbox-based project contracts in the `./tests` directory.
266
+ Learn more from examples:
266
267
 
268
+ * [FunC Test Examples](https://docs.ton.org/develop/smart-contracts/examples#examples-of-tests-for-smart-contracts)
269
+ * [Tact Test Examples](docs/tact-testing-examples.md)
267
270
 
268
271
  ## Viewing logs
269
272
 
@@ -88,11 +88,15 @@ export declare class Blockchain {
88
88
  get config(): Cell;
89
89
  get configBase64(): string;
90
90
  sendMessage(message: Message | Cell, params?: MessageParams): Promise<SendMessageResult>;
91
+ sendMessageIter(message: Message | Cell, params?: MessageParams): Promise<AsyncIterator<BlockchainTransaction> & AsyncIterable<BlockchainTransaction>>;
91
92
  runTickTock(on: Address | Address[], which: TickOrTock, params?: MessageParams): Promise<SendMessageResult>;
92
93
  runGetMethod(address: Address, method: number | string, stack?: TupleItem[], params?: GetMethodParams): Promise<import("./SmartContract").GetMethodResult>;
93
94
  protected pushMessage(message: Message | Cell): Promise<void>;
94
95
  protected pushTickTock(on: Address, which: TickOrTock): Promise<void>;
95
96
  protected runQueue(params?: MessageParams): Promise<SendMessageResult>;
97
+ protected txIter(needsLocking: boolean, params?: MessageParams): AsyncIterator<BlockchainTransaction> & AsyncIterable<BlockchainTransaction>;
98
+ protected processInternal(params?: MessageParams): Promise<IteratorResult<BlockchainTransaction>>;
99
+ protected processTx(needsLocking: boolean, params?: MessageParams): Promise<IteratorResult<BlockchainTransaction>>;
96
100
  protected processQueue(params?: MessageParams): Promise<BlockchainTransaction[]>;
97
101
  provider(address: Address, init?: {
98
102
  code: Cell;
@@ -89,6 +89,15 @@ class Blockchain {
89
89
  await this.pushMessage(message);
90
90
  return await this.runQueue(params);
91
91
  }
92
+ async sendMessageIter(message, params) {
93
+ params = {
94
+ now: this.now,
95
+ ...params,
96
+ };
97
+ await this.pushMessage(message);
98
+ // Iterable will lock on per tx basis
99
+ return await this.txIter(true, params);
100
+ }
92
101
  async runTickTock(on, which, params) {
93
102
  for (const addr of (Array.isArray(on) ? on : [on])) {
94
103
  await this.pushTickTock(addr, which);
@@ -130,60 +139,80 @@ class Blockchain {
130
139
  externals: txes.map(tx => tx.externals).flat(),
131
140
  };
132
141
  }
142
+ txIter(needsLocking, params) {
143
+ const it = { next: () => this.processTx(needsLocking, params), [Symbol.asyncIterator]() { return it; } };
144
+ return it;
145
+ }
146
+ async processInternal(params) {
147
+ let result = undefined;
148
+ let done = this.messageQueue.length == 0;
149
+ while (!done) {
150
+ const message = this.messageQueue.shift();
151
+ let tx;
152
+ if (message.type === 'message') {
153
+ if (message.info.type === 'external-out') {
154
+ done = this.messageQueue.length == 0;
155
+ continue;
156
+ }
157
+ this.currentLt += LT_ALIGN;
158
+ tx = (await this.getContract(message.info.dest)).receiveMessage(message, params);
159
+ }
160
+ else {
161
+ this.currentLt += LT_ALIGN;
162
+ tx = (await this.getContract(message.on)).runTickTock(message.which, params);
163
+ }
164
+ const transaction = {
165
+ ...tx,
166
+ events: (0, Event_1.extractEvents)(tx),
167
+ parent: message.parentTransaction,
168
+ children: [],
169
+ externals: [],
170
+ };
171
+ transaction.parent?.children.push(transaction);
172
+ result = transaction;
173
+ done = true;
174
+ for (const message of transaction.outMessages.values()) {
175
+ if (message.info.type === 'external-out') {
176
+ transaction.externals.push({
177
+ info: {
178
+ type: 'external-out',
179
+ src: message.info.src,
180
+ dest: message.info.dest ?? undefined,
181
+ createdAt: message.info.createdAt,
182
+ createdLt: message.info.createdLt,
183
+ },
184
+ init: message.init ?? undefined,
185
+ body: message.body,
186
+ });
187
+ continue;
188
+ }
189
+ this.messageQueue.push({
190
+ type: 'message',
191
+ parentTransaction: transaction,
192
+ ...message,
193
+ });
194
+ if (message.info.type === 'internal') {
195
+ this.startFetchingContract(message.info.dest);
196
+ }
197
+ }
198
+ }
199
+ return result === undefined ? { value: result, done: true } : { value: result, done: false };
200
+ }
201
+ async processTx(needsLocking, params) {
202
+ // Lock only if not locked already
203
+ return needsLocking ? await this.lock.with(async () => this.processInternal(params)) : await this.processInternal(params);
204
+ }
133
205
  async processQueue(params) {
134
206
  params = {
135
207
  now: this.now,
136
208
  ...params,
137
209
  };
138
210
  return await this.lock.with(async () => {
211
+ // Locked already
212
+ const txs = this.txIter(false, params);
139
213
  const result = [];
140
- while (this.messageQueue.length > 0) {
141
- const message = this.messageQueue.shift();
142
- let tx;
143
- if (message.type === 'message') {
144
- if (message.info.type === 'external-out') {
145
- continue;
146
- }
147
- this.currentLt += LT_ALIGN;
148
- tx = (await this.getContract(message.info.dest)).receiveMessage(message, params);
149
- }
150
- else {
151
- this.currentLt += LT_ALIGN;
152
- tx = (await this.getContract(message.on)).runTickTock(message.which, params);
153
- }
154
- const transaction = {
155
- ...tx,
156
- events: (0, Event_1.extractEvents)(tx),
157
- parent: message.parentTransaction,
158
- children: [],
159
- externals: [],
160
- };
161
- transaction.parent?.children.push(transaction);
162
- result.push(transaction);
163
- for (const message of transaction.outMessages.values()) {
164
- if (message.info.type === 'external-out') {
165
- transaction.externals.push({
166
- info: {
167
- type: 'external-out',
168
- src: message.info.src,
169
- dest: message.info.dest ?? undefined,
170
- createdAt: message.info.createdAt,
171
- createdLt: message.info.createdLt,
172
- },
173
- init: message.init ?? undefined,
174
- body: message.body,
175
- });
176
- continue;
177
- }
178
- this.messageQueue.push({
179
- type: 'message',
180
- parentTransaction: transaction,
181
- ...message,
182
- });
183
- if (message.info.type === 'internal') {
184
- this.startFetchingContract(message.info.dest);
185
- }
186
- }
214
+ for await (const tx of txs) {
215
+ result.push(tx);
187
216
  }
188
217
  return result;
189
218
  });
@@ -55,6 +55,12 @@ export declare class TimeError extends Error {
55
55
  currentTime: number;
56
56
  constructor(address: Address, previousTxTime: number, currentTime: number);
57
57
  }
58
+ export declare class EmulationError extends Error {
59
+ error: string;
60
+ vmLogs?: string | undefined;
61
+ exitCode?: number | undefined;
62
+ constructor(error: string, vmLogs?: string | undefined, exitCode?: number | undefined);
63
+ }
58
64
  export type SmartContractSnapshot = {
59
65
  address: Address;
60
66
  account: ShardAccount;
@@ -12,7 +12,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
12
12
  };
13
13
  var _SmartContract_account, _SmartContract_parsedAccount, _SmartContract_lastTxTime, _SmartContract_verbosity;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.SmartContract = exports.TimeError = exports.GetMethodError = exports.createEmptyShardAccount = exports.createShardAccount = void 0;
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
18
  function createShardAccount(args) {
@@ -98,6 +98,15 @@ class TimeError extends Error {
98
98
  }
99
99
  }
100
100
  exports.TimeError = TimeError;
101
+ class EmulationError extends Error {
102
+ constructor(error, vmLogs, exitCode) {
103
+ super(`Error while executing transaction: ${error}`);
104
+ this.error = error;
105
+ this.vmLogs = vmLogs;
106
+ this.exitCode = exitCode;
107
+ }
108
+ }
109
+ exports.EmulationError = EmulationError;
101
110
  class SmartContract {
102
111
  constructor(shardAccount, blockchain) {
103
112
  _SmartContract_account.set(this, void 0);
@@ -181,6 +190,11 @@ class SmartContract {
181
190
  };
182
191
  }
183
192
  receiveMessage(message, params) {
193
+ // Sync now with blockchain instance if not specified in parameters
194
+ params = {
195
+ now: this.blockchain.now,
196
+ ...params,
197
+ };
184
198
  return this.runCommon(() => this.blockchain.executor.runTransaction({
185
199
  ...this.createCommonArgs(params),
186
200
  message: (0, core_1.beginCell)().store((0, core_1.storeMessage)(message)).endCell(),
@@ -198,8 +212,7 @@ class SmartContract {
198
212
  console.log(res.logs);
199
213
  }
200
214
  if (!res.result.success) {
201
- console.error('Error:', res.result.error, 'VM logs', res.result.vmResults);
202
- throw new Error('Error executing transaction');
215
+ throw new EmulationError(res.result.error, res.result.vmResults?.vmLog, res.result.vmResults?.vmExitCode);
203
216
  }
204
217
  if (this.verbosity.print && this.verbosity.vmLogs !== 'none' && res.result.vmLog.length > 0) {
205
218
  console.log(res.result.vmLog);
@@ -249,7 +262,7 @@ class SmartContract {
249
262
  if (this.verbosity.print && this.verbosity.debugLogs && res.debugLogs.length > 0) {
250
263
  console.log(res.debugLogs);
251
264
  }
252
- if (res.output.vm_exit_code !== 0) {
265
+ if (res.output.vm_exit_code !== 0 && res.output.vm_exit_code !== 1) {
253
266
  throw new GetMethodError(res.output.vm_exit_code, BigInt(res.output.gas_used), res.logs, res.output.vm_log, res.debugLogs);
254
267
  }
255
268
  const resStack = (0, core_1.parseTuple)(core_1.Cell.fromBase64(res.output.stack));
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export { Blockchain, SendMessageResult, BlockchainTransaction, PendingMessage, S
3
3
  export { BlockchainContractProvider, SandboxContractProvider, } from './blockchain/BlockchainContractProvider';
4
4
  export { BlockchainSender, } from './blockchain/BlockchainSender';
5
5
  export { BlockchainStorage, LocalBlockchainStorage, RemoteBlockchainStorage, RemoteBlockchainStorageClient, wrapTonClient4ForRemote, } from './blockchain/BlockchainStorage';
6
- export { Verbosity, LogsVerbosity, SmartContract, SmartContractTransaction, MessageParams, GetMethodParams, GetMethodResult, createEmptyShardAccount, createShardAccount, GetMethodError, TimeError, SmartContractSnapshot, } from './blockchain/SmartContract';
6
+ export { Verbosity, LogsVerbosity, SmartContract, SmartContractTransaction, MessageParams, GetMethodParams, GetMethodResult, createEmptyShardAccount, createShardAccount, GetMethodError, TimeError, SmartContractSnapshot, EmulationError, } from './blockchain/SmartContract';
7
7
  export { TickOrTock, } from './executor/Executor';
8
8
  export { Event, EventAccountCreated, EventAccountDestroyed, EventMessageSent, } from './event/Event';
9
9
  export { Treasury, TreasuryContract, } from './treasury/Treasury';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.internal = exports.printTransactionFees = exports.prettyLogTransactions = exports.prettyLogTransaction = exports.TreasuryContract = exports.TimeError = exports.GetMethodError = exports.createShardAccount = exports.createEmptyShardAccount = exports.SmartContract = exports.wrapTonClient4ForRemote = exports.RemoteBlockchainStorage = exports.LocalBlockchainStorage = exports.BlockchainSender = exports.BlockchainContractProvider = exports.Blockchain = exports.defaultConfigSeqno = exports.defaultConfig = void 0;
3
+ exports.internal = exports.printTransactionFees = exports.prettyLogTransactions = exports.prettyLogTransaction = exports.TreasuryContract = exports.EmulationError = exports.TimeError = exports.GetMethodError = exports.createShardAccount = exports.createEmptyShardAccount = exports.SmartContract = exports.wrapTonClient4ForRemote = exports.RemoteBlockchainStorage = exports.LocalBlockchainStorage = exports.BlockchainSender = exports.BlockchainContractProvider = exports.Blockchain = exports.defaultConfigSeqno = exports.defaultConfig = void 0;
4
4
  var defaultConfig_1 = require("./config/defaultConfig");
5
5
  Object.defineProperty(exports, "defaultConfig", { enumerable: true, get: function () { return defaultConfig_1.defaultConfig; } });
6
6
  Object.defineProperty(exports, "defaultConfigSeqno", { enumerable: true, get: function () { return defaultConfig_1.defaultConfigSeqno; } });
@@ -20,6 +20,7 @@ Object.defineProperty(exports, "createEmptyShardAccount", { enumerable: true, ge
20
20
  Object.defineProperty(exports, "createShardAccount", { enumerable: true, get: function () { return SmartContract_1.createShardAccount; } });
21
21
  Object.defineProperty(exports, "GetMethodError", { enumerable: true, get: function () { return SmartContract_1.GetMethodError; } });
22
22
  Object.defineProperty(exports, "TimeError", { enumerable: true, get: function () { return SmartContract_1.TimeError; } });
23
+ Object.defineProperty(exports, "EmulationError", { enumerable: true, get: function () { return SmartContract_1.EmulationError; } });
23
24
  var Treasury_1 = require("./treasury/Treasury");
24
25
  Object.defineProperty(exports, "TreasuryContract", { enumerable: true, get: function () { return Treasury_1.TreasuryContract; } });
25
26
  var prettyLogTransaction_1 = require("./utils/prettyLogTransaction");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ton/sandbox",
3
- "version": "0.11.1",
3
+ "version": "0.13.0",
4
4
  "description": "TON transaction emulator",
5
5
  "main": "dist/index.js",
6
6
  "license": "MIT",