@ton/sandbox 0.18.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 +13 -0
- package/README.md +48 -0
- package/dist/blockchain/Blockchain.d.ts +212 -3
- package/dist/blockchain/Blockchain.js +192 -1
- package/dist/blockchain/BlockchainContractProvider.d.ts +27 -0
- package/dist/blockchain/BlockchainContractProvider.js +27 -0
- package/dist/blockchain/BlockchainSender.d.ts +3 -0
- package/dist/blockchain/BlockchainSender.js +3 -0
- package/dist/blockchain/BlockchainStorage.d.ts +28 -0
- package/dist/blockchain/BlockchainStorage.js +25 -0
- package/dist/blockchain/SmartContract.d.ts +3 -1
- package/dist/blockchain/SmartContract.js +18 -3
- package/dist/executor/emulator-emscripten.js +1 -1
- package/dist/executor/emulator-emscripten.wasm.js +1 -1
- package/dist/treasury/Treasury.d.ts +20 -0
- package/dist/treasury/Treasury.js +20 -0
- package/dist/utils/message.d.ts +3 -0
- package/dist/utils/message.js +3 -0
- package/dist/utils/prettyLogTransaction.d.ts +13 -0
- package/dist/utils/prettyLogTransaction.js +13 -0
- package/dist/utils/printTransactionFees.d.ts +13 -0
- package/dist/utils/printTransactionFees.js +13 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,19 @@ 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
|
+
|
|
15
|
+
## [0.19.0] - 2024-04-27
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Fixed a bug in the emulator that caused send mode 16 to not properly work
|
|
20
|
+
|
|
8
21
|
## [0.18.0] - 2024-04-23
|
|
9
22
|
|
|
10
23
|
### Changed
|
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 {
|
|
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:
|
|
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:
|
|
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
|
});
|