@ton/sandbox 0.19.0 → 0.20.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,13 @@ 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.20.0] - 2024-05-31
9
+
10
+ ### Added
11
+
12
+ - Added the ability to create `Blockchain` using a custom `IExecutor` instead of the default `Executor`
13
+ - Added more information to `EmulationError`, extended its error message
14
+
8
15
  ## [0.19.0] - 2024-04-27
9
16
 
10
17
  ### Fixed
package/README.md CHANGED
@@ -13,7 +13,9 @@ The key difference of this package from [ton-contract-executor](https://github.c
13
13
  * [Test a transaction with matcher](#test-a-transaction-with-matcher)
14
14
  * [Testing transaction fees](#testing-transaction-fees)
15
15
  * [Cross contract tests](#cross-contract-tests)
16
+ * [Testing key points](#testing-key-points)
16
17
  * [Test examples](#test-examples)
18
+ * [Sandbox pitfalls](#sandbox-pitfalls)
17
19
  * [Viewing logs](#viewing-logs)
18
20
  * [Setting smart contract state directly](#setting-smart-contract-state-directly)
19
21
  * [Using snapshots](#using-snapshots)
@@ -265,6 +267,22 @@ it('minter admin should be able to mint jettons', async () => {
265
267
  });
266
268
  ```
267
269
 
270
+ ### Testing key points
271
+
272
+ In order to make sure that the contract will work as expected, you need to follow the following points in testing
273
+
274
+ * Test positive flows to make sure your contracts work
275
+ * Test negative flows to make sure that smart contracts behave correctly under abnormal conditions. Abnormal conditions includes:
276
+ * incorrect input
277
+ * action list overflow
278
+ * insufficient toncoin amount
279
+ * integer overflow
280
+ * owner assertions
281
+
282
+ More information about testing key points can be found here:
283
+ * [Testing key point](docs/testing-key-points.md)
284
+
285
+
268
286
  ### Test Examples
269
287
  You can typically find various tests for Sandbox-based project contracts in the `./tests` directory.
270
288
  Learn more from examples:
@@ -272,6 +290,36 @@ Learn more from examples:
272
290
  * [FunC Test Examples](https://docs.ton.org/develop/smart-contracts/examples#examples-of-tests-for-smart-contracts)
273
291
  * [Tact Test Examples](docs/tact-testing-examples.md)
274
292
 
293
+
294
+ ## Sandbox pitfalls
295
+
296
+ There are several pitfalls in the sandbox due to the limitations of emulation. Be aware of it while testing your smart contracts.
297
+
298
+ * Libs cells not updating in contract by `SETLIBCODE`, `CHANGELIB`. They need to be updated manually.
299
+ ```typescript
300
+ const blockchain = await Blockchain.create();
301
+ const code = await compile('Contract');
302
+
303
+ // consist of a hash of a lib cell and its representation
304
+ const libsDict = Dictionary.empty(Dictionary.Keys.Buffer(32), Dictionary.Values.Cell());
305
+ libsDict.set(code.hash(), code);
306
+
307
+ // manualy set libs
308
+ blockchain.libs = beginCell().storeDictDirect(libsDict).endCell();
309
+ ```
310
+ * There is no blocks in emulation, so opcodes like `PREVBLOCKSINFO`, `PREVMCBLOCKS`, `PREVKEYBLOCK` will return empty tuple.
311
+ * The randomness in the TON is always deterministic and the same randomSeed always gives the same random number sequence. If necessary, you can change the randomSeed to make `RAND` provide result based on provided seed. Currently, there is no way to provide randomSeed in opened contracts.
312
+ ```typescript
313
+ const res = await blockchain.runGetMethod(example.address,
314
+ 'get_method',
315
+ [],
316
+ { randomSeed: randomBytes(32) }
317
+ );
318
+ const stack = new TupleReader(res.stack);
319
+ // read data from stack ...
320
+ ```
321
+ * Because there is no concept of blocks in Sandbox, things like sharding do not work.
322
+
275
323
  ## Viewing logs
276
324
 
277
325
  `Blockchain` and `SmartContract` use `LogsVerbosity` to determine what kinds of logs to print. Here is the definition:
@@ -1,5 +1,5 @@
1
1
  import { Address, Cell, Message, Transaction, ContractProvider, Contract, Sender, ShardAccount, TupleItem, ExternalAddress, StateInit, OpenedContract } from "@ton/core";
2
- import { Executor, TickOrTock } from "../executor/Executor";
2
+ import { IExecutor, TickOrTock } from "../executor/Executor";
3
3
  import { BlockchainStorage } from "./BlockchainStorage";
4
4
  import { Event } from "../event/Event";
5
5
  import { SandboxContractProvider } from "./BlockchainContractProvider";
@@ -27,6 +27,12 @@ export type BlockchainTransaction = Transaction & {
27
27
  children: BlockchainTransaction[];
28
28
  externals: ExternalOut[];
29
29
  };
30
+ /**
31
+ * @type SendMessageResult Represents the result of sending a message.
32
+ * @property {BlockchainTransaction[]} transactions Array of blockchain transactions.
33
+ * @property {Event[]} events Array of blockchain events.
34
+ * @property {ExternalOut[]} externals - Array of external messages.
35
+ */
30
36
  export type SendMessageResult = {
31
37
  transactions: BlockchainTransaction[];
32
38
  events: Event[];
@@ -34,11 +40,20 @@ export type SendMessageResult = {
34
40
  };
35
41
  type ExtendsContractProvider<T> = T extends ContractProvider ? true : (T extends SandboxContractProvider ? true : false);
36
42
  export declare const SANDBOX_CONTRACT_SYMBOL: unique symbol;
43
+ /**
44
+ * @type SandboxContract Represents a sandbox contract.
45
+ * @template F Type parameter representing the original contract object.
46
+ */
37
47
  export type SandboxContract<F> = {
38
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 & {
39
49
  result: R extends Promise<infer PR> ? PR : R;
40
50
  }> : never) : never) : F[P]);
41
51
  };
52
+ /**
53
+ * Provide way to check if contract is in sandbox environment.
54
+ * @param contract Any open contract
55
+ * @throws Error if contract not a sandbox contract
56
+ */
42
57
  export declare function toSandboxContract<T>(contract: OpenedContract<T>): SandboxContract<T>;
43
58
  export type PendingMessage = (({
44
59
  type: 'message';
@@ -49,6 +64,13 @@ export type PendingMessage = (({
49
64
  })) & {
50
65
  parentTransaction?: BlockchainTransaction;
51
66
  };
67
+ /**
68
+ * @type TreasuryParams Parameters for configuring a treasury contract.
69
+ * @property {number} workchain The workchain ID of the treasury.
70
+ * @property {boolean} predeploy If set the treasury will be deployed on the moment of creation.
71
+ * @property {bigint} balance Initial balance of the treasury. If omitted 1_000_000 is used.
72
+ * @property {boolean} resetBalanceIfZero If set and treasury balance is zero on moment of calling method it reset balance to {@link balance}.
73
+ */
52
74
  export type TreasuryParams = Partial<{
53
75
  workchain: number;
54
76
  predeploy: boolean;
@@ -76,22 +98,111 @@ export declare class Blockchain {
76
98
  protected lock: AsyncLock;
77
99
  protected contractFetches: Map<string, Promise<SmartContract>>;
78
100
  protected nextCreateWalletIndex: number;
79
- readonly executor: Executor;
101
+ readonly executor: IExecutor;
102
+ /**
103
+ * Saves snapshot of current blockchain.
104
+ * ```ts
105
+ * const snapshot = blockchain.snapshot();
106
+ * // some operations
107
+ * await blockchain.loadFrom(snapshot); // restores blockchain state
108
+ * ```
109
+ */
80
110
  snapshot(): BlockchainSnapshot;
111
+ /**
112
+ * Restores blockchain state from snapshot.
113
+ * Usage provided in {@link snapshot}.
114
+ *
115
+ * @param snapshot Snapshot of blockchain
116
+ */
81
117
  loadFrom(snapshot: BlockchainSnapshot): Promise<void>;
118
+ /**
119
+ * @returns Current time in blockchain
120
+ */
82
121
  get now(): number | undefined;
122
+ /**
123
+ * Updates Current time in blockchain.
124
+ * @param now UNIX time to set
125
+ */
83
126
  set now(now: number | undefined);
127
+ /**
128
+ * @returns Current logical time in blockchain
129
+ */
84
130
  get lt(): bigint;
85
131
  protected constructor(opts: {
86
- executor: Executor;
132
+ executor: IExecutor;
87
133
  config?: BlockchainConfig;
88
134
  storage: BlockchainStorage;
89
135
  });
136
+ /**
137
+ * @returns Config used in blockchain.
138
+ */
90
139
  get config(): Cell;
140
+ /**
141
+ * @returns Config used in blockchain in base64 format.
142
+ */
91
143
  get configBase64(): string;
144
+ /**
145
+ * Emulates the result of sending a message to this Blockchain. Emulates the whole chain of transactions before returning the result. Each transaction increases lt by 1000000.
146
+ * ```ts
147
+ * const result = await blockchain.sendMessage(internal({
148
+ * from: sender.address,
149
+ * to: address,
150
+ * value: toNano('1'),
151
+ * body: beginCell().storeUint(0, 32).endCell(),
152
+ * }));
153
+ * ```
154
+ *
155
+ * @param message Message to sent
156
+ * @param params Optional params
157
+ * @returns Result of queue processing
158
+ */
92
159
  sendMessage(message: Message | Cell, params?: MessageParams): Promise<SendMessageResult>;
160
+ /**
161
+ * Starts emulating the result of sending a message to this Blockchain (refer to {@link sendMessage}). Each iterator call emulates one transaction, so the whole chain is not emulated immediately, unlike in {@link sendMessage}.
162
+ * ```ts
163
+ * const message = internal({
164
+ * from: sender.address,
165
+ * to: address,
166
+ * value: toNano('1'),
167
+ * body: beginCell().storeUint(0, 32).endCell(),
168
+ * }, { randomSeed: crypto.randomBytes(32) });
169
+ * for await (const tx of await blockchain.sendMessageIter(message)) {
170
+ * // process transaction
171
+ * }
172
+ * ```
173
+ *
174
+ * @param message Message to sent
175
+ * @param params Optional params
176
+ * @returns Async iterable of {@link BlockchainTransaction}
177
+ */
93
178
  sendMessageIter(message: Message | Cell, params?: MessageParams): Promise<AsyncIterator<BlockchainTransaction> & AsyncIterable<BlockchainTransaction>>;
179
+ /**
180
+ * Runs tick or tock transaction.
181
+ * ```ts
182
+ * let res = await blockchain.runTickTock(address, 'tock');
183
+ * ```
184
+ *
185
+ * @param on Address or addresses to run tick-tock
186
+ * @param which Type of transaction (tick or tock)
187
+ * @param [params] Params to run tick tock transaction
188
+ * @returns Result of tick-tock transaction
189
+ */
94
190
  runTickTock(on: Address | Address[], which: TickOrTock, params?: MessageParams): Promise<SendMessageResult>;
191
+ /**
192
+ * Runs get method on contract.
193
+ * ```ts
194
+ * const { stackReader } = await blockchain.runGetMethod(address, 'get_now', [], {
195
+ * now: 2,
196
+ * });
197
+ * const now = res.stackReader.readNumber();
198
+ * ```
199
+ *
200
+ * @param address Address or addresses to run get method
201
+ * @param method MethodId or method name to run
202
+ * @param stack Method params
203
+ * @param [params] Params to run get method
204
+ * @returns Result of get method
205
+ */
95
206
  runGetMethod(address: Address, method: number | string, stack?: TupleItem[], params?: GetMethodParams): Promise<import("./SmartContract").GetMethodResult>;
96
207
  protected pushMessage(message: Message | Cell): Promise<void>;
97
208
  protected pushTickTock(on: Address, which: TickOrTock): Promise<void>;
@@ -100,22 +211,120 @@ export declare class Blockchain {
100
211
  protected processInternal(params?: MessageParams): Promise<IteratorResult<BlockchainTransaction>>;
101
212
  protected processTx(needsLocking: boolean, params?: MessageParams): Promise<IteratorResult<BlockchainTransaction>>;
102
213
  protected processQueue(params?: MessageParams): Promise<BlockchainTransaction[]>;
214
+ /**
215
+ * Creates new {@link ContractProvider} for contract address.
216
+ * ```ts
217
+ * const contractProvider = blockchain.provider(address, init);
218
+ * const txs = await contractProvider.getTransactions(...);
219
+ * ```
220
+ *
221
+ * @param address Address to create contract provider for
222
+ * @param init Initial state of contract
223
+ */
103
224
  provider(address: Address, init?: StateInit | null): ContractProvider;
225
+ /**
226
+ * Creates {@link Sender} for address.
227
+ * ```ts
228
+ * const sender = this.sender(address);
229
+ * await contract.send(sender, ...);
230
+ * ```
231
+ *
232
+ * @param address Address to create sender for
233
+ */
104
234
  sender(address: Address): Sender;
105
235
  protected treasuryParamsToMapKey(workchain: number, seed: string): string;
236
+ /**
237
+ * Creates treasury wallet contract. This wallet is used as alternative to wallet-v4 smart contract.
238
+ * ```ts
239
+ * const wallet = await blockchain.treasury('wallet')
240
+ * await wallet.send({
241
+ * to: someAddress,
242
+ * value: toNano('0.5'),
243
+ * });
244
+ * ```
245
+ *
246
+ * @param {string} seed Initial seed for treasury. If the same seed is used to create a treasury, then these treasuries will be identical
247
+ * @param [params] Params for treasury creation. See {@link TreasuryParams} for more information.
248
+ */
106
249
  treasury(seed: string, params?: TreasuryParams): Promise<SandboxContract<TreasuryContract>>;
250
+ /**
251
+ * Bulk variant of {@link treasury}.
252
+ * ```ts
253
+ * const [wallet1, wallet2, wallet3] = await blockchain.createWallets(3);
254
+ * ```
255
+ * @param n Number of wallets to create
256
+ * @param params Params for treasury creation. See {@link TreasuryParams} for more information.
257
+ * @returns Array of opened treasury contracts
258
+ */
107
259
  createWallets(n: number, params?: TreasuryParams): Promise<SandboxContract<TreasuryContract>[]>;
260
+ /**
261
+ * Opens contract. Returns proxy that substitutes the blockchain Provider in methods starting with get and set.
262
+ * ```ts
263
+ * const contract = blockchain.openContract(new Contract(address));
264
+ * ```
265
+ *
266
+ * @param contract Contract to open.
267
+ */
108
268
  openContract<T extends Contract>(contract: T): SandboxContract<T>;
109
269
  protected startFetchingContract(address: Address): Promise<SmartContract>;
270
+ /**
271
+ * Retrieves {@link SmartContract} from {@link BlockchainStorage}.
272
+ * @param address Address of contract to get
273
+ */
110
274
  getContract(address: Address): Promise<SmartContract>;
275
+ /**
276
+ * @returns {LogsVerbosity} level
277
+ */
111
278
  get verbosity(): LogsVerbosity;
279
+ /**
280
+ * Updates logs verbosity level.
281
+ * @param {LogsVerbosity} value
282
+ */
112
283
  set verbosity(value: LogsVerbosity);
113
284
  setVerbosityForAddress(address: Address, verbosity: Partial<LogsVerbosity> | Verbosity | undefined): Promise<void>;
114
285
  setConfig(config: BlockchainConfig): void;
115
286
  setShardAccount(address: Address, account: ShardAccount): Promise<void>;
287
+ /**
288
+ * Retrieves global libs cell
289
+ */
116
290
  get libs(): Cell | undefined;
291
+ /**
292
+ * Update global blockchain libs.
293
+ * ```ts
294
+ * const code = await compile('Contract');
295
+ *
296
+ * const libsDict = Dictionary.empty(Dictionary.Keys.Buffer(32), Dictionary.Values.Cell());
297
+ * libsDict.set(code.hash(), code);
298
+ *
299
+ * blockchain.libs = beginCell().storeDictDirect(libsDict).endCell();
300
+ * ```
301
+ *
302
+ * @param value Cell in libs format: Dictionary<CellHash, Cell>
303
+ */
117
304
  set libs(value: Cell | undefined);
305
+ /**
306
+ * Creates instance of sandbox blockchain.
307
+ * ```ts
308
+ * const blockchain = await Blockchain.create({ config: 'slim' });
309
+ * ```
310
+ *
311
+ * Remote storage example:
312
+ * ```ts
313
+ * let client = new TonClient4({
314
+ * endpoint: 'https://mainnet-v4.tonhubapi.com'
315
+ * })
316
+ *
317
+ * let blockchain = await Blockchain.create({
318
+ * storage: new RemoteBlockchainStorage(wrapTonClient4ForRemote(client), 34892000)
319
+ * });
320
+ * ```
321
+ *
322
+ * @param [opts.executor] Custom contract executor. If omitted {@link Executor} used.
323
+ * @param [opts.config] Config used in blockchain. If omitted {@link defaultConfig} used.
324
+ * @param [opts.storage] Contracts storage used for blockchain. If omitted {@link LocalBlockchainStorage} used.
325
+ */
118
326
  static create(opts?: {
327
+ executor?: IExecutor;
119
328
  config?: BlockchainConfig;
120
329
  storage?: BlockchainStorage;
121
330
  }): Promise<Blockchain>;
@@ -19,6 +19,11 @@ function createWalletsSeed(idx) {
19
19
  }
20
20
  const LT_ALIGN = 1000000n;
21
21
  exports.SANDBOX_CONTRACT_SYMBOL = Symbol('SandboxContract');
22
+ /**
23
+ * Provide way to check if contract is in sandbox environment.
24
+ * @param contract Any open contract
25
+ * @throws Error if contract not a sandbox contract
26
+ */
22
27
  function toSandboxContract(contract) {
23
28
  if (contract[exports.SANDBOX_CONTRACT_SYMBOL] === true) {
24
29
  return contract;
@@ -38,6 +43,14 @@ function blockchainConfigToBase64(config) {
38
43
  }
39
44
  }
40
45
  class Blockchain {
46
+ /**
47
+ * Saves snapshot of current blockchain.
48
+ * ```ts
49
+ * const snapshot = blockchain.snapshot();
50
+ * // some operations
51
+ * await blockchain.loadFrom(snapshot); // restores blockchain state
52
+ * ```
53
+ */
41
54
  snapshot() {
42
55
  return {
43
56
  contracts: this.storage.knownContracts().map(s => s.snapshot()),
@@ -49,6 +62,12 @@ class Blockchain {
49
62
  nextCreateWalletIndex: this.nextCreateWalletIndex,
50
63
  };
51
64
  }
65
+ /**
66
+ * Restores blockchain state from snapshot.
67
+ * Usage provided in {@link snapshot}.
68
+ *
69
+ * @param snapshot Snapshot of blockchain
70
+ */
52
71
  async loadFrom(snapshot) {
53
72
  this.storage.clearKnownContracts();
54
73
  for (const contract of snapshot.contracts) {
@@ -62,12 +81,22 @@ class Blockchain {
62
81
  this.globalLibs = snapshot.libs;
63
82
  this.nextCreateWalletIndex = snapshot.nextCreateWalletIndex;
64
83
  }
84
+ /**
85
+ * @returns Current time in blockchain
86
+ */
65
87
  get now() {
66
88
  return this.currentTime;
67
89
  }
90
+ /**
91
+ * Updates Current time in blockchain.
92
+ * @param now UNIX time to set
93
+ */
68
94
  set now(now) {
69
95
  this.currentTime = now;
70
96
  }
97
+ /**
98
+ * @returns Current logical time in blockchain
99
+ */
71
100
  get lt() {
72
101
  return this.currentLt;
73
102
  }
@@ -87,16 +116,55 @@ class Blockchain {
87
116
  this.executor = opts.executor;
88
117
  this.storage = opts.storage;
89
118
  }
119
+ /**
120
+ * @returns Config used in blockchain.
121
+ */
90
122
  get config() {
91
123
  return core_1.Cell.fromBase64(this.networkConfig);
92
124
  }
125
+ /**
126
+ * @returns Config used in blockchain in base64 format.
127
+ */
93
128
  get configBase64() {
94
129
  return this.networkConfig;
95
130
  }
131
+ /**
132
+ * Emulates the result of sending a message to this Blockchain. Emulates the whole chain of transactions before returning the result. Each transaction increases lt by 1000000.
133
+ * ```ts
134
+ * const result = await blockchain.sendMessage(internal({
135
+ * from: sender.address,
136
+ * to: address,
137
+ * value: toNano('1'),
138
+ * body: beginCell().storeUint(0, 32).endCell(),
139
+ * }));
140
+ * ```
141
+ *
142
+ * @param message Message to sent
143
+ * @param params Optional params
144
+ * @returns Result of queue processing
145
+ */
96
146
  async sendMessage(message, params) {
97
147
  await this.pushMessage(message);
98
148
  return await this.runQueue(params);
99
149
  }
150
+ /**
151
+ * Starts emulating the result of sending a message to this Blockchain (refer to {@link sendMessage}). Each iterator call emulates one transaction, so the whole chain is not emulated immediately, unlike in {@link sendMessage}.
152
+ * ```ts
153
+ * const message = internal({
154
+ * from: sender.address,
155
+ * to: address,
156
+ * value: toNano('1'),
157
+ * body: beginCell().storeUint(0, 32).endCell(),
158
+ * }, { randomSeed: crypto.randomBytes(32) });
159
+ * for await (const tx of await blockchain.sendMessageIter(message)) {
160
+ * // process transaction
161
+ * }
162
+ * ```
163
+ *
164
+ * @param message Message to sent
165
+ * @param params Optional params
166
+ * @returns Async iterable of {@link BlockchainTransaction}
167
+ */
100
168
  async sendMessageIter(message, params) {
101
169
  params = {
102
170
  now: this.now,
@@ -106,12 +174,38 @@ class Blockchain {
106
174
  // Iterable will lock on per tx basis
107
175
  return await this.txIter(true, params);
108
176
  }
177
+ /**
178
+ * Runs tick or tock transaction.
179
+ * ```ts
180
+ * let res = await blockchain.runTickTock(address, 'tock');
181
+ * ```
182
+ *
183
+ * @param on Address or addresses to run tick-tock
184
+ * @param which Type of transaction (tick or tock)
185
+ * @param [params] Params to run tick tock transaction
186
+ * @returns Result of tick-tock transaction
187
+ */
109
188
  async runTickTock(on, which, params) {
110
189
  for (const addr of (Array.isArray(on) ? on : [on])) {
111
190
  await this.pushTickTock(addr, which);
112
191
  }
113
192
  return await this.runQueue(params);
114
193
  }
194
+ /**
195
+ * Runs get method on contract.
196
+ * ```ts
197
+ * const { stackReader } = await blockchain.runGetMethod(address, 'get_now', [], {
198
+ * now: 2,
199
+ * });
200
+ * const now = res.stackReader.readNumber();
201
+ * ```
202
+ *
203
+ * @param address Address or addresses to run get method
204
+ * @param method MethodId or method name to run
205
+ * @param stack Method params
206
+ * @param [params] Params to run get method
207
+ * @returns Result of get method
208
+ */
115
209
  async runGetMethod(address, method, stack = [], params) {
116
210
  return await (await this.getContract(address)).get(method, stack, {
117
211
  now: this.now,
@@ -225,6 +319,16 @@ class Blockchain {
225
319
  return result;
226
320
  });
227
321
  }
322
+ /**
323
+ * Creates new {@link ContractProvider} for contract address.
324
+ * ```ts
325
+ * const contractProvider = blockchain.provider(address, init);
326
+ * const txs = await contractProvider.getTransactions(...);
327
+ * ```
328
+ *
329
+ * @param address Address to create contract provider for
330
+ * @param init Initial state of contract
331
+ */
228
332
  provider(address, init) {
229
333
  return new BlockchainContractProvider_1.BlockchainContractProvider({
230
334
  getContract: (addr) => this.getContract(addr),
@@ -234,6 +338,15 @@ class Blockchain {
234
338
  openContract: (contract) => this.openContract(contract),
235
339
  }, address, init);
236
340
  }
341
+ /**
342
+ * Creates {@link Sender} for address.
343
+ * ```ts
344
+ * const sender = this.sender(address);
345
+ * await contract.send(sender, ...);
346
+ * ```
347
+ *
348
+ * @param address Address to create sender for
349
+ */
237
350
  sender(address) {
238
351
  return new BlockchainSender_1.BlockchainSender({
239
352
  pushMessage: (msg) => this.pushMessage(msg),
@@ -242,6 +355,19 @@ class Blockchain {
242
355
  treasuryParamsToMapKey(workchain, seed) {
243
356
  return `${workchain}:${seed}`;
244
357
  }
358
+ /**
359
+ * Creates treasury wallet contract. This wallet is used as alternative to wallet-v4 smart contract.
360
+ * ```ts
361
+ * const wallet = await blockchain.treasury('wallet')
362
+ * await wallet.send({
363
+ * to: someAddress,
364
+ * value: toNano('0.5'),
365
+ * });
366
+ * ```
367
+ *
368
+ * @param {string} seed Initial seed for treasury. If the same seed is used to create a treasury, then these treasuries will be identical
369
+ * @param [params] Params for treasury creation. See {@link TreasuryParams} for more information.
370
+ */
245
371
  async treasury(seed, params) {
246
372
  const subwalletId = (0, testTreasurySubwalletId_1.testSubwalletId)(seed);
247
373
  const wallet = this.openContract(Treasury_1.TreasuryContract.create(params?.workchain ?? 0, subwalletId));
@@ -260,6 +386,15 @@ class Blockchain {
260
386
  }
261
387
  return wallet;
262
388
  }
389
+ /**
390
+ * Bulk variant of {@link treasury}.
391
+ * ```ts
392
+ * const [wallet1, wallet2, wallet3] = await blockchain.createWallets(3);
393
+ * ```
394
+ * @param n Number of wallets to create
395
+ * @param params Params for treasury creation. See {@link TreasuryParams} for more information.
396
+ * @returns Array of opened treasury contracts
397
+ */
263
398
  async createWallets(n, params) {
264
399
  const wallets = [];
265
400
  for (let i = 0; i < n; i++) {
@@ -268,6 +403,14 @@ class Blockchain {
268
403
  }
269
404
  return wallets;
270
405
  }
406
+ /**
407
+ * Opens contract. Returns proxy that substitutes the blockchain Provider in methods starting with get and set.
408
+ * ```ts
409
+ * const contract = blockchain.openContract(new Contract(address));
410
+ * ```
411
+ *
412
+ * @param contract Contract to open.
413
+ */
271
414
  openContract(contract) {
272
415
  let address;
273
416
  let init = undefined;
@@ -329,6 +472,10 @@ class Blockchain {
329
472
  this.contractFetches.set(addrString, promise);
330
473
  return promise;
331
474
  }
475
+ /**
476
+ * Retrieves {@link SmartContract} from {@link BlockchainStorage}.
477
+ * @param address Address of contract to get
478
+ */
332
479
  async getContract(address) {
333
480
  try {
334
481
  const contract = await this.startFetchingContract(address);
@@ -341,9 +488,16 @@ class Blockchain {
341
488
  this.contractFetches.delete(address.toRawString());
342
489
  }
343
490
  }
491
+ /**
492
+ * @returns {LogsVerbosity} level
493
+ */
344
494
  get verbosity() {
345
495
  return this.logsVerbosity;
346
496
  }
497
+ /**
498
+ * Updates logs verbosity level.
499
+ * @param {LogsVerbosity} value
500
+ */
347
501
  set verbosity(value) {
348
502
  this.logsVerbosity = value;
349
503
  }
@@ -358,15 +512,52 @@ class Blockchain {
358
512
  const contract = await this.getContract(address);
359
513
  contract.account = account;
360
514
  }
515
+ /**
516
+ * Retrieves global libs cell
517
+ */
361
518
  get libs() {
362
519
  return this.globalLibs;
363
520
  }
521
+ /**
522
+ * Update global blockchain libs.
523
+ * ```ts
524
+ * const code = await compile('Contract');
525
+ *
526
+ * const libsDict = Dictionary.empty(Dictionary.Keys.Buffer(32), Dictionary.Values.Cell());
527
+ * libsDict.set(code.hash(), code);
528
+ *
529
+ * blockchain.libs = beginCell().storeDictDirect(libsDict).endCell();
530
+ * ```
531
+ *
532
+ * @param value Cell in libs format: Dictionary<CellHash, Cell>
533
+ */
364
534
  set libs(value) {
365
535
  this.globalLibs = value;
366
536
  }
537
+ /**
538
+ * Creates instance of sandbox blockchain.
539
+ * ```ts
540
+ * const blockchain = await Blockchain.create({ config: 'slim' });
541
+ * ```
542
+ *
543
+ * Remote storage example:
544
+ * ```ts
545
+ * let client = new TonClient4({
546
+ * endpoint: 'https://mainnet-v4.tonhubapi.com'
547
+ * })
548
+ *
549
+ * let blockchain = await Blockchain.create({
550
+ * storage: new RemoteBlockchainStorage(wrapTonClient4ForRemote(client), 34892000)
551
+ * });
552
+ * ```
553
+ *
554
+ * @param [opts.executor] Custom contract executor. If omitted {@link Executor} used.
555
+ * @param [opts.config] Config used in blockchain. If omitted {@link defaultConfig} used.
556
+ * @param [opts.storage] Contracts storage used for blockchain. If omitted {@link LocalBlockchainStorage} used.
557
+ */
367
558
  static async create(opts) {
368
559
  return new Blockchain({
369
- executor: await Executor_1.Executor.create(),
560
+ executor: opts?.executor ?? await Executor_1.Executor.create(),
370
561
  storage: opts?.storage ?? new BlockchainStorage_1.LocalBlockchainStorage(),
371
562
  ...opts
372
563
  });
@@ -5,6 +5,9 @@ import { GetMethodResult, SmartContract } from "./SmartContract";
5
5
  export interface SandboxContractProvider extends ContractProvider {
6
6
  tickTock(which: TickOrTock): Promise<void>;
7
7
  }
8
+ /**
9
+ * Provider used in contracts to send messages or invoke getters. For additional information see {@link Blockchain.provider}
10
+ */
8
11
  export declare class BlockchainContractProvider implements SandboxContractProvider {
9
12
  private readonly blockchain;
10
13
  private readonly address;
@@ -16,16 +19,40 @@ export declare class BlockchainContractProvider implements SandboxContractProvid
16
19
  pushTickTock(on: Address, which: TickOrTock): Promise<void>;
17
20
  openContract<T extends Contract>(contract: T): OpenedContract<T>;
18
21
  }, address: Address, init?: StateInit | null | undefined);
22
+ /**
23
+ * Opens contract. For additional information see {@link Blockchain.open}
24
+ */
19
25
  open<T extends Contract>(contract: T): OpenedContract<T>;
20
26
  getState(): Promise<ContractState>;
27
+ /**
28
+ * Invokes get method.
29
+ * @param name Name of get method
30
+ * @param args Args to invoke get method.
31
+ */
21
32
  get(name: string, args: TupleItem[]): Promise<ContractGetMethodResult>;
33
+ /**
34
+ * Dummy implementation of getTransactions. Sandbox does not store transactions, so its ContractProvider cannot fetch any.
35
+ * Throws error in every call.
36
+ *
37
+ * @throws {Error}
38
+ */
22
39
  getTransactions(address: Address, lt: bigint, hash: Buffer, limit?: number | undefined): Promise<Transaction[]>;
40
+ /**
41
+ * Pushes external-in message to message queue.
42
+ * @param message Message to push
43
+ */
23
44
  external(message: Cell): Promise<void>;
45
+ /**
46
+ * Pushes internal message to message queue.
47
+ */
24
48
  internal(via: Sender, args: {
25
49
  value: string | bigint;
26
50
  bounce?: boolean | null;
27
51
  sendMode?: SendMode;
28
52
  body?: string | Cell | null;
29
53
  }): Promise<void>;
54
+ /**
55
+ * Pushes tick-tock message to message queue.
56
+ */
30
57
  tickTock(which: TickOrTock): Promise<void>;
31
58
  }
@@ -32,12 +32,18 @@ function convertState(state) {
32
32
  };
33
33
  }
34
34
  }
35
+ /**
36
+ * Provider used in contracts to send messages or invoke getters. For additional information see {@link Blockchain.provider}
37
+ */
35
38
  class BlockchainContractProvider {
36
39
  constructor(blockchain, address, init) {
37
40
  this.blockchain = blockchain;
38
41
  this.address = address;
39
42
  this.init = init;
40
43
  }
44
+ /**
45
+ * Opens contract. For additional information see {@link Blockchain.open}
46
+ */
41
47
  open(contract) {
42
48
  return this.blockchain.openContract(contract);
43
49
  }
@@ -52,6 +58,11 @@ class BlockchainContractProvider {
52
58
  state: convertState(contract.accountState),
53
59
  };
54
60
  }
61
+ /**
62
+ * Invokes get method.
63
+ * @param name Name of get method
64
+ * @param args Args to invoke get method.
65
+ */
55
66
  async get(name, args) {
56
67
  const result = await this.blockchain.runGetMethod(this.address, name, args);
57
68
  const ret = {
@@ -63,9 +74,19 @@ class BlockchainContractProvider {
63
74
  delete ret.stackReader;
64
75
  return ret;
65
76
  }
77
+ /**
78
+ * Dummy implementation of getTransactions. Sandbox does not store transactions, so its ContractProvider cannot fetch any.
79
+ * Throws error in every call.
80
+ *
81
+ * @throws {Error}
82
+ */
66
83
  getTransactions(address, lt, hash, limit) {
67
84
  throw new Error("`getTransactions` is not implemented in `BlockchainContractProvider`, do not use it in the tests");
68
85
  }
86
+ /**
87
+ * Pushes external-in message to message queue.
88
+ * @param message Message to push
89
+ */
69
90
  async external(message) {
70
91
  const init = ((await this.getState()).state.type !== 'active' && this.init) ? this.init : undefined;
71
92
  await this.blockchain.pushMessage({
@@ -78,6 +99,9 @@ class BlockchainContractProvider {
78
99
  body: message,
79
100
  });
80
101
  }
102
+ /**
103
+ * Pushes internal message to message queue.
104
+ */
81
105
  async internal(via, args) {
82
106
  const init = ((await this.getState()).state.type !== 'active' && this.init) ? this.init : undefined;
83
107
  const bounce = (args.bounce !== null && args.bounce !== undefined) ? args.bounce : true;
@@ -92,6 +116,9 @@ class BlockchainContractProvider {
92
116
  body,
93
117
  });
94
118
  }
119
+ /**
120
+ * Pushes tick-tock message to message queue.
121
+ */
95
122
  async tickTock(which) {
96
123
  await this.blockchain.pushTickTock(this.address, which);
97
124
  }
@@ -1,4 +1,7 @@
1
1
  import { Address, Message, Sender, SenderArguments } from "@ton/core";
2
+ /**
3
+ * Sender for sandbox blockchain. For additional information see {@link Blockchain.sender}
4
+ */
2
5
  export declare class BlockchainSender implements Sender {
3
6
  private readonly blockchain;
4
7
  readonly address: Address;
@@ -2,6 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BlockchainSender = void 0;
4
4
  const core_1 = require("@ton/core");
5
+ /**
6
+ * Sender for sandbox blockchain. For additional information see {@link Blockchain.sender}
7
+ */
5
8
  class BlockchainSender {
6
9
  constructor(blockchain, address) {
7
10
  this.blockchain = blockchain;
@@ -2,6 +2,9 @@
2
2
  import { AccountState, Address } from "@ton/core";
3
3
  import { SmartContract } from "./SmartContract";
4
4
  import { Blockchain } from "./Blockchain";
5
+ /**
6
+ * @interface BlockchainStorage Provides information about contracts by blockchain
7
+ */
5
8
  export interface BlockchainStorage {
6
9
  getContract(blockchain: Blockchain, address: Address): Promise<SmartContract>;
7
10
  knownContracts(): SmartContract[];
@@ -24,6 +27,19 @@ export interface RemoteBlockchainStorageClient {
24
27
  };
25
28
  }>;
26
29
  }
30
+ /**
31
+ * Wraps ton client for remote storage.
32
+ *
33
+ * ```ts
34
+ * let client = new TonClient4({
35
+ * endpoint: 'https://mainnet-v4.tonhubapi.com'
36
+ * })
37
+ *
38
+ * let remoteStorageClient = wrapTonClient4ForRemote(client);
39
+ * ```
40
+ *
41
+ * @param client TonClient4 to wrap
42
+ */
27
43
  export declare function wrapTonClient4ForRemote(client: {
28
44
  getLastBlock(): Promise<{
29
45
  last: {
@@ -52,6 +68,18 @@ export declare function wrapTonClient4ForRemote(client: {
52
68
  };
53
69
  }>;
54
70
  }): RemoteBlockchainStorageClient;
71
+ /**
72
+ * @class {RemoteBlockchainStorage} Remote blockchain storage implementation.
73
+ * ```ts
74
+ * let client = new TonClient4({
75
+ * endpoint: 'https://mainnet-v4.tonhubapi.com'
76
+ * })
77
+ *
78
+ * let blockchain = await Blockchain.create({
79
+ * storage: new RemoteBlockchainStorage(wrapTonClient4ForRemote(client), 34892000)
80
+ * });
81
+ * ```
82
+ */
55
83
  export declare class RemoteBlockchainStorage implements BlockchainStorage {
56
84
  private contracts;
57
85
  private client;
@@ -35,6 +35,19 @@ function convertTonClient4State(state) {
35
35
  throw new Error(`Unknown type ${state}`);
36
36
  }
37
37
  }
38
+ /**
39
+ * Wraps ton client for remote storage.
40
+ *
41
+ * ```ts
42
+ * let client = new TonClient4({
43
+ * endpoint: 'https://mainnet-v4.tonhubapi.com'
44
+ * })
45
+ *
46
+ * let remoteStorageClient = wrapTonClient4ForRemote(client);
47
+ * ```
48
+ *
49
+ * @param client TonClient4 to wrap
50
+ */
38
51
  function wrapTonClient4ForRemote(client) {
39
52
  return {
40
53
  getLastBlockSeqno: async () => {
@@ -55,6 +68,18 @@ function wrapTonClient4ForRemote(client) {
55
68
  };
56
69
  }
57
70
  exports.wrapTonClient4ForRemote = wrapTonClient4ForRemote;
71
+ /**
72
+ * @class {RemoteBlockchainStorage} Remote blockchain storage implementation.
73
+ * ```ts
74
+ * let client = new TonClient4({
75
+ * endpoint: 'https://mainnet-v4.tonhubapi.com'
76
+ * })
77
+ *
78
+ * let blockchain = await Blockchain.create({
79
+ * storage: new RemoteBlockchainStorage(wrapTonClient4ForRemote(client), 34892000)
80
+ * });
81
+ * ```
82
+ */
58
83
  class RemoteBlockchainStorage {
59
84
  constructor(client, blockSeqno) {
60
85
  this.contracts = new Map();
@@ -59,7 +59,9 @@ export declare class EmulationError extends Error {
59
59
  error: string;
60
60
  vmLogs?: string | undefined;
61
61
  exitCode?: number | undefined;
62
- constructor(error: string, vmLogs?: string | undefined, exitCode?: number | undefined);
62
+ blockchainLogs?: string | undefined;
63
+ debugLogs?: string | undefined;
64
+ constructor(error: string, vmLogs?: string | undefined, exitCode?: number | undefined, blockchainLogs?: string | undefined, debugLogs?: string | undefined);
63
65
  }
64
66
  export type SmartContractSnapshot = {
65
67
  address: Address;
@@ -99,11 +99,26 @@ class TimeError extends Error {
99
99
  }
100
100
  exports.TimeError = TimeError;
101
101
  class EmulationError extends Error {
102
- constructor(error, vmLogs, exitCode) {
103
- super(`Error while executing transaction: ${error}`);
102
+ constructor(error, vmLogs, exitCode, blockchainLogs, debugLogs) {
103
+ let errMsg = `Error while executing transaction: ${error}`;
104
+ if (exitCode !== undefined) {
105
+ errMsg += `\nExit code: ${exitCode}`;
106
+ }
107
+ if (vmLogs !== undefined) {
108
+ errMsg += `\nVM logs:\n${vmLogs}`;
109
+ }
110
+ if (blockchainLogs !== undefined) {
111
+ errMsg += `\nBlockchain logs:\n${blockchainLogs}`;
112
+ }
113
+ if (debugLogs !== undefined) {
114
+ errMsg += `\nDebug logs:\n${debugLogs}`;
115
+ }
116
+ super(errMsg);
104
117
  this.error = error;
105
118
  this.vmLogs = vmLogs;
106
119
  this.exitCode = exitCode;
120
+ this.blockchainLogs = blockchainLogs;
121
+ this.debugLogs = debugLogs;
107
122
  }
108
123
  }
109
124
  exports.EmulationError = EmulationError;
@@ -212,7 +227,7 @@ class SmartContract {
212
227
  console.log(res.logs);
213
228
  }
214
229
  if (!res.result.success) {
215
- throw new EmulationError(res.result.error, res.result.vmResults?.vmLog, res.result.vmResults?.vmExitCode);
230
+ throw new EmulationError(res.result.error, res.result.vmResults?.vmLog, res.result.vmResults?.vmExitCode, res.logs.length === 0 ? undefined : res.logs, res.debugLogs.length === 0 ? undefined : res.debugLogs);
216
231
  }
217
232
  if (this.verbosity.print && this.verbosity.vmLogs !== 'none' && res.result.vmLog.length > 0) {
218
233
  console.log(res.result.vmLog);
@@ -2,6 +2,9 @@ import { Address, Cell, Contract, ContractProvider, MessageRelaxed, Sender, Send
2
2
  export type Treasury = Sender & {
3
3
  address: Address;
4
4
  };
5
+ /**
6
+ * @class TreasuryContract is Wallet v4 alternative. For additional information see {@link Blockchain.treasury}
7
+ */
5
8
  export declare class TreasuryContract implements Contract {
6
9
  static readonly code: Cell;
7
10
  static create(workchain: number, subwalletId: bigint): TreasuryContract;
@@ -9,10 +12,27 @@ export declare class TreasuryContract implements Contract {
9
12
  readonly init: StateInit;
10
13
  readonly subwalletId: bigint;
11
14
  constructor(workchain: number, subwalletId: bigint);
15
+ /**
16
+ * Send bulk messages using one external message.
17
+ * @param messages Messages to send
18
+ * @param sendMode Send mode of every message
19
+ */
12
20
  sendMessages(provider: ContractProvider, messages: MessageRelaxed[], sendMode?: SendMode): Promise<void>;
21
+ /**
22
+ * Sends message by arguments specified.
23
+ */
13
24
  send(provider: ContractProvider, args: SenderArguments): Promise<void>;
25
+ /**
26
+ * @returns Sender
27
+ */
14
28
  getSender(provider: ContractProvider): Treasury;
29
+ /**
30
+ * @returns wallet balance in nanoTONs
31
+ */
15
32
  getBalance(provider: ContractProvider): Promise<bigint>;
33
+ /**
34
+ * Creates transfer cell for {@link sendMessages}.
35
+ */
16
36
  createTransfer(args: {
17
37
  messages: MessageRelaxed[];
18
38
  sendMode?: SendMode;
@@ -22,6 +22,9 @@ function senderArgsToMessageRelaxed(args) {
22
22
  bounce: args.bounce
23
23
  });
24
24
  }
25
+ /**
26
+ * @class TreasuryContract is Wallet v4 alternative. For additional information see {@link Blockchain.treasury}
27
+ */
25
28
  class TreasuryContract {
26
29
  static create(workchain, subwalletId) {
27
30
  return new TreasuryContract(workchain, subwalletId);
@@ -34,6 +37,11 @@ class TreasuryContract {
34
37
  this.address = (0, core_1.contractAddress)(workchain, this.init);
35
38
  this.subwalletId = subwalletId;
36
39
  }
40
+ /**
41
+ * Send bulk messages using one external message.
42
+ * @param messages Messages to send
43
+ * @param sendMode Send mode of every message
44
+ */
37
45
  async sendMessages(provider, messages, sendMode) {
38
46
  let transfer = this.createTransfer({
39
47
  sendMode: sendMode,
@@ -41,9 +49,15 @@ class TreasuryContract {
41
49
  });
42
50
  await provider.external(transfer);
43
51
  }
52
+ /**
53
+ * Sends message by arguments specified.
54
+ */
44
55
  async send(provider, args) {
45
56
  await this.sendMessages(provider, [senderArgsToMessageRelaxed(args)], args.sendMode ?? undefined);
46
57
  }
58
+ /**
59
+ * @returns Sender
60
+ */
47
61
  getSender(provider) {
48
62
  return {
49
63
  address: this.address,
@@ -56,9 +70,15 @@ class TreasuryContract {
56
70
  }
57
71
  };
58
72
  }
73
+ /**
74
+ * @returns wallet balance in nanoTONs
75
+ */
59
76
  async getBalance(provider) {
60
77
  return (await provider.getState()).balance;
61
78
  }
79
+ /**
80
+ * Creates transfer cell for {@link sendMessages}.
81
+ */
62
82
  createTransfer(args) {
63
83
  let sendMode = core_1.SendMode.PAY_GAS_SEPARATELY;
64
84
  if (args.sendMode !== null && args.sendMode !== undefined) {
@@ -1,4 +1,7 @@
1
1
  import { Address, Cell, Message, StateInit } from "@ton/core";
2
+ /**
3
+ * Creates {@link Message} from params.
4
+ */
2
5
  export declare function internal(params: {
3
6
  from: Address;
4
7
  to: Address;
@@ -2,6 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.internal = void 0;
4
4
  const core_1 = require("@ton/core");
5
+ /**
6
+ * Creates {@link Message} from params.
7
+ */
5
8
  function internal(params) {
6
9
  return {
7
10
  info: {
@@ -1,3 +1,16 @@
1
1
  import { Transaction } from "@ton/core";
2
+ /**
3
+ * @param tx Transaction to create log string
4
+ * @returns Transaction log string
5
+ */
2
6
  export declare function prettyLogTransaction(tx: Transaction): string;
7
+ /**
8
+ * Log transaction using `console.log`. Logs base on result of {@link prettyLogTransaction}.
9
+ * Example output:
10
+ * ```
11
+ * null ➡️ EQBGhqLAZseEqRXz4ByFPTGV7SVMlI4hrbs-Sps_Xzx01x8G
12
+ * ➡️ 0.05 💎 EQC2VluVfpj2FoHNMAiDMpcMzwvjLZxxTG8ecq477RE3NvVt
13
+ * ```
14
+ * @param txs Transactions to log
15
+ */
3
16
  export declare function prettyLogTransactions(txs: Transaction[]): void;
@@ -2,6 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.prettyLogTransactions = exports.prettyLogTransaction = void 0;
4
4
  const core_1 = require("@ton/core");
5
+ /**
6
+ * @param tx Transaction to create log string
7
+ * @returns Transaction log string
8
+ */
5
9
  function prettyLogTransaction(tx) {
6
10
  let res = `${tx.inMessage?.info.src} ➡️ ${tx.inMessage?.info.dest}\n`;
7
11
  for (let message of tx.outMessages.values()) {
@@ -15,6 +19,15 @@ function prettyLogTransaction(tx) {
15
19
  return res;
16
20
  }
17
21
  exports.prettyLogTransaction = prettyLogTransaction;
22
+ /**
23
+ * Log transaction using `console.log`. Logs base on result of {@link prettyLogTransaction}.
24
+ * Example output:
25
+ * ```
26
+ * null ➡️ EQBGhqLAZseEqRXz4ByFPTGV7SVMlI4hrbs-Sps_Xzx01x8G
27
+ * ➡️ 0.05 💎 EQC2VluVfpj2FoHNMAiDMpcMzwvjLZxxTG8ecq477RE3NvVt
28
+ * ```
29
+ * @param txs Transactions to log
30
+ */
18
31
  function prettyLogTransactions(txs) {
19
32
  let out = '';
20
33
  for (let tx of txs) {
@@ -1,3 +1,16 @@
1
1
  import { Transaction } from "@ton/core";
2
2
  export declare function formatCoinsPure(value: bigint, precision?: number): string;
3
+ /**
4
+ * Prints transaction fees.
5
+ * Example output:
6
+ * ```
7
+ * ┌─────────┬─────────────┬────────────────┬────────────────┬────────────────┬────────────────┬───────────────┬────────────┬────────────────┬──────────┬────────────┐
8
+ * │ (index) │ op │ valueIn │ valueOut │ totalFees │ inForwardFee │ outForwardFee │ outActions │ computeFee │ exitCode │ actionCode │
9
+ * ├─────────┼─────────────┼────────────────┼────────────────┼────────────────┼────────────────┼───────────────┼────────────┼────────────────┼──────────┼────────────┤
10
+ * │ 0 │ 'N/A' │ 'N/A' │ '1000 TON' │ '0.004007 TON' │ 'N/A' │ '0.001 TON' │ 1 │ '0.001937 TON' │ 0 │ 0 │
11
+ * │ 1 │ '0x45ab564' │ '1000 TON' │ '998.8485 TON' │ '1.051473 TON' │ '0.000667 TON' │ '0.255 TON' │ 255 │ '0.966474 TON' │ 0 │ 0 │
12
+ * │ 2 │ '0x0' │ '3.917053 TON' │ '0 TON' │ '0.00031 TON' │ '0.000667 TON' │ 'N/A' │ 0 │ '0.000309 TON' │ 0 │ 0 │
13
+ * ```
14
+ * @param transactions List of transaction to print fees
15
+ */
3
16
  export declare function printTransactionFees(transactions: Transaction[]): void;
@@ -30,6 +30,19 @@ function formatCoins(value, precision = 6) {
30
30
  return 'N/A';
31
31
  return formatCoinsPure(value, precision) + ' TON';
32
32
  }
33
+ /**
34
+ * Prints transaction fees.
35
+ * Example output:
36
+ * ```
37
+ * ┌─────────┬─────────────┬────────────────┬────────────────┬────────────────┬────────────────┬───────────────┬────────────┬────────────────┬──────────┬────────────┐
38
+ * │ (index) │ op │ valueIn │ valueOut │ totalFees │ inForwardFee │ outForwardFee │ outActions │ computeFee │ exitCode │ actionCode │
39
+ * ├─────────┼─────────────┼────────────────┼────────────────┼────────────────┼────────────────┼───────────────┼────────────┼────────────────┼──────────┼────────────┤
40
+ * │ 0 │ 'N/A' │ 'N/A' │ '1000 TON' │ '0.004007 TON' │ 'N/A' │ '0.001 TON' │ 1 │ '0.001937 TON' │ 0 │ 0 │
41
+ * │ 1 │ '0x45ab564' │ '1000 TON' │ '998.8485 TON' │ '1.051473 TON' │ '0.000667 TON' │ '0.255 TON' │ 255 │ '0.966474 TON' │ 0 │ 0 │
42
+ * │ 2 │ '0x0' │ '3.917053 TON' │ '0 TON' │ '0.00031 TON' │ '0.000667 TON' │ 'N/A' │ 0 │ '0.000309 TON' │ 0 │ 0 │
43
+ * ```
44
+ * @param transactions List of transaction to print fees
45
+ */
33
46
  function printTransactionFees(transactions) {
34
47
  console.table(transactions
35
48
  .map((tx) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ton/sandbox",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "description": "TON transaction emulator",
5
5
  "main": "dist/index.js",
6
6
  "license": "MIT",