@ton/sandbox 0.19.0 → 0.21.0-beta.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
  });