@ton/sandbox 0.37.0-tvmbeta.1 → 0.37.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 +10 -0
- package/README.md +29 -0
- package/dist/blockchain/Blockchain.d.ts +2 -14
- package/dist/blockchain/BlockchainSnapshot.d.ts +61 -0
- package/dist/blockchain/BlockchainSnapshot.js +116 -0
- package/dist/config/defaultConfig.d.ts +2 -2
- package/dist/config/defaultConfig.js +2 -2
- package/dist/config/slimConfig.d.ts +2 -2
- package/dist/config/slimConfig.js +2 -2
- package/dist/executor/emulator-emscripten.js +1 -1
- package/dist/executor/emulator-emscripten.wasm.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -1
- package/dist/treasury/Treasury.js +1 -0
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,16 @@ 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.37.0] - 2025-08-18
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Added `snapshotToSerializable` and `snapshotFromSerializable` utilities to enable snapshot serialization (e.g., to JSON), allowing snapshots to be saved, transferred, and restored easily.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- Switched to Jest 30
|
|
17
|
+
|
|
8
18
|
## [0.36.0] - 2025-08-04
|
|
9
19
|
|
|
10
20
|
### Added
|
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@ The key difference of this package from [ton-contract-executor](https://github.c
|
|
|
23
23
|
* [Viewing logs](#viewing-logs)
|
|
24
24
|
* [Setting smart contract state directly](#setting-smart-contract-state-directly)
|
|
25
25
|
* [Using snapshots](#using-snapshots)
|
|
26
|
+
* [Serializing snapshots](#serializing-snapshots)
|
|
26
27
|
* [Performing testing on contracts from a real network](#performing-testing-on-contracts-from-a-real-network)
|
|
27
28
|
* [Step-by-step execution](#step-by-step-execution)
|
|
28
29
|
* [Network/Block configuration](#networkblock-configuration)
|
|
@@ -666,11 +667,13 @@ Note that this is a low-level function and does not check any invariants, such a
|
|
|
666
667
|
It is possible to store the whole `Blockchain` state in an object and restore this state later. This can be useful to compare the outcomes of different actions after a certain point, or to store the state of the contract system after a long series of configuration actions in order to quickly restore it for all required tests instead of setting it up each time.
|
|
667
668
|
|
|
668
669
|
To store the state, do the following:
|
|
670
|
+
|
|
669
671
|
```typescript
|
|
670
672
|
const snapshot = blockchain.snapshot()
|
|
671
673
|
```
|
|
672
674
|
|
|
673
675
|
To restore the state, do the following:
|
|
676
|
+
|
|
674
677
|
```typescript
|
|
675
678
|
await blockchain.loadFrom(snapshot)
|
|
676
679
|
```
|
|
@@ -686,6 +689,32 @@ Note: snapshots store **the entire state** of a `Blockchain` instance, that incl
|
|
|
686
689
|
|
|
687
690
|
Basically, the state of a `Blockchain` instance after it is restored using a snapshot is the same as if the same actions were performed on that instance as on the instance from which the snapshot originates.
|
|
688
691
|
|
|
692
|
+
### Serializing snapshots
|
|
693
|
+
|
|
694
|
+
Snapshots contain `Cell`s and `Buffer`s, which are **not JSON-safe**. Use the provided helpers to convert snapshots to/from plain JSON objects:
|
|
695
|
+
|
|
696
|
+
```ts
|
|
697
|
+
import { snapshotToSerializable, snapshotFromSerializable } from './BlockchainSnapshot';
|
|
698
|
+
|
|
699
|
+
const serializable = snapshotToSerializable(blockchain.snapshot());
|
|
700
|
+
const restored = snapshotFromSerializable(serializable);
|
|
701
|
+
await blockchain.loadFrom(restored);
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
This is useful for saving snapshots to disk or sending them over the network.
|
|
705
|
+
|
|
706
|
+
```ts
|
|
707
|
+
import fs from 'fs';
|
|
708
|
+
|
|
709
|
+
fs.writeFileSync(
|
|
710
|
+
'snapshot.json',
|
|
711
|
+
JSON.stringify(snapshotToSerializable(blockchain.snapshot()))
|
|
712
|
+
);
|
|
713
|
+
|
|
714
|
+
const snapshotJson = JSON.parse(fs.readFileSync('snapshot.json', 'utf8'));
|
|
715
|
+
await blockchain.loadFrom(snapshotFromSerializable(snapshotJson));
|
|
716
|
+
```
|
|
717
|
+
|
|
689
718
|
## Performing testing on contracts from a real network
|
|
690
719
|
|
|
691
720
|
It is possible to use Sandbox to perform tests on contracts that are deployed to a real network. To do that, create your `Blockchain` instance using a `RemoteBlockchainStorage`, like so:
|
|
@@ -4,11 +4,12 @@ import { BlockchainStorage } from './BlockchainStorage';
|
|
|
4
4
|
import { Event } from '../event/Event';
|
|
5
5
|
import { SandboxContractProvider } from './BlockchainContractProvider';
|
|
6
6
|
import { TreasuryContract } from '../treasury/Treasury';
|
|
7
|
-
import { GetMethodParams, GetMethodResult, LogsVerbosity, MessageParams, SmartContract,
|
|
7
|
+
import { GetMethodParams, GetMethodResult, LogsVerbosity, MessageParams, SmartContract, SmartContractTransaction, Verbosity } from './SmartContract';
|
|
8
8
|
import { ContractsMeta } from '../meta/ContractsMeta';
|
|
9
9
|
import { Coverage } from '../coverage';
|
|
10
10
|
import { MessageQueueManager } from './MessageQueueManager';
|
|
11
11
|
import { AsyncLock } from '../utils/AsyncLock';
|
|
12
|
+
import { BlockchainSnapshot } from './BlockchainSnapshot';
|
|
12
13
|
export type ExternalOutInfo = {
|
|
13
14
|
type: 'external-out';
|
|
14
15
|
src: Address;
|
|
@@ -80,19 +81,6 @@ export type TreasuryParams = Partial<{
|
|
|
80
81
|
resetBalanceIfZero: boolean;
|
|
81
82
|
}>;
|
|
82
83
|
export type BlockchainConfig = Cell | 'default' | 'slim';
|
|
83
|
-
export type BlockchainSnapshot = {
|
|
84
|
-
contracts: SmartContractSnapshot[];
|
|
85
|
-
networkConfig: string;
|
|
86
|
-
lt: bigint;
|
|
87
|
-
time?: number;
|
|
88
|
-
verbosity: LogsVerbosity;
|
|
89
|
-
libs?: Cell;
|
|
90
|
-
nextCreateWalletIndex: number;
|
|
91
|
-
prevBlocksInfo?: PrevBlocksInfo;
|
|
92
|
-
randomSeed?: Buffer;
|
|
93
|
-
autoDeployLibs: boolean;
|
|
94
|
-
transactions: BlockchainTransaction[];
|
|
95
|
-
};
|
|
96
84
|
export type SendMessageIterParams = MessageParams & {
|
|
97
85
|
allowParallel?: boolean;
|
|
98
86
|
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Cell } from '@ton/core';
|
|
2
|
+
import { LogsVerbosity, SmartContractSnapshot } from './SmartContract';
|
|
3
|
+
import { BlockId, PrevBlocksInfo } from '../executor/Executor';
|
|
4
|
+
import { BlockchainTransaction } from './Blockchain';
|
|
5
|
+
export type BlockchainSnapshot = {
|
|
6
|
+
contracts: SmartContractSnapshot[];
|
|
7
|
+
networkConfig: string;
|
|
8
|
+
lt: bigint;
|
|
9
|
+
time?: number;
|
|
10
|
+
verbosity: LogsVerbosity;
|
|
11
|
+
libs?: Cell;
|
|
12
|
+
nextCreateWalletIndex: number;
|
|
13
|
+
prevBlocksInfo?: PrevBlocksInfo;
|
|
14
|
+
randomSeed?: Buffer;
|
|
15
|
+
autoDeployLibs: boolean;
|
|
16
|
+
transactions: BlockchainTransaction[];
|
|
17
|
+
};
|
|
18
|
+
export type SerializableBlockId = {
|
|
19
|
+
workchain: number;
|
|
20
|
+
shard: string;
|
|
21
|
+
seqno: number;
|
|
22
|
+
rootHash: string;
|
|
23
|
+
fileHash: string;
|
|
24
|
+
};
|
|
25
|
+
export declare function blockIdToSerializable(blockId: BlockId): SerializableBlockId;
|
|
26
|
+
export type SerializableSnapshot = {
|
|
27
|
+
contracts: {
|
|
28
|
+
address: string;
|
|
29
|
+
account: string;
|
|
30
|
+
lastTxTime: number;
|
|
31
|
+
verbosity?: Partial<LogsVerbosity>;
|
|
32
|
+
}[];
|
|
33
|
+
networkConfig: string;
|
|
34
|
+
lt: string;
|
|
35
|
+
time?: number;
|
|
36
|
+
verbosity: LogsVerbosity;
|
|
37
|
+
libs?: string;
|
|
38
|
+
nextCreateWalletIndex: number;
|
|
39
|
+
prevBlocksInfo?: {
|
|
40
|
+
lastMcBlocks: SerializableBlockId[];
|
|
41
|
+
prevKeyBlock: SerializableBlockId;
|
|
42
|
+
lastMcBlocks100?: SerializableBlockId[];
|
|
43
|
+
};
|
|
44
|
+
randomSeed?: string;
|
|
45
|
+
autoDeployLibs: boolean;
|
|
46
|
+
transactions: {
|
|
47
|
+
transaction: string;
|
|
48
|
+
blockchainLogs: string;
|
|
49
|
+
vmLogs: string;
|
|
50
|
+
debugLogs: string;
|
|
51
|
+
oldStorage?: string;
|
|
52
|
+
newStorage?: string;
|
|
53
|
+
outActions?: string;
|
|
54
|
+
externals: string[];
|
|
55
|
+
mode?: number;
|
|
56
|
+
parentHash?: string;
|
|
57
|
+
childrenHashes: string[];
|
|
58
|
+
}[];
|
|
59
|
+
};
|
|
60
|
+
export declare function snapshotToSerializable(snapshot: BlockchainSnapshot): SerializableSnapshot;
|
|
61
|
+
export declare function snapshotFromSerializable(serialized: SerializableSnapshot): BlockchainSnapshot;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.blockIdToSerializable = blockIdToSerializable;
|
|
4
|
+
exports.snapshotToSerializable = snapshotToSerializable;
|
|
5
|
+
exports.snapshotFromSerializable = snapshotFromSerializable;
|
|
6
|
+
const core_1 = require("@ton/core");
|
|
7
|
+
const Event_1 = require("../event/Event");
|
|
8
|
+
function blockIdToSerializable(blockId) {
|
|
9
|
+
return {
|
|
10
|
+
workchain: blockId.workchain,
|
|
11
|
+
shard: blockId.shard.toString(),
|
|
12
|
+
seqno: blockId.seqno,
|
|
13
|
+
rootHash: blockId.rootHash.toString('hex'),
|
|
14
|
+
fileHash: blockId.fileHash.toString('hex'),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function writableToBase64(writer) {
|
|
18
|
+
return (0, core_1.beginCell)().store(writer).endCell().toBoc().toString('base64');
|
|
19
|
+
}
|
|
20
|
+
function snapshotToSerializable(snapshot) {
|
|
21
|
+
return {
|
|
22
|
+
contracts: snapshot.contracts.map((contract) => ({
|
|
23
|
+
account: writableToBase64((0, core_1.storeShardAccount)(contract.account)),
|
|
24
|
+
address: contract.address.toString(),
|
|
25
|
+
lastTxTime: contract.lastTxTime,
|
|
26
|
+
verbosity: contract.verbosity,
|
|
27
|
+
})),
|
|
28
|
+
networkConfig: snapshot.networkConfig,
|
|
29
|
+
lt: snapshot.lt.toString(),
|
|
30
|
+
time: snapshot.time,
|
|
31
|
+
verbosity: snapshot.verbosity,
|
|
32
|
+
libs: snapshot.libs?.toBoc().toString('base64'),
|
|
33
|
+
nextCreateWalletIndex: snapshot.nextCreateWalletIndex,
|
|
34
|
+
prevBlocksInfo: snapshot.prevBlocksInfo
|
|
35
|
+
? {
|
|
36
|
+
lastMcBlocks: snapshot.prevBlocksInfo.lastMcBlocks.map(blockIdToSerializable),
|
|
37
|
+
prevKeyBlock: blockIdToSerializable(snapshot.prevBlocksInfo.prevKeyBlock),
|
|
38
|
+
lastMcBlocks100: snapshot.prevBlocksInfo.lastMcBlocks100?.map(blockIdToSerializable),
|
|
39
|
+
}
|
|
40
|
+
: undefined,
|
|
41
|
+
randomSeed: snapshot.randomSeed?.toString('hex'),
|
|
42
|
+
autoDeployLibs: snapshot.autoDeployLibs,
|
|
43
|
+
transactions: snapshot.transactions.map((transaction) => {
|
|
44
|
+
return {
|
|
45
|
+
transaction: writableToBase64((0, core_1.storeTransaction)(transaction)),
|
|
46
|
+
blockchainLogs: transaction.blockchainLogs,
|
|
47
|
+
vmLogs: transaction.vmLogs,
|
|
48
|
+
debugLogs: transaction.debugLogs,
|
|
49
|
+
oldStorage: transaction.oldStorage?.toBoc().toString('base64'),
|
|
50
|
+
newStorage: transaction.newStorage?.toBoc().toString('base64'),
|
|
51
|
+
outActions: transaction.outActions ? writableToBase64((0, core_1.storeOutList)(transaction.outActions)) : undefined,
|
|
52
|
+
externals: transaction.externals.map((external) => writableToBase64((0, core_1.storeMessageRelaxed)(external))),
|
|
53
|
+
mode: transaction.mode,
|
|
54
|
+
parentHash: transaction.parent?.hash().toString('hex'),
|
|
55
|
+
childrenHashes: transaction.children.map((tx) => tx.hash().toString('hex')),
|
|
56
|
+
};
|
|
57
|
+
}),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function blockIdFromSerializable(s) {
|
|
61
|
+
return {
|
|
62
|
+
workchain: s.workchain,
|
|
63
|
+
shard: BigInt(s.shard),
|
|
64
|
+
seqno: s.seqno,
|
|
65
|
+
rootHash: Buffer.from(s.rootHash, 'hex'),
|
|
66
|
+
fileHash: Buffer.from(s.fileHash, 'hex'),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function snapshotFromSerializable(serialized) {
|
|
70
|
+
const transactions = serialized.transactions.map((t) => {
|
|
71
|
+
const transaction = (0, core_1.loadTransaction)(core_1.Cell.fromBase64(t.transaction).beginParse());
|
|
72
|
+
return {
|
|
73
|
+
...transaction,
|
|
74
|
+
blockchainLogs: t.blockchainLogs,
|
|
75
|
+
vmLogs: t.vmLogs,
|
|
76
|
+
debugLogs: t.debugLogs,
|
|
77
|
+
oldStorage: t.oldStorage ? core_1.Cell.fromBase64(t.oldStorage) : undefined,
|
|
78
|
+
newStorage: t.newStorage ? core_1.Cell.fromBase64(t.newStorage) : undefined,
|
|
79
|
+
outActions: t.outActions ? (0, core_1.loadOutList)(core_1.Cell.fromBase64(t.outActions).beginParse()) : undefined,
|
|
80
|
+
events: (0, Event_1.extractEvents)(transaction),
|
|
81
|
+
mode: t.mode,
|
|
82
|
+
externals: t.externals.map((ext) => (0, core_1.loadMessageRelaxed)(core_1.Cell.fromBase64(ext).beginParse())),
|
|
83
|
+
children: [],
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
const transactionsMap = new Map(transactions.map((tx) => [tx.hash().toString('hex'), tx]));
|
|
87
|
+
for (let i = 0; i < transactions.length; i++) {
|
|
88
|
+
const { parentHash, childrenHashes } = serialized.transactions[i];
|
|
89
|
+
transactions[i].parent = parentHash ? transactionsMap.get(parentHash) : undefined;
|
|
90
|
+
transactions[i].children = childrenHashes.map((hash) => transactionsMap.get(hash));
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
contracts: serialized.contracts.map((contract) => ({
|
|
94
|
+
address: core_1.Address.parse(contract.address),
|
|
95
|
+
account: (0, core_1.loadShardAccount)(core_1.Cell.fromBase64(contract.account).beginParse()),
|
|
96
|
+
lastTxTime: contract.lastTxTime,
|
|
97
|
+
verbosity: contract.verbosity,
|
|
98
|
+
})),
|
|
99
|
+
networkConfig: serialized.networkConfig,
|
|
100
|
+
lt: BigInt(serialized.lt),
|
|
101
|
+
time: serialized.time,
|
|
102
|
+
verbosity: serialized.verbosity,
|
|
103
|
+
libs: serialized.libs ? core_1.Cell.fromBase64(serialized.libs) : undefined,
|
|
104
|
+
nextCreateWalletIndex: serialized.nextCreateWalletIndex,
|
|
105
|
+
prevBlocksInfo: serialized.prevBlocksInfo
|
|
106
|
+
? {
|
|
107
|
+
lastMcBlocks: serialized.prevBlocksInfo.lastMcBlocks.map(blockIdFromSerializable),
|
|
108
|
+
prevKeyBlock: blockIdFromSerializable(serialized.prevBlocksInfo.prevKeyBlock),
|
|
109
|
+
lastMcBlocks100: serialized.prevBlocksInfo.lastMcBlocks100?.map(blockIdFromSerializable),
|
|
110
|
+
}
|
|
111
|
+
: undefined,
|
|
112
|
+
randomSeed: serialized.randomSeed ? Buffer.from(serialized.randomSeed, 'hex') : undefined,
|
|
113
|
+
autoDeployLibs: serialized.autoDeployLibs,
|
|
114
|
+
transactions,
|
|
115
|
+
};
|
|
116
|
+
}
|