@ton/sandbox 0.19.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 +7 -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/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,13 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.20.0] - 2024-05-31
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Added the ability to create `Blockchain` using a custom `IExecutor` instead of the default `Executor`
|
|
13
|
+
- Added more information to `EmulationError`, extended its error message
|
|
14
|
+
|
|
8
15
|
## [0.19.0] - 2024-04-27
|
|
9
16
|
|
|
10
17
|
### Fixed
|
package/README.md
CHANGED
|
@@ -13,7 +13,9 @@ The key difference of this package from [ton-contract-executor](https://github.c
|
|
|
13
13
|
* [Test a transaction with matcher](#test-a-transaction-with-matcher)
|
|
14
14
|
* [Testing transaction fees](#testing-transaction-fees)
|
|
15
15
|
* [Cross contract tests](#cross-contract-tests)
|
|
16
|
+
* [Testing key points](#testing-key-points)
|
|
16
17
|
* [Test examples](#test-examples)
|
|
18
|
+
* [Sandbox pitfalls](#sandbox-pitfalls)
|
|
17
19
|
* [Viewing logs](#viewing-logs)
|
|
18
20
|
* [Setting smart contract state directly](#setting-smart-contract-state-directly)
|
|
19
21
|
* [Using snapshots](#using-snapshots)
|
|
@@ -265,6 +267,22 @@ it('minter admin should be able to mint jettons', async () => {
|
|
|
265
267
|
});
|
|
266
268
|
```
|
|
267
269
|
|
|
270
|
+
### Testing key points
|
|
271
|
+
|
|
272
|
+
In order to make sure that the contract will work as expected, you need to follow the following points in testing
|
|
273
|
+
|
|
274
|
+
* Test positive flows to make sure your contracts work
|
|
275
|
+
* Test negative flows to make sure that smart contracts behave correctly under abnormal conditions. Abnormal conditions includes:
|
|
276
|
+
* incorrect input
|
|
277
|
+
* action list overflow
|
|
278
|
+
* insufficient toncoin amount
|
|
279
|
+
* integer overflow
|
|
280
|
+
* owner assertions
|
|
281
|
+
|
|
282
|
+
More information about testing key points can be found here:
|
|
283
|
+
* [Testing key point](docs/testing-key-points.md)
|
|
284
|
+
|
|
285
|
+
|
|
268
286
|
### Test Examples
|
|
269
287
|
You can typically find various tests for Sandbox-based project contracts in the `./tests` directory.
|
|
270
288
|
Learn more from examples:
|
|
@@ -272,6 +290,36 @@ Learn more from examples:
|
|
|
272
290
|
* [FunC Test Examples](https://docs.ton.org/develop/smart-contracts/examples#examples-of-tests-for-smart-contracts)
|
|
273
291
|
* [Tact Test Examples](docs/tact-testing-examples.md)
|
|
274
292
|
|
|
293
|
+
|
|
294
|
+
## Sandbox pitfalls
|
|
295
|
+
|
|
296
|
+
There are several pitfalls in the sandbox due to the limitations of emulation. Be aware of it while testing your smart contracts.
|
|
297
|
+
|
|
298
|
+
* Libs cells not updating in contract by `SETLIBCODE`, `CHANGELIB`. They need to be updated manually.
|
|
299
|
+
```typescript
|
|
300
|
+
const blockchain = await Blockchain.create();
|
|
301
|
+
const code = await compile('Contract');
|
|
302
|
+
|
|
303
|
+
// consist of a hash of a lib cell and its representation
|
|
304
|
+
const libsDict = Dictionary.empty(Dictionary.Keys.Buffer(32), Dictionary.Values.Cell());
|
|
305
|
+
libsDict.set(code.hash(), code);
|
|
306
|
+
|
|
307
|
+
// manualy set libs
|
|
308
|
+
blockchain.libs = beginCell().storeDictDirect(libsDict).endCell();
|
|
309
|
+
```
|
|
310
|
+
* There is no blocks in emulation, so opcodes like `PREVBLOCKSINFO`, `PREVMCBLOCKS`, `PREVKEYBLOCK` will return empty tuple.
|
|
311
|
+
* The randomness in the TON is always deterministic and the same randomSeed always gives the same random number sequence. If necessary, you can change the randomSeed to make `RAND` provide result based on provided seed. Currently, there is no way to provide randomSeed in opened contracts.
|
|
312
|
+
```typescript
|
|
313
|
+
const res = await blockchain.runGetMethod(example.address,
|
|
314
|
+
'get_method',
|
|
315
|
+
[],
|
|
316
|
+
{ randomSeed: randomBytes(32) }
|
|
317
|
+
);
|
|
318
|
+
const stack = new TupleReader(res.stack);
|
|
319
|
+
// read data from stack ...
|
|
320
|
+
```
|
|
321
|
+
* Because there is no concept of blocks in Sandbox, things like sharding do not work.
|
|
322
|
+
|
|
275
323
|
## Viewing logs
|
|
276
324
|
|
|
277
325
|
`Blockchain` and `SmartContract` use `LogsVerbosity` to determine what kinds of logs to print. Here is the definition:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Address, Cell, Message, Transaction, ContractProvider, Contract, Sender, ShardAccount, TupleItem, ExternalAddress, StateInit, OpenedContract } from "@ton/core";
|
|
2
|
-
import {
|
|
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
|
});
|
|
@@ -5,6 +5,9 @@ import { GetMethodResult, SmartContract } from "./SmartContract";
|
|
|
5
5
|
export interface SandboxContractProvider extends ContractProvider {
|
|
6
6
|
tickTock(which: TickOrTock): Promise<void>;
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Provider used in contracts to send messages or invoke getters. For additional information see {@link Blockchain.provider}
|
|
10
|
+
*/
|
|
8
11
|
export declare class BlockchainContractProvider implements SandboxContractProvider {
|
|
9
12
|
private readonly blockchain;
|
|
10
13
|
private readonly address;
|
|
@@ -16,16 +19,40 @@ export declare class BlockchainContractProvider implements SandboxContractProvid
|
|
|
16
19
|
pushTickTock(on: Address, which: TickOrTock): Promise<void>;
|
|
17
20
|
openContract<T extends Contract>(contract: T): OpenedContract<T>;
|
|
18
21
|
}, address: Address, init?: StateInit | null | undefined);
|
|
22
|
+
/**
|
|
23
|
+
* Opens contract. For additional information see {@link Blockchain.open}
|
|
24
|
+
*/
|
|
19
25
|
open<T extends Contract>(contract: T): OpenedContract<T>;
|
|
20
26
|
getState(): Promise<ContractState>;
|
|
27
|
+
/**
|
|
28
|
+
* Invokes get method.
|
|
29
|
+
* @param name Name of get method
|
|
30
|
+
* @param args Args to invoke get method.
|
|
31
|
+
*/
|
|
21
32
|
get(name: string, args: TupleItem[]): Promise<ContractGetMethodResult>;
|
|
33
|
+
/**
|
|
34
|
+
* Dummy implementation of getTransactions. Sandbox does not store transactions, so its ContractProvider cannot fetch any.
|
|
35
|
+
* Throws error in every call.
|
|
36
|
+
*
|
|
37
|
+
* @throws {Error}
|
|
38
|
+
*/
|
|
22
39
|
getTransactions(address: Address, lt: bigint, hash: Buffer, limit?: number | undefined): Promise<Transaction[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Pushes external-in message to message queue.
|
|
42
|
+
* @param message Message to push
|
|
43
|
+
*/
|
|
23
44
|
external(message: Cell): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Pushes internal message to message queue.
|
|
47
|
+
*/
|
|
24
48
|
internal(via: Sender, args: {
|
|
25
49
|
value: string | bigint;
|
|
26
50
|
bounce?: boolean | null;
|
|
27
51
|
sendMode?: SendMode;
|
|
28
52
|
body?: string | Cell | null;
|
|
29
53
|
}): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Pushes tick-tock message to message queue.
|
|
56
|
+
*/
|
|
30
57
|
tickTock(which: TickOrTock): Promise<void>;
|
|
31
58
|
}
|
|
@@ -32,12 +32,18 @@ function convertState(state) {
|
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Provider used in contracts to send messages or invoke getters. For additional information see {@link Blockchain.provider}
|
|
37
|
+
*/
|
|
35
38
|
class BlockchainContractProvider {
|
|
36
39
|
constructor(blockchain, address, init) {
|
|
37
40
|
this.blockchain = blockchain;
|
|
38
41
|
this.address = address;
|
|
39
42
|
this.init = init;
|
|
40
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Opens contract. For additional information see {@link Blockchain.open}
|
|
46
|
+
*/
|
|
41
47
|
open(contract) {
|
|
42
48
|
return this.blockchain.openContract(contract);
|
|
43
49
|
}
|
|
@@ -52,6 +58,11 @@ class BlockchainContractProvider {
|
|
|
52
58
|
state: convertState(contract.accountState),
|
|
53
59
|
};
|
|
54
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Invokes get method.
|
|
63
|
+
* @param name Name of get method
|
|
64
|
+
* @param args Args to invoke get method.
|
|
65
|
+
*/
|
|
55
66
|
async get(name, args) {
|
|
56
67
|
const result = await this.blockchain.runGetMethod(this.address, name, args);
|
|
57
68
|
const ret = {
|
|
@@ -63,9 +74,19 @@ class BlockchainContractProvider {
|
|
|
63
74
|
delete ret.stackReader;
|
|
64
75
|
return ret;
|
|
65
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Dummy implementation of getTransactions. Sandbox does not store transactions, so its ContractProvider cannot fetch any.
|
|
79
|
+
* Throws error in every call.
|
|
80
|
+
*
|
|
81
|
+
* @throws {Error}
|
|
82
|
+
*/
|
|
66
83
|
getTransactions(address, lt, hash, limit) {
|
|
67
84
|
throw new Error("`getTransactions` is not implemented in `BlockchainContractProvider`, do not use it in the tests");
|
|
68
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Pushes external-in message to message queue.
|
|
88
|
+
* @param message Message to push
|
|
89
|
+
*/
|
|
69
90
|
async external(message) {
|
|
70
91
|
const init = ((await this.getState()).state.type !== 'active' && this.init) ? this.init : undefined;
|
|
71
92
|
await this.blockchain.pushMessage({
|
|
@@ -78,6 +99,9 @@ class BlockchainContractProvider {
|
|
|
78
99
|
body: message,
|
|
79
100
|
});
|
|
80
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Pushes internal message to message queue.
|
|
104
|
+
*/
|
|
81
105
|
async internal(via, args) {
|
|
82
106
|
const init = ((await this.getState()).state.type !== 'active' && this.init) ? this.init : undefined;
|
|
83
107
|
const bounce = (args.bounce !== null && args.bounce !== undefined) ? args.bounce : true;
|
|
@@ -92,6 +116,9 @@ class BlockchainContractProvider {
|
|
|
92
116
|
body,
|
|
93
117
|
});
|
|
94
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Pushes tick-tock message to message queue.
|
|
121
|
+
*/
|
|
95
122
|
async tickTock(which) {
|
|
96
123
|
await this.blockchain.pushTickTock(this.address, which);
|
|
97
124
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { Address, Message, Sender, SenderArguments } from "@ton/core";
|
|
2
|
+
/**
|
|
3
|
+
* Sender for sandbox blockchain. For additional information see {@link Blockchain.sender}
|
|
4
|
+
*/
|
|
2
5
|
export declare class BlockchainSender implements Sender {
|
|
3
6
|
private readonly blockchain;
|
|
4
7
|
readonly address: Address;
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BlockchainSender = void 0;
|
|
4
4
|
const core_1 = require("@ton/core");
|
|
5
|
+
/**
|
|
6
|
+
* Sender for sandbox blockchain. For additional information see {@link Blockchain.sender}
|
|
7
|
+
*/
|
|
5
8
|
class BlockchainSender {
|
|
6
9
|
constructor(blockchain, address) {
|
|
7
10
|
this.blockchain = blockchain;
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
import { AccountState, Address } from "@ton/core";
|
|
3
3
|
import { SmartContract } from "./SmartContract";
|
|
4
4
|
import { Blockchain } from "./Blockchain";
|
|
5
|
+
/**
|
|
6
|
+
* @interface BlockchainStorage Provides information about contracts by blockchain
|
|
7
|
+
*/
|
|
5
8
|
export interface BlockchainStorage {
|
|
6
9
|
getContract(blockchain: Blockchain, address: Address): Promise<SmartContract>;
|
|
7
10
|
knownContracts(): SmartContract[];
|
|
@@ -24,6 +27,19 @@ export interface RemoteBlockchainStorageClient {
|
|
|
24
27
|
};
|
|
25
28
|
}>;
|
|
26
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Wraps ton client for remote storage.
|
|
32
|
+
*
|
|
33
|
+
* ```ts
|
|
34
|
+
* let client = new TonClient4({
|
|
35
|
+
* endpoint: 'https://mainnet-v4.tonhubapi.com'
|
|
36
|
+
* })
|
|
37
|
+
*
|
|
38
|
+
* let remoteStorageClient = wrapTonClient4ForRemote(client);
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @param client TonClient4 to wrap
|
|
42
|
+
*/
|
|
27
43
|
export declare function wrapTonClient4ForRemote(client: {
|
|
28
44
|
getLastBlock(): Promise<{
|
|
29
45
|
last: {
|
|
@@ -52,6 +68,18 @@ export declare function wrapTonClient4ForRemote(client: {
|
|
|
52
68
|
};
|
|
53
69
|
}>;
|
|
54
70
|
}): RemoteBlockchainStorageClient;
|
|
71
|
+
/**
|
|
72
|
+
* @class {RemoteBlockchainStorage} Remote blockchain storage implementation.
|
|
73
|
+
* ```ts
|
|
74
|
+
* let client = new TonClient4({
|
|
75
|
+
* endpoint: 'https://mainnet-v4.tonhubapi.com'
|
|
76
|
+
* })
|
|
77
|
+
*
|
|
78
|
+
* let blockchain = await Blockchain.create({
|
|
79
|
+
* storage: new RemoteBlockchainStorage(wrapTonClient4ForRemote(client), 34892000)
|
|
80
|
+
* });
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
55
83
|
export declare class RemoteBlockchainStorage implements BlockchainStorage {
|
|
56
84
|
private contracts;
|
|
57
85
|
private client;
|
|
@@ -35,6 +35,19 @@ function convertTonClient4State(state) {
|
|
|
35
35
|
throw new Error(`Unknown type ${state}`);
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Wraps ton client for remote storage.
|
|
40
|
+
*
|
|
41
|
+
* ```ts
|
|
42
|
+
* let client = new TonClient4({
|
|
43
|
+
* endpoint: 'https://mainnet-v4.tonhubapi.com'
|
|
44
|
+
* })
|
|
45
|
+
*
|
|
46
|
+
* let remoteStorageClient = wrapTonClient4ForRemote(client);
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @param client TonClient4 to wrap
|
|
50
|
+
*/
|
|
38
51
|
function wrapTonClient4ForRemote(client) {
|
|
39
52
|
return {
|
|
40
53
|
getLastBlockSeqno: async () => {
|
|
@@ -55,6 +68,18 @@ function wrapTonClient4ForRemote(client) {
|
|
|
55
68
|
};
|
|
56
69
|
}
|
|
57
70
|
exports.wrapTonClient4ForRemote = wrapTonClient4ForRemote;
|
|
71
|
+
/**
|
|
72
|
+
* @class {RemoteBlockchainStorage} Remote blockchain storage implementation.
|
|
73
|
+
* ```ts
|
|
74
|
+
* let client = new TonClient4({
|
|
75
|
+
* endpoint: 'https://mainnet-v4.tonhubapi.com'
|
|
76
|
+
* })
|
|
77
|
+
*
|
|
78
|
+
* let blockchain = await Blockchain.create({
|
|
79
|
+
* storage: new RemoteBlockchainStorage(wrapTonClient4ForRemote(client), 34892000)
|
|
80
|
+
* });
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
58
83
|
class RemoteBlockchainStorage {
|
|
59
84
|
constructor(client, blockSeqno) {
|
|
60
85
|
this.contracts = new Map();
|
|
@@ -59,7 +59,9 @@ export declare class EmulationError extends Error {
|
|
|
59
59
|
error: string;
|
|
60
60
|
vmLogs?: string | undefined;
|
|
61
61
|
exitCode?: number | undefined;
|
|
62
|
-
|
|
62
|
+
blockchainLogs?: string | undefined;
|
|
63
|
+
debugLogs?: string | undefined;
|
|
64
|
+
constructor(error: string, vmLogs?: string | undefined, exitCode?: number | undefined, blockchainLogs?: string | undefined, debugLogs?: string | undefined);
|
|
63
65
|
}
|
|
64
66
|
export type SmartContractSnapshot = {
|
|
65
67
|
address: Address;
|
|
@@ -99,11 +99,26 @@ class TimeError extends Error {
|
|
|
99
99
|
}
|
|
100
100
|
exports.TimeError = TimeError;
|
|
101
101
|
class EmulationError extends Error {
|
|
102
|
-
constructor(error, vmLogs, exitCode) {
|
|
103
|
-
|
|
102
|
+
constructor(error, vmLogs, exitCode, blockchainLogs, debugLogs) {
|
|
103
|
+
let errMsg = `Error while executing transaction: ${error}`;
|
|
104
|
+
if (exitCode !== undefined) {
|
|
105
|
+
errMsg += `\nExit code: ${exitCode}`;
|
|
106
|
+
}
|
|
107
|
+
if (vmLogs !== undefined) {
|
|
108
|
+
errMsg += `\nVM logs:\n${vmLogs}`;
|
|
109
|
+
}
|
|
110
|
+
if (blockchainLogs !== undefined) {
|
|
111
|
+
errMsg += `\nBlockchain logs:\n${blockchainLogs}`;
|
|
112
|
+
}
|
|
113
|
+
if (debugLogs !== undefined) {
|
|
114
|
+
errMsg += `\nDebug logs:\n${debugLogs}`;
|
|
115
|
+
}
|
|
116
|
+
super(errMsg);
|
|
104
117
|
this.error = error;
|
|
105
118
|
this.vmLogs = vmLogs;
|
|
106
119
|
this.exitCode = exitCode;
|
|
120
|
+
this.blockchainLogs = blockchainLogs;
|
|
121
|
+
this.debugLogs = debugLogs;
|
|
107
122
|
}
|
|
108
123
|
}
|
|
109
124
|
exports.EmulationError = EmulationError;
|
|
@@ -212,7 +227,7 @@ class SmartContract {
|
|
|
212
227
|
console.log(res.logs);
|
|
213
228
|
}
|
|
214
229
|
if (!res.result.success) {
|
|
215
|
-
throw new EmulationError(res.result.error, res.result.vmResults?.vmLog, res.result.vmResults?.vmExitCode);
|
|
230
|
+
throw new EmulationError(res.result.error, res.result.vmResults?.vmLog, res.result.vmResults?.vmExitCode, res.logs.length === 0 ? undefined : res.logs, res.debugLogs.length === 0 ? undefined : res.debugLogs);
|
|
216
231
|
}
|
|
217
232
|
if (this.verbosity.print && this.verbosity.vmLogs !== 'none' && res.result.vmLog.length > 0) {
|
|
218
233
|
console.log(res.result.vmLog);
|
|
@@ -2,6 +2,9 @@ import { Address, Cell, Contract, ContractProvider, MessageRelaxed, Sender, Send
|
|
|
2
2
|
export type Treasury = Sender & {
|
|
3
3
|
address: Address;
|
|
4
4
|
};
|
|
5
|
+
/**
|
|
6
|
+
* @class TreasuryContract is Wallet v4 alternative. For additional information see {@link Blockchain.treasury}
|
|
7
|
+
*/
|
|
5
8
|
export declare class TreasuryContract implements Contract {
|
|
6
9
|
static readonly code: Cell;
|
|
7
10
|
static create(workchain: number, subwalletId: bigint): TreasuryContract;
|
|
@@ -9,10 +12,27 @@ export declare class TreasuryContract implements Contract {
|
|
|
9
12
|
readonly init: StateInit;
|
|
10
13
|
readonly subwalletId: bigint;
|
|
11
14
|
constructor(workchain: number, subwalletId: bigint);
|
|
15
|
+
/**
|
|
16
|
+
* Send bulk messages using one external message.
|
|
17
|
+
* @param messages Messages to send
|
|
18
|
+
* @param sendMode Send mode of every message
|
|
19
|
+
*/
|
|
12
20
|
sendMessages(provider: ContractProvider, messages: MessageRelaxed[], sendMode?: SendMode): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Sends message by arguments specified.
|
|
23
|
+
*/
|
|
13
24
|
send(provider: ContractProvider, args: SenderArguments): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* @returns Sender
|
|
27
|
+
*/
|
|
14
28
|
getSender(provider: ContractProvider): Treasury;
|
|
29
|
+
/**
|
|
30
|
+
* @returns wallet balance in nanoTONs
|
|
31
|
+
*/
|
|
15
32
|
getBalance(provider: ContractProvider): Promise<bigint>;
|
|
33
|
+
/**
|
|
34
|
+
* Creates transfer cell for {@link sendMessages}.
|
|
35
|
+
*/
|
|
16
36
|
createTransfer(args: {
|
|
17
37
|
messages: MessageRelaxed[];
|
|
18
38
|
sendMode?: SendMode;
|
|
@@ -22,6 +22,9 @@ function senderArgsToMessageRelaxed(args) {
|
|
|
22
22
|
bounce: args.bounce
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* @class TreasuryContract is Wallet v4 alternative. For additional information see {@link Blockchain.treasury}
|
|
27
|
+
*/
|
|
25
28
|
class TreasuryContract {
|
|
26
29
|
static create(workchain, subwalletId) {
|
|
27
30
|
return new TreasuryContract(workchain, subwalletId);
|
|
@@ -34,6 +37,11 @@ class TreasuryContract {
|
|
|
34
37
|
this.address = (0, core_1.contractAddress)(workchain, this.init);
|
|
35
38
|
this.subwalletId = subwalletId;
|
|
36
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Send bulk messages using one external message.
|
|
42
|
+
* @param messages Messages to send
|
|
43
|
+
* @param sendMode Send mode of every message
|
|
44
|
+
*/
|
|
37
45
|
async sendMessages(provider, messages, sendMode) {
|
|
38
46
|
let transfer = this.createTransfer({
|
|
39
47
|
sendMode: sendMode,
|
|
@@ -41,9 +49,15 @@ class TreasuryContract {
|
|
|
41
49
|
});
|
|
42
50
|
await provider.external(transfer);
|
|
43
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Sends message by arguments specified.
|
|
54
|
+
*/
|
|
44
55
|
async send(provider, args) {
|
|
45
56
|
await this.sendMessages(provider, [senderArgsToMessageRelaxed(args)], args.sendMode ?? undefined);
|
|
46
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* @returns Sender
|
|
60
|
+
*/
|
|
47
61
|
getSender(provider) {
|
|
48
62
|
return {
|
|
49
63
|
address: this.address,
|
|
@@ -56,9 +70,15 @@ class TreasuryContract {
|
|
|
56
70
|
}
|
|
57
71
|
};
|
|
58
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* @returns wallet balance in nanoTONs
|
|
75
|
+
*/
|
|
59
76
|
async getBalance(provider) {
|
|
60
77
|
return (await provider.getState()).balance;
|
|
61
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Creates transfer cell for {@link sendMessages}.
|
|
81
|
+
*/
|
|
62
82
|
createTransfer(args) {
|
|
63
83
|
let sendMode = core_1.SendMode.PAY_GAS_SEPARATELY;
|
|
64
84
|
if (args.sendMode !== null && args.sendMode !== undefined) {
|
package/dist/utils/message.d.ts
CHANGED
package/dist/utils/message.js
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
1
|
import { Transaction } from "@ton/core";
|
|
2
|
+
/**
|
|
3
|
+
* @param tx Transaction to create log string
|
|
4
|
+
* @returns Transaction log string
|
|
5
|
+
*/
|
|
2
6
|
export declare function prettyLogTransaction(tx: Transaction): string;
|
|
7
|
+
/**
|
|
8
|
+
* Log transaction using `console.log`. Logs base on result of {@link prettyLogTransaction}.
|
|
9
|
+
* Example output:
|
|
10
|
+
* ```
|
|
11
|
+
* null ➡️ EQBGhqLAZseEqRXz4ByFPTGV7SVMlI4hrbs-Sps_Xzx01x8G
|
|
12
|
+
* ➡️ 0.05 💎 EQC2VluVfpj2FoHNMAiDMpcMzwvjLZxxTG8ecq477RE3NvVt
|
|
13
|
+
* ```
|
|
14
|
+
* @param txs Transactions to log
|
|
15
|
+
*/
|
|
3
16
|
export declare function prettyLogTransactions(txs: Transaction[]): void;
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.prettyLogTransactions = exports.prettyLogTransaction = void 0;
|
|
4
4
|
const core_1 = require("@ton/core");
|
|
5
|
+
/**
|
|
6
|
+
* @param tx Transaction to create log string
|
|
7
|
+
* @returns Transaction log string
|
|
8
|
+
*/
|
|
5
9
|
function prettyLogTransaction(tx) {
|
|
6
10
|
let res = `${tx.inMessage?.info.src} ➡️ ${tx.inMessage?.info.dest}\n`;
|
|
7
11
|
for (let message of tx.outMessages.values()) {
|
|
@@ -15,6 +19,15 @@ function prettyLogTransaction(tx) {
|
|
|
15
19
|
return res;
|
|
16
20
|
}
|
|
17
21
|
exports.prettyLogTransaction = prettyLogTransaction;
|
|
22
|
+
/**
|
|
23
|
+
* Log transaction using `console.log`. Logs base on result of {@link prettyLogTransaction}.
|
|
24
|
+
* Example output:
|
|
25
|
+
* ```
|
|
26
|
+
* null ➡️ EQBGhqLAZseEqRXz4ByFPTGV7SVMlI4hrbs-Sps_Xzx01x8G
|
|
27
|
+
* ➡️ 0.05 💎 EQC2VluVfpj2FoHNMAiDMpcMzwvjLZxxTG8ecq477RE3NvVt
|
|
28
|
+
* ```
|
|
29
|
+
* @param txs Transactions to log
|
|
30
|
+
*/
|
|
18
31
|
function prettyLogTransactions(txs) {
|
|
19
32
|
let out = '';
|
|
20
33
|
for (let tx of txs) {
|
|
@@ -1,3 +1,16 @@
|
|
|
1
1
|
import { Transaction } from "@ton/core";
|
|
2
2
|
export declare function formatCoinsPure(value: bigint, precision?: number): string;
|
|
3
|
+
/**
|
|
4
|
+
* Prints transaction fees.
|
|
5
|
+
* Example output:
|
|
6
|
+
* ```
|
|
7
|
+
* ┌─────────┬─────────────┬────────────────┬────────────────┬────────────────┬────────────────┬───────────────┬────────────┬────────────────┬──────────┬────────────┐
|
|
8
|
+
* │ (index) │ op │ valueIn │ valueOut │ totalFees │ inForwardFee │ outForwardFee │ outActions │ computeFee │ exitCode │ actionCode │
|
|
9
|
+
* ├─────────┼─────────────┼────────────────┼────────────────┼────────────────┼────────────────┼───────────────┼────────────┼────────────────┼──────────┼────────────┤
|
|
10
|
+
* │ 0 │ 'N/A' │ 'N/A' │ '1000 TON' │ '0.004007 TON' │ 'N/A' │ '0.001 TON' │ 1 │ '0.001937 TON' │ 0 │ 0 │
|
|
11
|
+
* │ 1 │ '0x45ab564' │ '1000 TON' │ '998.8485 TON' │ '1.051473 TON' │ '0.000667 TON' │ '0.255 TON' │ 255 │ '0.966474 TON' │ 0 │ 0 │
|
|
12
|
+
* │ 2 │ '0x0' │ '3.917053 TON' │ '0 TON' │ '0.00031 TON' │ '0.000667 TON' │ 'N/A' │ 0 │ '0.000309 TON' │ 0 │ 0 │
|
|
13
|
+
* ```
|
|
14
|
+
* @param transactions List of transaction to print fees
|
|
15
|
+
*/
|
|
3
16
|
export declare function printTransactionFees(transactions: Transaction[]): void;
|
|
@@ -30,6 +30,19 @@ function formatCoins(value, precision = 6) {
|
|
|
30
30
|
return 'N/A';
|
|
31
31
|
return formatCoinsPure(value, precision) + ' TON';
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Prints transaction fees.
|
|
35
|
+
* Example output:
|
|
36
|
+
* ```
|
|
37
|
+
* ┌─────────┬─────────────┬────────────────┬────────────────┬────────────────┬────────────────┬───────────────┬────────────┬────────────────┬──────────┬────────────┐
|
|
38
|
+
* │ (index) │ op │ valueIn │ valueOut │ totalFees │ inForwardFee │ outForwardFee │ outActions │ computeFee │ exitCode │ actionCode │
|
|
39
|
+
* ├─────────┼─────────────┼────────────────┼────────────────┼────────────────┼────────────────┼───────────────┼────────────┼────────────────┼──────────┼────────────┤
|
|
40
|
+
* │ 0 │ 'N/A' │ 'N/A' │ '1000 TON' │ '0.004007 TON' │ 'N/A' │ '0.001 TON' │ 1 │ '0.001937 TON' │ 0 │ 0 │
|
|
41
|
+
* │ 1 │ '0x45ab564' │ '1000 TON' │ '998.8485 TON' │ '1.051473 TON' │ '0.000667 TON' │ '0.255 TON' │ 255 │ '0.966474 TON' │ 0 │ 0 │
|
|
42
|
+
* │ 2 │ '0x0' │ '3.917053 TON' │ '0 TON' │ '0.00031 TON' │ '0.000667 TON' │ 'N/A' │ 0 │ '0.000309 TON' │ 0 │ 0 │
|
|
43
|
+
* ```
|
|
44
|
+
* @param transactions List of transaction to print fees
|
|
45
|
+
*/
|
|
33
46
|
function printTransactionFees(transactions) {
|
|
34
47
|
console.table(transactions
|
|
35
48
|
.map((tx) => {
|