@ton/sandbox 0.38.0 → 0.40.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 +22 -0
- package/dist/blockchain/Blockchain.d.ts +15 -1
- package/dist/blockchain/Blockchain.js +45 -8
- package/dist/blockchain/MessageQueueManager.d.ts +2 -2
- package/dist/blockchain/MessageQueueManager.js +6 -3
- package/dist/blockchain/SmartContract.d.ts +3 -2
- package/dist/blockchain/SmartContract.js +5 -4
- package/dist/executor/emulator-emscripten.debugger.bpatch.gzip.js +1 -1
- package/dist/executor/emulator-emscripten.debugger.js +2 -2
- package/dist/executor/emulator-emscripten.js +2 -20
- package/dist/executor/emulator-emscripten.wasm.js +1 -1
- package/dist/jest/uiSetup.d.ts +1 -0
- package/dist/jest/uiSetup.js +38 -0
- package/dist/ui/UIManager.d.ts +18 -0
- package/dist/ui/UIManager.js +31 -0
- package/dist/ui/connection/UIConnector.d.ts +3 -0
- package/dist/ui/connection/UIConnector.js +2 -0
- package/dist/ui/connection/websocket/ManagedWebSocketConnector.d.ts +10 -0
- package/dist/ui/connection/websocket/ManagedWebSocketConnector.js +38 -0
- package/dist/ui/connection/websocket/OneTimeWebSocketConnector.d.ts +7 -0
- package/dist/ui/connection/websocket/OneTimeWebSocketConnector.js +31 -0
- package/dist/ui/connection/websocket/constants.d.ts +3 -0
- package/dist/ui/connection/websocket/constants.js +6 -0
- package/dist/ui/connection/websocket/types.d.ts +4 -0
- package/dist/ui/connection/websocket/types.js +2 -0
- package/dist/ui/protocol.d.ts +48 -0
- package/dist/ui/protocol.js +48 -0
- package/dist/utils/environment.d.ts +7 -0
- package/dist/utils/environment.js +15 -0
- package/dist/utils/noop.d.ts +1 -0
- package/dist/utils/noop.js +4 -0
- package/dist/utils/require.d.ts +1 -1
- package/dist/utils/require.js +5 -8
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,28 @@ 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.40.0] - 2025-12-04
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Sandbox UI support
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- Updated emulator WASM binary
|
|
17
|
+
- Updated dependencies
|
|
18
|
+
- `Blockchain` now allows null code and data in state init
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Fixed `@ton/test-utils` import
|
|
23
|
+
|
|
24
|
+
## [0.39.0] - 2025-10-29
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Updated emulator WASM binary
|
|
29
|
+
|
|
8
30
|
## [0.38.0] - 2025-10-15
|
|
9
31
|
|
|
10
32
|
### Added
|
|
@@ -10,6 +10,9 @@ import { Coverage } from '../coverage';
|
|
|
10
10
|
import { MessageQueueManager } from './MessageQueueManager';
|
|
11
11
|
import { AsyncLock } from '../utils/AsyncLock';
|
|
12
12
|
import { BlockchainSnapshot } from './BlockchainSnapshot';
|
|
13
|
+
import { IUIConnector } from '../ui/connection/UIConnector';
|
|
14
|
+
import { WebSocketConnectionOptions } from '../ui/connection/websocket/types';
|
|
15
|
+
import { IUIManager } from '../ui/UIManager';
|
|
13
16
|
export type ExternalOutInfo = {
|
|
14
17
|
type: 'external-out';
|
|
15
18
|
src: Address;
|
|
@@ -59,6 +62,7 @@ export type SandboxContract<F> = {
|
|
|
59
62
|
export declare function toSandboxContract<T>(contract: OpenedContract<T>): SandboxContract<T>;
|
|
60
63
|
export type PendingMessage = (({
|
|
61
64
|
type: 'message';
|
|
65
|
+
callStack?: string;
|
|
62
66
|
mode?: number;
|
|
63
67
|
} & Message) | {
|
|
64
68
|
type: 'ticktock';
|
|
@@ -84,6 +88,10 @@ export type BlockchainConfig = Cell | 'default' | 'slim';
|
|
|
84
88
|
export type SendMessageIterParams = MessageParams & {
|
|
85
89
|
allowParallel?: boolean;
|
|
86
90
|
};
|
|
91
|
+
export type UIOptions = {
|
|
92
|
+
enabled?: boolean;
|
|
93
|
+
connector?: IUIConnector;
|
|
94
|
+
} & WebSocketConnectionOptions;
|
|
87
95
|
export declare class Blockchain {
|
|
88
96
|
protected lock: AsyncLock;
|
|
89
97
|
protected storage: BlockchainStorage;
|
|
@@ -102,6 +110,7 @@ export declare class Blockchain {
|
|
|
102
110
|
protected autoDeployLibs: boolean;
|
|
103
111
|
protected transactions: BlockchainTransaction[];
|
|
104
112
|
protected defaultQueueManager: MessageQueueManager;
|
|
113
|
+
protected uiManager: IUIManager;
|
|
105
114
|
protected collectCoverage: boolean;
|
|
106
115
|
protected readonly coverageTransactions: BlockchainTransaction[][];
|
|
107
116
|
protected readonly coverageGetMethodResults: GetMethodResult[];
|
|
@@ -155,7 +164,9 @@ export declare class Blockchain {
|
|
|
155
164
|
storage: BlockchainStorage;
|
|
156
165
|
meta?: ContractsMeta;
|
|
157
166
|
autoDeployLibs?: boolean;
|
|
167
|
+
uiOptions?: UIOptions;
|
|
158
168
|
});
|
|
169
|
+
protected createUiManager(opts?: UIOptions): IUIManager;
|
|
159
170
|
protected createQueueManager(): MessageQueueManager;
|
|
160
171
|
/**
|
|
161
172
|
* @returns Config used in blockchain.
|
|
@@ -332,11 +343,12 @@ export declare class Blockchain {
|
|
|
332
343
|
* Opens contract. Returns proxy that substitutes the blockchain Provider in methods starting with get and set.
|
|
333
344
|
*
|
|
334
345
|
* @param contract Contract to open.
|
|
346
|
+
* @param name Name of the contract.
|
|
335
347
|
*
|
|
336
348
|
* @example
|
|
337
349
|
* const contract = blockchain.openContract(new Contract(address));
|
|
338
350
|
*/
|
|
339
|
-
openContract<T extends Contract>(contract: T): SandboxContract<T>;
|
|
351
|
+
openContract<T extends Contract>(contract: T, name?: string): SandboxContract<T>;
|
|
340
352
|
protected startFetchingContract(address: Address): Promise<SmartContract>;
|
|
341
353
|
/**
|
|
342
354
|
* Retrieves {@link SmartContract} from {@link BlockchainStorage}.
|
|
@@ -444,6 +456,7 @@ export declare class Blockchain {
|
|
|
444
456
|
* @param [opts.storage] Contracts storage used for blockchain. If omitted {@link LocalBlockchainStorage} is used.
|
|
445
457
|
* @param [opts.meta] Optional contracts metadata provider. If not provided, {@link @ton/test-utils.contractsMeta} will be used to accumulate contracts metadata.
|
|
446
458
|
* @param [opts.autoDeployLibs] Optional flag. If set to true, libraries will be collected automatically
|
|
459
|
+
* @param [opts.uiOptions] Optional object to configure the UI connector.
|
|
447
460
|
* @example
|
|
448
461
|
* const blockchain = await Blockchain.create({ config: 'slim' });
|
|
449
462
|
*
|
|
@@ -462,6 +475,7 @@ export declare class Blockchain {
|
|
|
462
475
|
storage?: BlockchainStorage;
|
|
463
476
|
meta?: ContractsMeta;
|
|
464
477
|
autoDeployLibs?: boolean;
|
|
478
|
+
uiOptions?: UIOptions;
|
|
465
479
|
}): Promise<Blockchain>;
|
|
466
480
|
}
|
|
467
481
|
export {};
|
|
@@ -19,6 +19,10 @@ const coverage_1 = require("../coverage");
|
|
|
19
19
|
const MessageQueueManager_1 = require("./MessageQueueManager");
|
|
20
20
|
const AsyncLock_1 = require("../utils/AsyncLock");
|
|
21
21
|
const require_1 = require("../utils/require");
|
|
22
|
+
const noop_1 = require("../utils/noop");
|
|
23
|
+
const environment_1 = require("../utils/environment");
|
|
24
|
+
const OneTimeWebSocketConnector_1 = require("../ui/connection/websocket/OneTimeWebSocketConnector");
|
|
25
|
+
const UIManager_1 = require("../ui/UIManager");
|
|
22
26
|
const CREATE_WALLETS_PREFIX = 'CREATE_WALLETS';
|
|
23
27
|
function createWalletsSeed(idx) {
|
|
24
28
|
return `${CREATE_WALLETS_PREFIX}${idx}`;
|
|
@@ -72,6 +76,7 @@ class Blockchain {
|
|
|
72
76
|
autoDeployLibs;
|
|
73
77
|
transactions = [];
|
|
74
78
|
defaultQueueManager;
|
|
79
|
+
uiManager;
|
|
75
80
|
collectCoverage = false;
|
|
76
81
|
coverageTransactions = [];
|
|
77
82
|
coverageGetMethodResults = [];
|
|
@@ -180,6 +185,18 @@ class Blockchain {
|
|
|
180
185
|
this.meta = opts.meta;
|
|
181
186
|
this.autoDeployLibs = opts.autoDeployLibs ?? false;
|
|
182
187
|
this.defaultQueueManager = this.createQueueManager();
|
|
188
|
+
this.uiManager = this.createUiManager(opts.uiOptions);
|
|
189
|
+
}
|
|
190
|
+
createUiManager(opts) {
|
|
191
|
+
if (!opts?.enabled) {
|
|
192
|
+
// noop implementation
|
|
193
|
+
return { publishTransactions: noop_1.noop };
|
|
194
|
+
}
|
|
195
|
+
const connector = opts.connector ?? new OneTimeWebSocketConnector_1.OneTimeWebSocketConnector(opts);
|
|
196
|
+
return new UIManager_1.UIManager(connector, {
|
|
197
|
+
getMeta: (address) => this.meta?.get(address),
|
|
198
|
+
knownContracts: () => this.storage.knownContracts(),
|
|
199
|
+
});
|
|
183
200
|
}
|
|
184
201
|
createQueueManager() {
|
|
185
202
|
return new MessageQueueManager_1.MessageQueueManager(this.lock, {
|
|
@@ -189,8 +206,11 @@ class Blockchain {
|
|
|
189
206
|
getLibs: () => this.libs,
|
|
190
207
|
setLibs: (value) => (this.libs = value),
|
|
191
208
|
getAutoDeployLibs: () => this.autoDeployLibs,
|
|
192
|
-
|
|
193
|
-
|
|
209
|
+
onTransactions: (txs) => {
|
|
210
|
+
this.registerTxsForCoverage(txs);
|
|
211
|
+
this.uiManager.publishTransactions(txs);
|
|
212
|
+
},
|
|
213
|
+
onTransaction: (transaction) => this.transactions.push(transaction),
|
|
194
214
|
});
|
|
195
215
|
}
|
|
196
216
|
/**
|
|
@@ -456,11 +476,12 @@ class Blockchain {
|
|
|
456
476
|
* Opens contract. Returns proxy that substitutes the blockchain Provider in methods starting with get and set.
|
|
457
477
|
*
|
|
458
478
|
* @param contract Contract to open.
|
|
479
|
+
* @param name Name of the contract.
|
|
459
480
|
*
|
|
460
481
|
* @example
|
|
461
482
|
* const contract = blockchain.openContract(new Contract(address));
|
|
462
483
|
*/
|
|
463
|
-
openContract(contract) {
|
|
484
|
+
openContract(contract, name) {
|
|
464
485
|
let address;
|
|
465
486
|
let init = undefined;
|
|
466
487
|
if (!core_1.Address.isAddress(contract.address)) {
|
|
@@ -468,15 +489,19 @@ class Blockchain {
|
|
|
468
489
|
}
|
|
469
490
|
address = contract.address;
|
|
470
491
|
if (contract.init) {
|
|
471
|
-
if (
|
|
492
|
+
if (contract.init.code !== undefined &&
|
|
493
|
+
contract.init.code !== null &&
|
|
494
|
+
!(contract.init.code instanceof core_1.Cell)) {
|
|
472
495
|
throw Error('Invalid init.code');
|
|
473
496
|
}
|
|
474
|
-
if (
|
|
497
|
+
if (contract.init.data !== undefined &&
|
|
498
|
+
contract.init.data !== null &&
|
|
499
|
+
!(contract.init.data instanceof core_1.Cell)) {
|
|
475
500
|
throw Error('Invalid init.data');
|
|
476
501
|
}
|
|
477
502
|
init = contract.init;
|
|
478
503
|
}
|
|
479
|
-
this.meta?.upsert(address, { wrapperName: contract?.constructor?.name, abi: contract.abi });
|
|
504
|
+
this.meta?.upsert(address, { wrapperName: name ?? contract?.constructor?.name, abi: contract.abi });
|
|
480
505
|
const provider = this.provider(address, init);
|
|
481
506
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
482
507
|
return new Proxy(contract, {
|
|
@@ -596,6 +621,7 @@ class Blockchain {
|
|
|
596
621
|
*/
|
|
597
622
|
enableCoverage(enable = true) {
|
|
598
623
|
this.collectCoverage = enable;
|
|
624
|
+
this.verbosity.print = false;
|
|
599
625
|
this.verbosity.vmLogs = 'vm_logs_verbose';
|
|
600
626
|
}
|
|
601
627
|
/**
|
|
@@ -678,6 +704,7 @@ class Blockchain {
|
|
|
678
704
|
* @param [opts.storage] Contracts storage used for blockchain. If omitted {@link LocalBlockchainStorage} is used.
|
|
679
705
|
* @param [opts.meta] Optional contracts metadata provider. If not provided, {@link @ton/test-utils.contractsMeta} will be used to accumulate contracts metadata.
|
|
680
706
|
* @param [opts.autoDeployLibs] Optional flag. If set to true, libraries will be collected automatically
|
|
707
|
+
* @param [opts.uiOptions] Optional object to configure the UI connector.
|
|
681
708
|
* @example
|
|
682
709
|
* const blockchain = await Blockchain.create({ config: 'slim' });
|
|
683
710
|
*
|
|
@@ -691,12 +718,22 @@ class Blockchain {
|
|
|
691
718
|
* });
|
|
692
719
|
*/
|
|
693
720
|
static async create(opts) {
|
|
694
|
-
|
|
721
|
+
const uiEnabled = opts?.uiOptions?.enabled ?? (0, environment_1.getOptionalEnv)('SANDBOX_UI_ENABLED', 'boolean');
|
|
722
|
+
const blockchain = new Blockchain({
|
|
695
723
|
executor: opts?.executor ?? (await Executor_1.Executor.create()),
|
|
696
724
|
storage: opts?.storage ?? new BlockchainStorage_1.LocalBlockchainStorage(),
|
|
697
|
-
meta: opts?.meta ?? (0, require_1.
|
|
725
|
+
meta: opts?.meta ?? (0, require_1.requireTestUtils)()?.contractsMeta,
|
|
698
726
|
...opts,
|
|
727
|
+
uiOptions: {
|
|
728
|
+
enabled: uiEnabled,
|
|
729
|
+
...opts?.uiOptions,
|
|
730
|
+
},
|
|
699
731
|
});
|
|
732
|
+
if (uiEnabled) {
|
|
733
|
+
blockchain.verbosity.print = false;
|
|
734
|
+
blockchain.verbosity.vmLogs = 'vm_logs_verbose';
|
|
735
|
+
}
|
|
736
|
+
return blockchain;
|
|
700
737
|
}
|
|
701
738
|
}
|
|
702
739
|
exports.Blockchain = Blockchain;
|
|
@@ -14,8 +14,8 @@ export declare class MessageQueueManager {
|
|
|
14
14
|
getLibs(): Cell | undefined;
|
|
15
15
|
setLibs(libs: Cell | undefined): void;
|
|
16
16
|
getAutoDeployLibs(): boolean;
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
onTransactions(txs: BlockchainTransaction[]): void;
|
|
18
|
+
onTransaction(transaction: BlockchainTransaction): void;
|
|
19
19
|
});
|
|
20
20
|
pushMessage(message: Message | Cell): Promise<void>;
|
|
21
21
|
pushTickTock(on: Address, which: TickOrTock): Promise<void>;
|
|
@@ -65,7 +65,7 @@ class MessageQueueManager {
|
|
|
65
65
|
}
|
|
66
66
|
return result;
|
|
67
67
|
});
|
|
68
|
-
this.blockchain.
|
|
68
|
+
this.blockchain.onTransactions(results);
|
|
69
69
|
return results;
|
|
70
70
|
}
|
|
71
71
|
async processMessage(params) {
|
|
@@ -73,16 +73,18 @@ class MessageQueueManager {
|
|
|
73
73
|
let done = this.messageQueue.length == 0;
|
|
74
74
|
while (!done) {
|
|
75
75
|
const message = this.messageQueue.shift();
|
|
76
|
+
let callStack;
|
|
76
77
|
let tx;
|
|
77
78
|
let smartContract;
|
|
78
79
|
if (message.type === 'message') {
|
|
80
|
+
callStack = message.callStack;
|
|
79
81
|
if (message.info.type === 'external-out') {
|
|
80
82
|
done = this.messageQueue.length == 0;
|
|
81
83
|
continue;
|
|
82
84
|
}
|
|
83
85
|
this.blockchain.increaseLt();
|
|
84
86
|
smartContract = await this.blockchain.getContract(message.info.dest);
|
|
85
|
-
tx = await smartContract.receiveMessage(message, params);
|
|
87
|
+
tx = await smartContract.receiveMessage(message, params, callStack);
|
|
86
88
|
}
|
|
87
89
|
else {
|
|
88
90
|
this.blockchain.increaseLt();
|
|
@@ -98,7 +100,7 @@ class MessageQueueManager {
|
|
|
98
100
|
mode: message.type === 'message' ? message.mode : undefined,
|
|
99
101
|
};
|
|
100
102
|
transaction.parent?.children.push(transaction);
|
|
101
|
-
this.blockchain.
|
|
103
|
+
this.blockchain.onTransaction(transaction);
|
|
102
104
|
result = transaction;
|
|
103
105
|
done = true;
|
|
104
106
|
const sendMsgActions = (transaction.outActions?.filter((action) => action.type === 'sendMsg') ??
|
|
@@ -122,6 +124,7 @@ class MessageQueueManager {
|
|
|
122
124
|
type: 'message',
|
|
123
125
|
parentTransaction: transaction,
|
|
124
126
|
mode: sendMsgActions[index]?.mode,
|
|
127
|
+
callStack,
|
|
125
128
|
...message,
|
|
126
129
|
});
|
|
127
130
|
if (message.info.type === 'internal') {
|
|
@@ -30,6 +30,7 @@ export type SmartContractTransaction = Transaction & {
|
|
|
30
30
|
blockchainLogs: string;
|
|
31
31
|
vmLogs: string;
|
|
32
32
|
debugLogs: string;
|
|
33
|
+
callStack?: string;
|
|
33
34
|
oldStorage?: Cell;
|
|
34
35
|
newStorage?: Cell;
|
|
35
36
|
outActions?: OutActionExtended[];
|
|
@@ -109,9 +110,9 @@ export declare class SmartContract {
|
|
|
109
110
|
}): SmartContract;
|
|
110
111
|
static empty(blockchain: Blockchain, address: Address): SmartContract;
|
|
111
112
|
protected createCommonArgs(params?: MessageParams): RunCommonArgs;
|
|
112
|
-
receiveMessage(message: Message, params?: MessageParams): Promise<SmartContractTransaction>;
|
|
113
|
+
receiveMessage(message: Message, params?: MessageParams, callStack?: string): Promise<SmartContractTransaction>;
|
|
113
114
|
runTickTock(which: TickOrTock, params?: MessageParams): Promise<SmartContractTransaction>;
|
|
114
|
-
protected runCommon(run: () => Promise<EmulationResult
|
|
115
|
+
protected runCommon(run: () => Promise<EmulationResult>, callStack?: string): Promise<SmartContractTransaction>;
|
|
115
116
|
get(method: string | number, stack?: TupleItem[], params?: GetMethodParams): Promise<GetMethodResult>;
|
|
116
117
|
get verbosity(): LogsVerbosity;
|
|
117
118
|
set verbosity(value: LogsVerbosity);
|
|
@@ -294,7 +294,7 @@ class SmartContract {
|
|
|
294
294
|
prevBlocksInfo: this.blockchain.prevBlocks,
|
|
295
295
|
};
|
|
296
296
|
}
|
|
297
|
-
async receiveMessage(message, params) {
|
|
297
|
+
async receiveMessage(message, params, callStack) {
|
|
298
298
|
const args = {
|
|
299
299
|
...this.createCommonArgs(params),
|
|
300
300
|
message: (0, core_1.beginCell)().store((0, core_1.storeMessage)(message)).endCell(),
|
|
@@ -304,14 +304,14 @@ class SmartContract {
|
|
|
304
304
|
const { uninitialized, debugInfo } = debugContext.getDebugInfo(this.account);
|
|
305
305
|
if (debugInfo !== undefined) {
|
|
306
306
|
const executor = await this.blockchain.getDebuggerExecutor();
|
|
307
|
-
return await this.runCommon(() => debugContext.debugTransaction(executor, args, debugInfo));
|
|
307
|
+
return await this.runCommon(() => debugContext.debugTransaction(executor, args, debugInfo), callStack);
|
|
308
308
|
}
|
|
309
309
|
else if (uninitialized) {
|
|
310
310
|
// eslint-disable-next-line no-console
|
|
311
311
|
console.log('Debugging uninitialized accounts is unsupported in debugger beta');
|
|
312
312
|
}
|
|
313
313
|
}
|
|
314
|
-
return await this.runCommon(() => this.blockchain.executor.runTransaction(args));
|
|
314
|
+
return await this.runCommon(() => this.blockchain.executor.runTransaction(args), callStack);
|
|
315
315
|
}
|
|
316
316
|
async runTickTock(which, params) {
|
|
317
317
|
return await this.runCommon(() => this.blockchain.executor.runTickTock({
|
|
@@ -319,7 +319,7 @@ class SmartContract {
|
|
|
319
319
|
which,
|
|
320
320
|
}));
|
|
321
321
|
}
|
|
322
|
-
async runCommon(run) {
|
|
322
|
+
async runCommon(run, callStack) {
|
|
323
323
|
let oldStorage = undefined;
|
|
324
324
|
if (this.blockchain.recordStorage && this.account.account?.storage.state.type === 'active') {
|
|
325
325
|
oldStorage = this.account.account?.storage.state.state.data ?? undefined;
|
|
@@ -357,6 +357,7 @@ class SmartContract {
|
|
|
357
357
|
blockchainLogs: res.logs,
|
|
358
358
|
vmLogs: res.result.vmLog,
|
|
359
359
|
debugLogs: res.debugLogs,
|
|
360
|
+
callStack,
|
|
360
361
|
oldStorage,
|
|
361
362
|
newStorage,
|
|
362
363
|
outActions,
|