@ton/sandbox 0.11.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 +221 -0
- package/LICENSE +21 -0
- package/README.md +214 -0
- package/dist/blockchain/Blockchain.d.ts +120 -0
- package/dist/blockchain/Blockchain.js +327 -0
- package/dist/blockchain/BlockchainContractProvider.d.ts +30 -0
- package/dist/blockchain/BlockchainContractProvider.js +90 -0
- package/dist/blockchain/BlockchainSender.d.ts +9 -0
- package/dist/blockchain/BlockchainSender.js +29 -0
- package/dist/blockchain/BlockchainStorage.d.ts +64 -0
- package/dist/blockchain/BlockchainStorage.js +105 -0
- package/dist/blockchain/SmartContract.d.ts +93 -0
- package/dist/blockchain/SmartContract.js +289 -0
- package/dist/config/defaultConfig.d.ts +2 -0
- package/dist/config/defaultConfig.js +5 -0
- package/dist/config/slimConfig.d.ts +2 -0
- package/dist/config/slimConfig.js +5 -0
- package/dist/event/Event.d.ts +19 -0
- package/dist/event/Event.js +46 -0
- package/dist/executor/Executor.d.ts +90 -0
- package/dist/executor/Executor.js +208 -0
- package/dist/executor/emulator-emscripten.js +21 -0
- package/dist/executor/emulator-emscripten.wasm.js +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +31 -0
- package/dist/treasury/Treasury.d.ts +23 -0
- package/dist/treasury/Treasury.js +82 -0
- package/dist/utils/AsyncLock.d.ts +6 -0
- package/dist/utils/AsyncLock.js +48 -0
- package/dist/utils/base64.d.ts +1 -0
- package/dist/utils/base64.js +42 -0
- package/dist/utils/crc16.d.ts +2 -0
- package/dist/utils/crc16.js +49 -0
- package/dist/utils/message.d.ts +15 -0
- package/dist/utils/message.js +24 -0
- package/dist/utils/prettyLogTransaction.d.ts +3 -0
- package/dist/utils/prettyLogTransaction.js +25 -0
- package/dist/utils/printTransactionFees.d.ts +3 -0
- package/dist/utils/printTransactionFees.js +63 -0
- package/dist/utils/selector.d.ts +1 -0
- package/dist/utils/selector.js +19 -0
- package/dist/utils/testTreasurySubwalletId.d.ts +1 -0
- package/dist/utils/testTreasurySubwalletId.js +9 -0
- package/package.json +38 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.11.0] - 2023-05-11
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Added the ability to emulate ticktock transactions. There are 3 ways to do that: `blockchain.runTickTock(Address | Address[], TickOrTock, MessageParams?)`, `smartContract.runTickTock(TickOrTock, MessageParams?)`, or you can change `ContractProvider` in your wrapper classes to be `SandboxContractProvider` and invoke `tickTock(TickOrTock)` on it. `TickOrTock` is a union type `'tick' | 'tock'`
|
|
13
|
+
- Added new verbosity levels: `'vm_logs_location'` (same as `'vm_logs'` but also display code cell hash and offset), `'vm_logs_gas'` (same as `'vm_logs_location'` but also display gas remaining), `'vm_logs_verbose'` (same as `'vm_logs_full'` but display stack values in a more verbose way)
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Changed emulator WASM binary
|
|
18
|
+
|
|
19
|
+
## [0.10.0] - 2023-05-04
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Changed emulator WASM binary
|
|
24
|
+
- Changed treasury code
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- Fixed certain interactions between snapshots and treasuries
|
|
29
|
+
|
|
30
|
+
## [0.9.0] - 2023-04-27
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- Added `printTransactionFees` helper for easier calculation of fees of different operations
|
|
35
|
+
- Added `blockchain.snapshot`, `blockchain.loadFrom`, `smartContract.snapshot`, `smartContract.loadFrom` methods to create state snapshots of the respective objects and restore from them at a later point in time. They return and accept new types, `BlockchainSnapshot` and `SmartContractSnapshot`
|
|
36
|
+
|
|
37
|
+
## [0.8.0] - 2023-04-07
|
|
38
|
+
|
|
39
|
+
This release contains a breaking change.
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- Added `blockchain.createWallets` method which accepts a number `n` and optional `TreasuryParams`. It creates `n` treasuries and returns them as an array
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- `RemoteBlockchainStorage` now requires a `RemoteBlockchainStorageClient` instead of `TonClient4`. There is a helper function, `wrapTonClient4ForRemote`, to wrap a `TonClient4` into `RemoteBlockchainStorageClient`. This is a breaking change
|
|
48
|
+
- Updated default config
|
|
49
|
+
- `Blockchain.create` now accepts an optional `BlockchainConfig = Cell | 'default' | 'slim'` as the config. If nothing or `'default'` is specified, the default config is used, if `'slim'` is specified, the slim config is used (it is much smaller than the default config, which improves performance), if a `Cell` is passed, then it is used as the config
|
|
50
|
+
|
|
51
|
+
### Removed
|
|
52
|
+
|
|
53
|
+
- Removed ton as a peer dependency
|
|
54
|
+
|
|
55
|
+
## [0.7.0] - 2023-03-27
|
|
56
|
+
|
|
57
|
+
### Added
|
|
58
|
+
|
|
59
|
+
- Added `externals: ExternalOut[]` field to `BlockchainTransaction` and `SendMessageResult`. `ExternalOut` is a specialized type for external out messages compatible with `Message` from ton-core
|
|
60
|
+
|
|
61
|
+
### Changed
|
|
62
|
+
|
|
63
|
+
- Get methods now throw a specialized error type `GetMethodError` when exit code is not 0
|
|
64
|
+
- Smart contracts now throw a specialized error type `TimeError` when trying to run a transaction at a unix timestamp that is less than the unix timestamp of the last transaction
|
|
65
|
+
- Get methods now return `gasUsed` and `logs` from the `ContractProvider` on opened contracts
|
|
66
|
+
|
|
67
|
+
### Other
|
|
68
|
+
|
|
69
|
+
- Consecutive transaction emulations have been optimized
|
|
70
|
+
|
|
71
|
+
## [0.6.1] - 2023-03-16
|
|
72
|
+
|
|
73
|
+
### Fixed
|
|
74
|
+
|
|
75
|
+
- Fixed `blockchain.now` override for get methods in opened contracts
|
|
76
|
+
|
|
77
|
+
## [0.6.0] - 2023-03-13
|
|
78
|
+
|
|
79
|
+
### Added
|
|
80
|
+
|
|
81
|
+
- Added `treasury.getBalance` method
|
|
82
|
+
- Added `blockchain.now` getter and setter to override current unix time as seen during contract execution (both transactions and get methods). Note that this is unix timestamp, not JS timestamp, so you need to use `Math.floor(Date.now() / 1000)` instead of `Date.now()` to set current time
|
|
83
|
+
|
|
84
|
+
### Changed
|
|
85
|
+
|
|
86
|
+
- `RemoteBlockchainStorage` constructor now accepts a second optional parameter, `blockSeqno?: number`. If passed, all accounts will be pulled from that block number instead of the latest one
|
|
87
|
+
|
|
88
|
+
## [0.5.1] - 2023-03-02
|
|
89
|
+
|
|
90
|
+
### Changed
|
|
91
|
+
|
|
92
|
+
- Changed ton and ton-core dev and peer dependencies to versions 13.4.1 and 0.48.0 respectively
|
|
93
|
+
|
|
94
|
+
### Fixed
|
|
95
|
+
|
|
96
|
+
- Fixed typos in `SendMode.PAY_GAS_SEPARATLY` (missing E) from ton-core
|
|
97
|
+
|
|
98
|
+
## [0.5.0] - 2023-02-22
|
|
99
|
+
|
|
100
|
+
This release contains multiple breaking changes.
|
|
101
|
+
|
|
102
|
+
### Added
|
|
103
|
+
|
|
104
|
+
- Added `blockchain.libs: Cell | undefined` getter and setter for global libraries dictionary (as a `Cell`)
|
|
105
|
+
|
|
106
|
+
### Changed
|
|
107
|
+
|
|
108
|
+
- `blockchain.treasury` now accepts an optional `TreasuryParams` argument (see below for definition) instead of the old optional `workchain?: number` argument. This is a breaking change
|
|
109
|
+
```typescript
|
|
110
|
+
export type TreasuryParams = Partial<{
|
|
111
|
+
workchain: number
|
|
112
|
+
predeploy: boolean
|
|
113
|
+
balance: bigint
|
|
114
|
+
resetBalanceIfZero: boolean
|
|
115
|
+
}>
|
|
116
|
+
```
|
|
117
|
+
- `OpenedContract` was renamed to `SandboxContract`. This is a breaking change
|
|
118
|
+
- `LogsVerbosity` now has a new field, `print: boolean` (defaults to `true` on the `Blockchain` instance), which controls whether to `console.log` any logs at all (both from transactions and get methods). This is a breaking change
|
|
119
|
+
- `smartContract.get` and `blockchain.runGetMethod` now return `GetMethodResult` (see below for definition). The differences from the previous return type are as follows:
|
|
120
|
+
- `logs` renamed to `vmLogs`. This is a breaking change
|
|
121
|
+
- `gasUsed` is now of type `bigint`. This is a breaking change
|
|
122
|
+
- `blockchainLogs: string` and `debugLogs: string` were added
|
|
123
|
+
```typescript
|
|
124
|
+
export type GetMethodResult = {
|
|
125
|
+
stack: TupleItem[]
|
|
126
|
+
stackReader: TupleReader
|
|
127
|
+
exitCode: number
|
|
128
|
+
gasUsed: bigint
|
|
129
|
+
blockchainLogs: string
|
|
130
|
+
vmLogs: string
|
|
131
|
+
debugLogs: string
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
- Properties `storage` and `messageQueue` on `Blockchain` are now protected. This is a breaking change
|
|
135
|
+
- All properties and methods of `Blockchain` that were private are now protected to improve extensibility. Note that any invariants expected by `Blockchain` must be upheld
|
|
136
|
+
- `blockchain.sendMessage` and `smartContract.receiveMessage` now accept an optional `MessageParams` argument (see below for definition). These parameters are used for every transaction in the chain in case of `blockchain.sendMessage`
|
|
137
|
+
```typescript
|
|
138
|
+
export type MessageParams = Partial<{
|
|
139
|
+
now: number
|
|
140
|
+
randomSeed: Buffer
|
|
141
|
+
ignoreChksig: boolean
|
|
142
|
+
}>
|
|
143
|
+
```
|
|
144
|
+
- `blockchain.runGetMethod` and `smartContract.get` now accept an optional `GetMethodParams` argument (see below for definition)
|
|
145
|
+
```typescript
|
|
146
|
+
export type GetMethodParams = Partial<{
|
|
147
|
+
now: number
|
|
148
|
+
randomSeed: Buffer
|
|
149
|
+
gasLimit: bigint
|
|
150
|
+
}>
|
|
151
|
+
```
|
|
152
|
+
- `SendMessageResult` now has `transactions: BlockchainTransaction[]` instead of `transactions: Transaction[]`. Definition of `BlockchainTransaction`:
|
|
153
|
+
```typescript
|
|
154
|
+
export type BlockchainTransaction = Transaction & {
|
|
155
|
+
blockchainLogs: string
|
|
156
|
+
vmLogs: string
|
|
157
|
+
debugLogs: string
|
|
158
|
+
events: Event[]
|
|
159
|
+
parent?: BlockchainTransaction
|
|
160
|
+
children: BlockchainTransaction[]
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
- `smartContract.receiveMessage` now returns `SmartContractTransaction` (see below for definition)
|
|
164
|
+
```typescript
|
|
165
|
+
export type SmartContractTransaction = Transaction & {
|
|
166
|
+
blockchainLogs: string
|
|
167
|
+
vmLogs: string
|
|
168
|
+
debugLogs: string
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
- Emulator WASM binary has been updated
|
|
172
|
+
|
|
173
|
+
### Fixed
|
|
174
|
+
|
|
175
|
+
- Fixed empty message bodies in bounced messages. This fix is contained in the emulator WASM binary
|
|
176
|
+
|
|
177
|
+
## [0.4.0] - 2023-02-09
|
|
178
|
+
|
|
179
|
+
### Changed
|
|
180
|
+
|
|
181
|
+
- Treasuries obtained by `blockchain.treasury` calls are now initialized during this call and will no longer produce an extra transaction when first sending a message
|
|
182
|
+
- Transaction processing loop now prefetches contracts, which should provide a performance boost in some scenarios
|
|
183
|
+
|
|
184
|
+
## [0.3.0] - 2023-02-05
|
|
185
|
+
|
|
186
|
+
### Changed
|
|
187
|
+
|
|
188
|
+
- `Blockchain` and `SmartContract` now use `LogsVerbosity` (see below for definition) as the verbosity type, which allows for more control over what kinds of logs are printed. Logs from TVM debug primitives are now enabled by default, again. (You can disable them globally by setting verbosity with `debugLogs: false` on the `Blockchain` instance)
|
|
189
|
+
|
|
190
|
+
Definition of `LogsVerbosity`:
|
|
191
|
+
```typescript
|
|
192
|
+
type LogsVerbosity = {
|
|
193
|
+
blockchainLogs: boolean
|
|
194
|
+
vmLogs: Verbosity
|
|
195
|
+
debugLogs: boolean
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## [0.2.2] - 2023-02-03
|
|
200
|
+
|
|
201
|
+
### Added
|
|
202
|
+
|
|
203
|
+
- Added `blockchain.runGetMethod(address, method, stack)` to run a get method on the specified address
|
|
204
|
+
- Added `blockchain.setShardAccount(address, account)` to directly set the state of smart contracts
|
|
205
|
+
- Added `account: ShardAccount` getter and setter to `SmartContract`
|
|
206
|
+
- Exported helper methods `createEmptyShardAccount`, `createShardAccount` for use with `blockchain.setShardAccount` and `smartContract.account` setter
|
|
207
|
+
- Added the ability to pass `Cell`s into `blockchain.sendMessage`
|
|
208
|
+
|
|
209
|
+
### Changed
|
|
210
|
+
|
|
211
|
+
- Removed unnecessary `async` modifiers from `smartContract.receiveMessage` and `smartContract.get`
|
|
212
|
+
- Logs from TVM debug primitives (for example, `DUMP` and `STRDUMP` with corresponding FunC functions `~dump()` and `~strdump()`) now respect the `verbosity` parameter and will only work when it is not `none`
|
|
213
|
+
- Logs from TVM debug primitives are now printed using a single `console.log` call per one TVM execution to avoid cluttering the terminal during unit tests
|
|
214
|
+
|
|
215
|
+
### Fixed
|
|
216
|
+
|
|
217
|
+
- Fixed potential race conditions between execution of different transaction chains on the same `Blockchain` instance by use of an `AsyncLock`
|
|
218
|
+
|
|
219
|
+
### Removed
|
|
220
|
+
|
|
221
|
+
- Changed `blockchain.pushMessage`, `blockchain.processQueue`, `blockchain.runQueue` to be private
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Ton Tech
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Sandbox
|
|
2
|
+
|
|
3
|
+
This package allows you to emulate arbitrary TON smart contracts, send messages to them and run get methods on them as if they were deployed on a real network.
|
|
4
|
+
|
|
5
|
+
The key difference of this package from [ton-contract-executor](https://github.com/ton-community/ton-contract-executor) is the fact that the latter only emulates the compute phase of the contract - it does not know about any other phases and thus does not know anything about fees and balances (in a sense that it does not know whether a contract's balance will be enough to process all the out messages that it produces). On the other hand, this package emulates all the phases of a contract, and as a result, the emulation is much closer to what would happen in a real network.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Requires node 16 or higher.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
yarn add @ton/sandbox ton @ton/core ton-crypto
|
|
13
|
+
```
|
|
14
|
+
or
|
|
15
|
+
```
|
|
16
|
+
npm i @ton/sandbox ton @ton/core ton-crypto
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
To use this package, you need to create an instance of the `Blockchain` class using the static method `Blockchain.create` as follows:
|
|
22
|
+
```typescript
|
|
23
|
+
import { Blockchain } from "@ton/sandbox";
|
|
24
|
+
|
|
25
|
+
const blockchain = await Blockchain.create()
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
After that, you can use the low level methods on Blockchain (such as sendMessage) to emulate any messages that you want, but the recommended way to use it is to write wrappers for your contract using the `Contract` interface from `ton-core`. Then you can use `blockchain.openContract` on instances of such contracts, and they will be wrapped in a Proxy that will supply a `ContractProvider` as a first argument to all its methods starting with either `get` or `send`. Also all `send` methods will get Promisified and will return results of running the blockchain message queue along with the original method's result in the `result` field.
|
|
29
|
+
|
|
30
|
+
A good example of this is the [treasury contract](/src/treasury/Treasury.ts) that is basically a built-in highload wallet meant to help you write tests for your systems of smart contracts. When `blockchain.treasury` is called, an instance of `TreasuryContract` is created and `blockchain.openContract` is called to "open" it. After that, when you call `treasury.send`, `Blockchain` automatically supplies the first `provider` argument.
|
|
31
|
+
|
|
32
|
+
For your own contracts, you can draw inspiration from the contracts in the [examples](/examples/) - all of them use the `provider.internal` method to send internal messages using the treasuries passed in from the unit test file.
|
|
33
|
+
Here is an excerpt of that from [NftItem.ts](/examples/contracts/NftItem.ts):
|
|
34
|
+
```typescript
|
|
35
|
+
import { Address, beginCell, Cell, Contract, ContractProvider, Sender, toNano, Builder } from "ton-core";
|
|
36
|
+
|
|
37
|
+
class NftItem implements Contract {
|
|
38
|
+
async sendTransfer(provider: ContractProvider, via: Sender, params: {
|
|
39
|
+
value?: bigint
|
|
40
|
+
to: Address
|
|
41
|
+
responseTo?: Address
|
|
42
|
+
forwardAmount?: bigint
|
|
43
|
+
forwardBody?: Cell | Builder
|
|
44
|
+
}) {
|
|
45
|
+
await provider.internal(via, {
|
|
46
|
+
value: params.value ?? toNano('0.05'),
|
|
47
|
+
body: beginCell()
|
|
48
|
+
.storeUint(0x5fcc3d14, 32) // op
|
|
49
|
+
.storeUint(0, 64) // query id
|
|
50
|
+
.storeAddress(params.to)
|
|
51
|
+
.storeAddress(params.responseTo)
|
|
52
|
+
.storeBit(false) // custom payload
|
|
53
|
+
.storeCoins(params.forwardAmount ?? 0n)
|
|
54
|
+
.storeMaybeRef(params.forwardBody)
|
|
55
|
+
.endCell()
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
When you call `nftItem.sendTransfer(treasury.getSender(), { to: recipient })` (with `nftItem` being an "opened" instance of `NftItem`), an external message to the wallet represented by `treasury` will be pushed onto the message queue, then processed, generating an internal message to the NFT contract.
|
|
62
|
+
|
|
63
|
+
Here is another excerpt that shows the way to interact with get methods from wrappers:
|
|
64
|
+
```typescript
|
|
65
|
+
import { Contract, ContractProvider } from "ton-core";
|
|
66
|
+
|
|
67
|
+
export type NftItemData = {
|
|
68
|
+
inited: boolean
|
|
69
|
+
index: number
|
|
70
|
+
collection: Address | null
|
|
71
|
+
owner: Address | null
|
|
72
|
+
content: Cell | null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
class NftItem implements Contract {
|
|
76
|
+
async getData(provider: ContractProvider): Promise<NftItemData> {
|
|
77
|
+
const { stack } = await provider.get('get_nft_data', [])
|
|
78
|
+
return {
|
|
79
|
+
inited: stack.readBoolean(),
|
|
80
|
+
index: stack.readNumber(),
|
|
81
|
+
collection: stack.readAddressOpt(),
|
|
82
|
+
owner: stack.readAddressOpt(),
|
|
83
|
+
content: stack.readCellOpt(),
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
When you call `nftItem.getData()` (note that just like in the `sendTransfer` method, you don't need to supply the `provider` argument - it's done for you on "opened" instances), the `provider` will query the smart contract contained in blockchain and parse the data according to the code. Note that unlike the `send` methods, `get` methods on "opened" instances will return the original result as-is to the caller.
|
|
90
|
+
|
|
91
|
+
Notes:
|
|
92
|
+
- All of the methods of contracts that you want to "open" that start with `get` or `send` **NEED** to accept `provider: ContractProvider` as a first argument (even if not used) due to how the wrapper works.
|
|
93
|
+
- You can open any contract at any address, even if it is not yet deployed or was deployed by a "parent" opened contract. The only requirement is that the `address` field (required by the `Contract` interface) is the address of the contract that you want to open, and that `init` is present if you want to deploy using methods on the opened instance (in other cases, `init` is not necessary).
|
|
94
|
+
- Ideally, at most one call to **either** `provider.internal` or `provider.external` should be made within a `send` method. Otherwise, you may get hard to interpret (but generally speaking correct) results.
|
|
95
|
+
- No calls to `provider.external` or `provider.internal` should be made within `get` methods. Otherwise, you will get weird and wrong results in the following `send` methods of any contract.
|
|
96
|
+
|
|
97
|
+
## Writing tests
|
|
98
|
+
|
|
99
|
+
You can install additional `@ton/test-utils` package by running `yarn add @ton/test-utils -D` or `npm i --save-dev @ton/test-utils` (with `.toHaveTransaction` for jest or `.transaction` or `.to.have.transaction` for chai matcher) to add additional helpers for ease of testing. Don't forget to import them in your unit test files though!
|
|
100
|
+
|
|
101
|
+
Here is an excerpt of how it's used in the NFT collection example mentioned above:
|
|
102
|
+
```typescript
|
|
103
|
+
const buyResult = await buyer.send({
|
|
104
|
+
to: sale.address,
|
|
105
|
+
value: price + toNano('1'),
|
|
106
|
+
sendMode: SendMode.PAY_GAS_SEPARATELY,
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
expect(buyResult.transactions).toHaveTransaction({
|
|
110
|
+
from: sale.address,
|
|
111
|
+
to: marketplace.address,
|
|
112
|
+
value: fee,
|
|
113
|
+
})
|
|
114
|
+
expect(buyResult.transactions).toHaveTransaction({
|
|
115
|
+
from: sale.address,
|
|
116
|
+
to: collection.address,
|
|
117
|
+
value: fee,
|
|
118
|
+
})
|
|
119
|
+
```
|
|
120
|
+
(in that example `jest` is used)
|
|
121
|
+
|
|
122
|
+
The matcher supports the following fields:
|
|
123
|
+
```typescript
|
|
124
|
+
export type FlatTransaction = {
|
|
125
|
+
from?: Address
|
|
126
|
+
to: Address
|
|
127
|
+
value?: bigint
|
|
128
|
+
body: Cell
|
|
129
|
+
initData?: Cell
|
|
130
|
+
initCode?: Cell
|
|
131
|
+
deploy: boolean
|
|
132
|
+
lt: bigint
|
|
133
|
+
now: number
|
|
134
|
+
outMessagesCount: number
|
|
135
|
+
oldStatus: AccountStatus
|
|
136
|
+
endStatus: AccountStatus
|
|
137
|
+
totalFees?: bigint
|
|
138
|
+
aborted?: boolean
|
|
139
|
+
destroyed?: boolean
|
|
140
|
+
exitCode?: number
|
|
141
|
+
success?: boolean
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
But you can omit those you're not interested in, and you can also pass in functions accepting those types returning booleans (`true` meaning good) to check for example number ranges, message opcodes, etc. Note however that if a field is optional (like `from?: Address`), then the function needs to accept the optional type, too.
|
|
146
|
+
|
|
147
|
+
### Viewing logs
|
|
148
|
+
|
|
149
|
+
`Blockchain` and `SmartContract` use `LogsVerbosity` to determine what kinds of logs to print. Here is the definition:
|
|
150
|
+
```typescript
|
|
151
|
+
type LogsVerbosity = {
|
|
152
|
+
print: boolean
|
|
153
|
+
blockchainLogs: boolean
|
|
154
|
+
vmLogs: Verbosity
|
|
155
|
+
debugLogs: boolean
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
type Verbosity = 'none' | 'vm_logs' | 'vm_logs_full'
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Setting verbosity on `SmartContract`s works like an override with respect to what is set on `Blockchain`.
|
|
162
|
+
|
|
163
|
+
`debugLogs` is enabled by default on the `Blockchain` instance (so every `SmartContract` that does not have `debugLogs` overridden will print debug logs), other kinds of logs are turned off.
|
|
164
|
+
|
|
165
|
+
`print` determines whether to `console.log` all the non-empty logs (if set to `false`, logs will be collected but will only be exposed in the return values of methods on `Blockchain` and `SmartContract`, and not printed to console), defaults to `true` on the `Blockchain` instance.
|
|
166
|
+
|
|
167
|
+
`'vm_logs'` prints the log of every instruction that was executed, `'vm_logs_full'` also includes code cell hashes, locations, and stack information for every instruction executed.
|
|
168
|
+
|
|
169
|
+
To override verbosity on a specific contract, use `await blockchain.setVerbosityForAddress(targetAddress, verbosity)`, for example:
|
|
170
|
+
```typescript
|
|
171
|
+
await blockchain.setVerbosityForAddress(targetAddress, {
|
|
172
|
+
blockchainLogs: true,
|
|
173
|
+
vmLogs: 'vm_logs',
|
|
174
|
+
})
|
|
175
|
+
```
|
|
176
|
+
After that, the target contract will be using `debugLogs` from the `Blockchain` instance to determine whether to print debug logs, but will always print VM logs and blockchain logs.
|
|
177
|
+
|
|
178
|
+
To set global verbosity, use the `blockchain.verbosity` setter, for example:
|
|
179
|
+
```typescript
|
|
180
|
+
blockchain.verbosity = {
|
|
181
|
+
blockchainLogs: true,
|
|
182
|
+
vmLogs: 'none',
|
|
183
|
+
debugLogs: false,
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
Note that unlike with `setVerbosityForAddress`, with this setter you have to specify all the values from `LogsVerbosity`.
|
|
187
|
+
|
|
188
|
+
### Setting smart contract state directly
|
|
189
|
+
|
|
190
|
+
If you want to test some behavior on a contract if it had specific code, data, and other state fields, but do not want to execute all the required transactions for that, you can directly set the full state of the contract as it is stored in sandbox by using this method on the `Blockchain` instance:
|
|
191
|
+
```
|
|
192
|
+
async setShardAccount(address: Address, account: ShardAccount)
|
|
193
|
+
```
|
|
194
|
+
There are 2 helpers exported from sandbox that can help you create the `ShardAccount` from the common properties: `createEmptyShardAccount` and `createShardAccount`.
|
|
195
|
+
|
|
196
|
+
Note that this is a low-level function and does not check any invariants, such as that the address passed as the argument matches the one that is present in the `ShardAccount`, meaning it is possible to break stuff if you're not careful when using it.
|
|
197
|
+
|
|
198
|
+
### Network/Block configuration
|
|
199
|
+
|
|
200
|
+
By default, this package will use its [stored network configuration](src/config/defaultConfig.ts) to emulate messages. However, you can set any configuration you want when creating the `Blockchain` instance by passing the configuration cell in the optional `params` argument in the `config` field.
|
|
201
|
+
|
|
202
|
+
## Contributors
|
|
203
|
+
|
|
204
|
+
Special thanks to [@dungeon-master-666](https://github.com/dungeon-master-666) for their C++ code of the emulator.
|
|
205
|
+
|
|
206
|
+
Special thanks to [@TrueCarry](https://github.com/truecarry) for their help with performance and other suggestions.
|
|
207
|
+
|
|
208
|
+
## License
|
|
209
|
+
|
|
210
|
+
This package is released under the [MIT License](LICENSE).
|
|
211
|
+
|
|
212
|
+
## Donations
|
|
213
|
+
|
|
214
|
+
TON - `EQAQR1d1Q4NaE5EefwUMdrr1QvXg-8mDB0XI2-fwDBD0nYxC`
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { Address, Cell, Message, Transaction, ContractProvider, Contract, Sender, ShardAccount, TupleItem, ExternalAddress, StateInit } from "ton-core";
|
|
2
|
+
import { Executor, TickOrTock } from "../executor/Executor";
|
|
3
|
+
import { BlockchainStorage } from "./BlockchainStorage";
|
|
4
|
+
import { Event } from "../event/Event";
|
|
5
|
+
import { SandboxContractProvider } from "./BlockchainContractProvider";
|
|
6
|
+
import { TreasuryContract } from "../treasury/Treasury";
|
|
7
|
+
import { GetMethodParams, LogsVerbosity, MessageParams, SmartContract, SmartContractSnapshot, Verbosity } from "./SmartContract";
|
|
8
|
+
import { AsyncLock } from "../utils/AsyncLock";
|
|
9
|
+
export type ExternalOutInfo = {
|
|
10
|
+
type: 'external-out';
|
|
11
|
+
src: Address;
|
|
12
|
+
dest?: ExternalAddress;
|
|
13
|
+
createdAt: number;
|
|
14
|
+
createdLt: bigint;
|
|
15
|
+
};
|
|
16
|
+
export type ExternalOut = {
|
|
17
|
+
info: ExternalOutInfo;
|
|
18
|
+
init?: StateInit;
|
|
19
|
+
body: Cell;
|
|
20
|
+
};
|
|
21
|
+
export type BlockchainTransaction = Transaction & {
|
|
22
|
+
blockchainLogs: string;
|
|
23
|
+
vmLogs: string;
|
|
24
|
+
debugLogs: string;
|
|
25
|
+
events: Event[];
|
|
26
|
+
parent?: BlockchainTransaction;
|
|
27
|
+
children: BlockchainTransaction[];
|
|
28
|
+
externals: ExternalOut[];
|
|
29
|
+
};
|
|
30
|
+
export type SendMessageResult = {
|
|
31
|
+
transactions: BlockchainTransaction[];
|
|
32
|
+
events: Event[];
|
|
33
|
+
externals: ExternalOut[];
|
|
34
|
+
};
|
|
35
|
+
type ExtendsContractProvider<T> = T extends ContractProvider ? true : (T extends SandboxContractProvider ? true : false);
|
|
36
|
+
export type SandboxContract<F> = {
|
|
37
|
+
[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 & {
|
|
38
|
+
result: R extends Promise<infer PR> ? PR : R;
|
|
39
|
+
}> : never) : never) : F[P]);
|
|
40
|
+
};
|
|
41
|
+
export type PendingMessage = (({
|
|
42
|
+
type: 'message';
|
|
43
|
+
} & Message) | ({
|
|
44
|
+
type: 'ticktock';
|
|
45
|
+
which: TickOrTock;
|
|
46
|
+
on: Address;
|
|
47
|
+
})) & {
|
|
48
|
+
parentTransaction?: BlockchainTransaction;
|
|
49
|
+
};
|
|
50
|
+
export type TreasuryParams = Partial<{
|
|
51
|
+
workchain: number;
|
|
52
|
+
predeploy: boolean;
|
|
53
|
+
balance: bigint;
|
|
54
|
+
resetBalanceIfZero: boolean;
|
|
55
|
+
}>;
|
|
56
|
+
export type BlockchainConfig = Cell | 'default' | 'slim';
|
|
57
|
+
export type BlockchainSnapshot = {
|
|
58
|
+
contracts: SmartContractSnapshot[];
|
|
59
|
+
networkConfig: string;
|
|
60
|
+
lt: bigint;
|
|
61
|
+
time?: number;
|
|
62
|
+
verbosity: LogsVerbosity;
|
|
63
|
+
libs?: Cell;
|
|
64
|
+
nextCreateWalletIndex: number;
|
|
65
|
+
};
|
|
66
|
+
export declare class Blockchain {
|
|
67
|
+
protected storage: BlockchainStorage;
|
|
68
|
+
protected networkConfig: string;
|
|
69
|
+
protected currentLt: bigint;
|
|
70
|
+
protected currentTime?: number;
|
|
71
|
+
protected messageQueue: PendingMessage[];
|
|
72
|
+
protected logsVerbosity: LogsVerbosity;
|
|
73
|
+
protected globalLibs?: Cell;
|
|
74
|
+
protected lock: AsyncLock;
|
|
75
|
+
protected contractFetches: Map<string, Promise<SmartContract>>;
|
|
76
|
+
protected nextCreateWalletIndex: number;
|
|
77
|
+
readonly executor: Executor;
|
|
78
|
+
snapshot(): BlockchainSnapshot;
|
|
79
|
+
loadFrom(snapshot: BlockchainSnapshot): Promise<void>;
|
|
80
|
+
get now(): number | undefined;
|
|
81
|
+
set now(now: number | undefined);
|
|
82
|
+
get lt(): bigint;
|
|
83
|
+
protected constructor(opts: {
|
|
84
|
+
executor: Executor;
|
|
85
|
+
config?: BlockchainConfig;
|
|
86
|
+
storage: BlockchainStorage;
|
|
87
|
+
});
|
|
88
|
+
get config(): Cell;
|
|
89
|
+
get configBase64(): string;
|
|
90
|
+
sendMessage(message: Message | Cell, params?: MessageParams): Promise<SendMessageResult>;
|
|
91
|
+
runTickTock(on: Address | Address[], which: TickOrTock, params?: MessageParams): Promise<SendMessageResult>;
|
|
92
|
+
runGetMethod(address: Address, method: number | string, stack?: TupleItem[], params?: GetMethodParams): Promise<import("./SmartContract").GetMethodResult>;
|
|
93
|
+
protected pushMessage(message: Message | Cell): Promise<void>;
|
|
94
|
+
protected pushTickTock(on: Address, which: TickOrTock): Promise<void>;
|
|
95
|
+
protected runQueue(params?: MessageParams): Promise<SendMessageResult>;
|
|
96
|
+
protected processQueue(params?: MessageParams): Promise<BlockchainTransaction[]>;
|
|
97
|
+
provider(address: Address, init?: {
|
|
98
|
+
code: Cell;
|
|
99
|
+
data: Cell;
|
|
100
|
+
}): ContractProvider;
|
|
101
|
+
sender(address: Address): Sender;
|
|
102
|
+
protected treasuryParamsToMapKey(workchain: number, seed: string): string;
|
|
103
|
+
treasury(seed: string, params?: TreasuryParams): Promise<SandboxContract<TreasuryContract>>;
|
|
104
|
+
createWallets(n: number, params?: TreasuryParams): Promise<SandboxContract<TreasuryContract>[]>;
|
|
105
|
+
openContract<T extends Contract>(contract: T): SandboxContract<T>;
|
|
106
|
+
protected startFetchingContract(address: Address): Promise<SmartContract>;
|
|
107
|
+
getContract(address: Address): Promise<SmartContract>;
|
|
108
|
+
get verbosity(): LogsVerbosity;
|
|
109
|
+
set verbosity(value: LogsVerbosity);
|
|
110
|
+
setVerbosityForAddress(address: Address, verbosity: Partial<LogsVerbosity> | Verbosity | undefined): Promise<void>;
|
|
111
|
+
setConfig(config: BlockchainConfig): void;
|
|
112
|
+
setShardAccount(address: Address, account: ShardAccount): Promise<void>;
|
|
113
|
+
get libs(): Cell | undefined;
|
|
114
|
+
set libs(value: Cell | undefined);
|
|
115
|
+
static create(opts?: {
|
|
116
|
+
config?: BlockchainConfig;
|
|
117
|
+
storage?: BlockchainStorage;
|
|
118
|
+
}): Promise<Blockchain>;
|
|
119
|
+
}
|
|
120
|
+
export {};
|