@ton/sandbox 0.11.1 → 0.12.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,18 @@ 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.12.0] - 2023-10-03
9
+
10
+ ### Added
11
+
12
+ - Step by step execution (`blockchain.sendMessageIter`)
13
+ - Better docs
14
+
15
+ ### Fixed
16
+
17
+ - `now` from `Blockchain` is now honored in `SmartContract.receiveMessage`
18
+ - Exit code 1 is now counted as success in get methods
19
+
8
20
  ## [0.11.1] - 2023-07-26
9
21
 
10
22
  ### 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
  });
@@ -181,6 +181,11 @@ class SmartContract {
181
181
  };
182
182
  }
183
183
  receiveMessage(message, params) {
184
+ // Sync now with blockchain instance if not specified in parameters
185
+ params = {
186
+ now: this.blockchain.now,
187
+ ...params,
188
+ };
184
189
  return this.runCommon(() => this.blockchain.executor.runTransaction({
185
190
  ...this.createCommonArgs(params),
186
191
  message: (0, core_1.beginCell)().store((0, core_1.storeMessage)(message)).endCell(),
@@ -249,7 +254,7 @@ class SmartContract {
249
254
  if (this.verbosity.print && this.verbosity.debugLogs && res.debugLogs.length > 0) {
250
255
  console.log(res.debugLogs);
251
256
  }
252
- if (res.output.vm_exit_code !== 0) {
257
+ if (res.output.vm_exit_code !== 0 && res.output.vm_exit_code !== 1) {
253
258
  throw new GetMethodError(res.output.vm_exit_code, BigInt(res.output.gas_used), res.logs, res.output.vm_log, res.debugLogs);
254
259
  }
255
260
  const resStack = (0, core_1.parseTuple)(core_1.Cell.fromBase64(res.output.stack));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ton/sandbox",
3
- "version": "0.11.1",
3
+ "version": "0.12.0",
4
4
  "description": "TON transaction emulator",
5
5
  "main": "dist/index.js",
6
6
  "license": "MIT",