@ton/blueprint 0.33.0 → 0.34.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +98 -12
  3. package/dist/cli/Runner.d.ts +2 -0
  4. package/dist/cli/Runner.js +9 -1
  5. package/dist/cli/build.d.ts +1 -2
  6. package/dist/cli/build.js +3 -6
  7. package/dist/cli/cli.js +8 -1
  8. package/dist/cli/constants.d.ts +4 -0
  9. package/dist/cli/constants.js +44 -6
  10. package/dist/cli/create.js +2 -13
  11. package/dist/cli/help.js +2 -1
  12. package/dist/cli/pack.d.ts +2 -0
  13. package/dist/cli/pack.js +98 -0
  14. package/dist/cli/rename.d.ts +6 -0
  15. package/dist/cli/rename.js +101 -0
  16. package/dist/cli/rename.spec.d.ts +1 -0
  17. package/dist/cli/rename.spec.js +59 -0
  18. package/dist/cli/run.js +2 -1
  19. package/dist/cli/snapshot.d.ts +6 -0
  20. package/dist/cli/snapshot.js +36 -0
  21. package/dist/cli/test.d.ts +4 -0
  22. package/dist/cli/test.js +20 -4
  23. package/dist/compile/compile.d.ts +16 -0
  24. package/dist/compile/compile.js +23 -7
  25. package/dist/compile/tact/compile.tact.d.ts +2 -1
  26. package/dist/compile/tact/compile.tact.js +9 -5
  27. package/dist/config/Config.d.ts +15 -0
  28. package/dist/index.d.ts +1 -0
  29. package/dist/index.js +3 -1
  30. package/dist/network/Network.d.ts +1 -0
  31. package/dist/network/Network.js +2 -0
  32. package/dist/network/createNetworkProvider.js +20 -6
  33. package/dist/network/send/DeeplinkProvider.d.ts +2 -1
  34. package/dist/network/send/DeeplinkProvider.js +6 -4
  35. package/dist/network/send/MnemonicProvider.d.ts +14 -7
  36. package/dist/network/send/MnemonicProvider.js +28 -8
  37. package/dist/network/send/TonConnectProvider.d.ts +2 -1
  38. package/dist/network/send/TonConnectProvider.js +10 -4
  39. package/dist/paths.d.ts +3 -0
  40. package/dist/paths.js +4 -1
  41. package/dist/templates/func/common/contracts/imports/stdlib.fc.template +8 -7
  42. package/dist/templates/func/not-separated-common/contracts/imports/stdlib.fc.template +8 -7
  43. package/dist/templates/tact/counter/scripts/deploy.ts.template +1 -1
  44. package/dist/templates/tact/counter/scripts/increment.ts.template +1 -1
  45. package/dist/templates/tact/empty/scripts/deploy.ts.template +1 -1
  46. package/dist/utils/selection.utils.js +9 -4
  47. package/dist/utils/string.utils.d.ts +3 -0
  48. package/dist/utils/string.utils.js +20 -0
  49. package/dist/utils/ton.utils.d.ts +2 -1
  50. package/dist/utils/ton.utils.js +3 -1
  51. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,34 @@ 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.34.0] - 2025-05-20
9
+
10
+ ### Added
11
+
12
+ - Added config option to look for wrappers recursively
13
+ - Exported `getCompilerConfigForContract` function for plugin support
14
+ - Added request timout configuration
15
+ - Added docs for script args
16
+ - Added the `rename` command which renames contracts
17
+ - Added the `pack` command which builds and prepares a publish-ready package of contracts' wrappers
18
+ - Added support for wallet IDs in mnemonic provider. Environment variables `WALLET_ID` or `SUBWALLET_NUMBER` should be set, or a .env file with them must be present in order for it to be usable
19
+ - Added command `blueprint snapshot` to run tests with metric collection and write new benchmark report
20
+ - Added option `blueprint test --gas-report` to run tests and compare with the last snapshot metrics
21
+
22
+ ### Fixed
23
+
24
+ - Fix address format in testnet
25
+
26
+ ### Changed
27
+
28
+ - Updated FunC stdlib
29
+
30
+ ## [0.33.1] - 2025-05-16
31
+
32
+ ### Fixed
33
+
34
+ - Fixed blueprint build command failure in Tact projects
35
+
8
36
  ## [0.33.0] - 2025-05-16
9
37
 
10
38
  ### Added
package/README.md CHANGED
@@ -16,12 +16,14 @@ A development environment for TON blockchain for writing, testing, and deploying
16
16
  * [Directory structure](#directory-structure)
17
17
  * [Building contracts](#building-contracts)
18
18
  * [Running the test suites](#running-the-test-suites)
19
- * [Deploying contracts](#deploying-contracts)
20
- * [Custom scripts](#custom-scripts)
19
+ * [Running scripts](#running-scripts)
20
+ * [Deploying contracts](#deploying-contracts)
21
+ * [Using Mnemonic Provider](#using-mnemonic-provider)
21
22
  * [Contract development](#contract-development)
22
23
  * [Creating contracts](#creating-contracts)
23
24
  * [Writing contract code](#writing-contract-code)
24
25
  * [Testing contracts](#testing-contracts)
26
+ * [Benchmark contracts](#benchmark-contracts)
25
27
  * [Configuration](#configuration)
26
28
  * [Plugins](#plugins)
27
29
  * [Custom network](#custom-network)
@@ -100,23 +102,58 @@ Blueprint is an all-in-one development environment designed to enhance the proce
100
102
 
101
103
  > Learn more about writing tests from the Sandbox's documentation - [here](https://github.com/ton-org/sandbox#writing-tests).
102
104
 
103
- ### Deploying contracts
104
-
105
- 1. You need a deployment script in `scripts/deploy<CONTRACT>.ts` - [example](/example/scripts/deployCounter.ts)
106
- 2. Run interactive: &nbsp;&nbsp; `npx blueprint run` &nbsp; or &nbsp; `yarn blueprint run`
107
- 3. Non-interactive: &nbsp; `npx/yarn blueprint run deploy<CONTRACT> --<NETWORK> --<DEPLOY_METHOD>`
108
- * Example: `yarn blueprint run deployCounter --mainnet --tonconnect`
109
-
110
- ### Custom scripts
105
+ ### Running scripts
111
106
 
112
107
  1. Custom scripts should be located in `scripts` folder
113
108
  2. Script file must have exported function `run`
114
109
  ```ts
115
- export async function run(provider: NetworkProvider) {
110
+ export async function run(provider: NetworkProvider, args: string[]) {
116
111
  //
117
112
  }
118
113
  ```
119
- 3. Script can be run using `npx/yarn blueprint run <SCRIPT>` command
114
+ 3. Script can be run using `npx/yarn blueprint run <SCRIPT> [arg1, arg2, ...]` command
115
+
116
+ #### Deploying contracts
117
+
118
+ 1. You need a deployment script in `scripts/deploy<CONTRACT>.ts` - [example](/example/scripts/deployCounter.ts)
119
+ 2. Run interactive: &nbsp;&nbsp; `npx blueprint run` &nbsp; or &nbsp; `yarn blueprint run`
120
+ 3. Non-interactive: &nbsp; `npx/yarn blueprint run deploy<CONTRACT> --<NETWORK> --<DEPLOY_METHOD>`
121
+ * Example: `yarn blueprint run deployCounter --mainnet --tonconnect`
122
+
123
+ #### Using Mnemonic Provider
124
+
125
+ To run scripts using a wallet by mnemonic authentication, you need to configure your environment and use the `Mnemonic` option when running scripts.
126
+
127
+ Start by adding the following environment variables to your `.env` file:
128
+ * **`WALLET_MNEMONIC`**: Your wallet's mnemonic phrase (space-separated words).
129
+ * **`WALLET_VERSION`**: The wallet contract version to use. Supported versions: `v1r1`, `v1r2`, `v1r3`, `v2r1`, `v2r2`, `v3r1`, `v3r2`, `v4`, `v5r1`.
130
+
131
+ **Optional variables:**
132
+ * **`WALLET_ID`**: The wallet ID (can be used with versions below `v5r1`).
133
+ * **`SUBWALLET_NUMBER`**: The subwallet number used to build the wallet ID (can be used with `v5r1` wallets).
134
+
135
+ Once your environment is set up, you can use the mnemonic wallet for deployment with the appropriate configuration.
136
+
137
+ #### Deploying contracts
138
+
139
+ 1. You need a deployment script in `scripts/deploy<CONTRACT>.ts` - [example](/example/scripts/deployCounter.ts)
140
+ 2. Run interactive: &nbsp;&nbsp; `npx blueprint run` &nbsp; or &nbsp; `yarn blueprint run`
141
+ 3. Non-interactive: &nbsp; `npx/yarn blueprint run deploy<CONTRACT> --<NETWORK> --<DEPLOY_METHOD>`
142
+ * Example: `yarn blueprint run deployCounter --mainnet --tonconnect`
143
+
144
+ #### Using Mnemonic Provider
145
+
146
+ To run scripts using a wallet by mnemonic authentication, you need to configure your environment and use the `Mnemonic` option when running scripts.
147
+
148
+ Start by adding the following environment variables to your `.env` file:
149
+ * **`WALLET_MNEMONIC`**: Your wallet's mnemonic phrase (space-separated words).
150
+ * **`WALLET_VERSION`**: The wallet contract version to use. Supported versions: `v1r1`, `v1r2`, `v1r3`, `v2r1`, `v2r2`, `v3r1`, `v3r2`, `v4`, `v5r1`.
151
+
152
+ **Optional variables:**
153
+ * **`WALLET_ID`**: The wallet ID (required for versions below `v5r1`).
154
+ * **`SUBWALLET_NUMBER`**: The subwallet number used to build the wallet ID (required for `v5r1` wallets).
155
+
156
+ Once your environment is set up, you can use the mnemonic wallet for deployment with the appropriate configuration.
120
157
 
121
158
  ### Updating FunC version
122
159
 
@@ -140,6 +177,12 @@ Before developing, make sure that your current working directory is located in t
140
177
  2. Non-interactive: &nbsp; `npx/yarn blueprint create <CONTRACT> --type <TYPE>` (type can be `func-empty`, `tolk-empty`, `tact-empty`, `func-counter`, `tolk-counter`, `tact-counter`)
141
178
  * Example: `yarn blueprint create MyNewContract --type func-empty`
142
179
 
180
+ ### Renaming contracts
181
+
182
+ 1. Run interactive: &nbsp;&nbsp; `npx blueprint rename` &nbsp; or &nbsp; `yarn blueprint rename`
183
+ 2. Non-interactive: &nbsp; `npx/yarn blueprint rename <OLD_NAME> <NEW_NAME>`
184
+ * Example: `yarn blueprint rename OldContract NewContract `
185
+
143
186
  ### Writing contract code
144
187
 
145
188
  #### FunC
@@ -162,6 +205,37 @@ Before developing, make sure that your current working directory is located in t
162
205
 
163
206
  > Learn more about writing tests from the Sandbox's documentation - [here](https://github.com/ton-org/sandbox#writing-tests).
164
207
 
208
+ ### Publishing Wrapper Code
209
+
210
+ 1. **Authenticate with npm**
211
+ Run `npm adduser` to log in to your npm account.
212
+ > 📝 **Note:** You can learn more about advanced authentication in the official npm docs:
213
+ > [npmrc – Auth-Related Configuration](https://docs.npmjs.com/cli/v9/configuring-npm/npmrc#auth-related-configuration)
214
+
215
+ 2. **Update the package version**
216
+ Edit the `version` field in your `package.json` to reflect the new release (e.g., `1.0.1` → `1.0.2`).
217
+
218
+ 3. **Build the package**
219
+ Run the following command to generate and bundle your contract wrappers:
220
+
221
+ ```bash
222
+ blueprint pack
223
+ ```
224
+
225
+ 4. **Publish to npm**
226
+ Push the package to the public npm registry:
227
+
228
+ ```bash
229
+ npm publish --access public
230
+ ```
231
+
232
+ ### Benchmark contracts
233
+
234
+ 1. Run `npx blueprint snapshot [--label=<comment>|-l <comment>]` to collect metrics of contracts and save snapshot
235
+ 2. Run `npx blueprint test --gas-report|-g` to compare current metrics and saved snapshot
236
+
237
+ > Learn more about collect metric from the Sandbox's documentation - [here](https://github.com/ton-org/sandbox#benchmark-contracts).
238
+
165
239
  ## Configuration
166
240
 
167
241
  A config may be created in order to control some of blueprint's features. If a config is needed, create a `blueprint.config.ts` file in the root of your project with something like this:
@@ -224,6 +298,18 @@ npx blueprint verify --custom https://toncenter.com/api/v2/jsonRPC --custom-vers
224
298
  ```
225
299
  (or similarly using the config), however custom type MUST be specified as either `mainnet` or `testnet` when verifying.
226
300
 
301
+ ### Request timeout
302
+
303
+ You can optionally configure how long HTTP requests should wait before timing out using the `requestTimout` field. This can be especially useful when working with unstable or slow networks.
304
+
305
+ ```typescript
306
+ import { Config } from '@ton/blueprint';
307
+
308
+ export const config: Config = {
309
+ requestTimeout: 10000, // 10 seconds
310
+ };
311
+ ```
312
+
227
313
  ## Contributors
228
314
 
229
315
  Special thanks to [@qdevstudio](https://t.me/qdevstudio) for their logo for blueprint.
@@ -3,6 +3,8 @@ import { UIProvider } from '../ui/UIProvider';
3
3
  import { Config } from '../config/Config';
4
4
  export declare const argSpec: {};
5
5
  export type Args = arg.Result<typeof argSpec>;
6
+ export declare function extractPosArg(args: Args, position: number): string | undefined;
7
+ export declare function extractSecondArg(args: Args): string | undefined;
6
8
  export declare function extractFirstArg(args: Args): string | undefined;
7
9
  export type RunnerContext = {
8
10
  config?: Config;
@@ -1,8 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.argSpec = void 0;
4
+ exports.extractPosArg = extractPosArg;
5
+ exports.extractSecondArg = extractSecondArg;
4
6
  exports.extractFirstArg = extractFirstArg;
5
7
  exports.argSpec = {};
8
+ function extractPosArg(args, position) {
9
+ return args._.length > position && args._[position].trim().length > 0 ? args._[position].trim() : undefined;
10
+ }
11
+ function extractSecondArg(args) {
12
+ return extractPosArg(args, 2);
13
+ }
6
14
  function extractFirstArg(args) {
7
- return args._.length > 1 && args._[1].trim().length > 0 ? args._[1].trim() : undefined;
15
+ return extractPosArg(args, 1);
8
16
  }
@@ -1,6 +1,5 @@
1
- import { Args, Runner } from './Runner';
2
1
  import { UIProvider } from '../ui/UIProvider';
3
- export declare function extractBuildFile(args: Args): string | undefined;
2
+ import { Runner } from './Runner';
4
3
  export declare function selectContract(ui: UIProvider, hint?: string): Promise<string>;
5
4
  export declare function selectContract(ui: UIProvider, hint?: string, withAllOption?: boolean): Promise<string | string[]>;
6
5
  export declare const build: Runner;
package/dist/cli/build.js CHANGED
@@ -4,15 +4,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.build = void 0;
7
- exports.extractBuildFile = extractBuildFile;
8
7
  exports.selectContract = selectContract;
9
- const utils_1 = require("../utils");
10
8
  const arg_1 = __importDefault(require("arg"));
9
+ const utils_1 = require("../utils");
11
10
  const build_1 = require("../build");
12
11
  const constants_1 = require("./constants");
13
- function extractBuildFile(args) {
14
- return args._.length > 1 && args._[1].length > 0 ? args._[1] : undefined;
15
- }
12
+ const Runner_1 = require("./Runner");
16
13
  async function selectContract(ui, hint, withAllOption = false) {
17
14
  const contracts = await (0, utils_1.findContracts)();
18
15
  const options = contracts.map((contract) => ({ name: contract, value: contract }));
@@ -47,7 +44,7 @@ const build = async (args, ui) => {
47
44
  await (0, build_1.buildAll)(ui);
48
45
  }
49
46
  else {
50
- const selected = await selectContract(ui, extractBuildFile(args), true);
47
+ const selected = await selectContract(ui, (0, Runner_1.extractFirstArg)(args), true);
51
48
  if (typeof selected === 'string') {
52
49
  await (0, build_1.buildOne)(selected, ui);
53
50
  }
package/dist/cli/cli.js CHANGED
@@ -41,6 +41,7 @@ const dotenv = __importStar(require("dotenv"));
41
41
  dotenv.config();
42
42
  const arg_1 = __importDefault(require("arg"));
43
43
  const chalk_1 = __importDefault(require("chalk"));
44
+ const snapshot_1 = require("./snapshot");
44
45
  const create_1 = require("./create");
45
46
  const run_1 = require("./run");
46
47
  const build_1 = require("./build");
@@ -49,9 +50,11 @@ const test_1 = require("./test");
49
50
  const verify_1 = require("./verify");
50
51
  const convert_1 = require("./convert");
51
52
  const help_1 = require("./help");
53
+ const pack_1 = require("./pack");
52
54
  const InquirerUIProvider_1 = require("../ui/InquirerUIProvider");
53
55
  const Runner_1 = require("./Runner");
54
56
  const utils_1 = require("../config/utils");
57
+ const rename_1 = require("./rename");
55
58
  const runners = {
56
59
  create: create_1.create,
57
60
  run: run_1.run,
@@ -61,6 +64,9 @@ const runners = {
61
64
  help: help_1.help,
62
65
  verify: verify_1.verify,
63
66
  convert: convert_1.convert,
67
+ rename: rename_1.rename,
68
+ pack: pack_1.pack,
69
+ snapshot: snapshot_1.snapshot,
64
70
  };
65
71
  async function main() {
66
72
  require('ts-node/register');
@@ -95,7 +101,8 @@ async function main() {
95
101
  const command = args._[0];
96
102
  const runner = effectiveRunners[command];
97
103
  if (!runner) {
98
- console.log(chalk_1.default.redBright(`Error: command ${command} not found.`) + `\nRunning ${chalk_1.default.cyanBright('blueprint help')}...`);
104
+ console.log(chalk_1.default.redBright(`Error: command ${command} not found.`) +
105
+ `\nRunning ${chalk_1.default.cyanBright('blueprint help')}...`);
99
106
  const helpMessage = (0, help_1.buildHelpMessage)();
100
107
  console.log(helpMessage);
101
108
  process.exit(1);
@@ -4,6 +4,7 @@ export declare const templateTypes: {
4
4
  }[];
5
5
  export declare const helpArgs: {
6
6
  '--help': BooleanConstructor;
7
+ '-h': string;
7
8
  };
8
9
  export declare const helpMessages: {
9
10
  help: string;
@@ -14,4 +15,7 @@ export declare const helpMessages: {
14
15
  test: string;
15
16
  verify: string;
16
17
  convert: string;
18
+ rename: string;
19
+ pack: string;
20
+ snapshot: string;
17
21
  };
@@ -31,8 +31,20 @@ exports.templateTypes = [
31
31
  value: 'tact-counter',
32
32
  },
33
33
  ];
34
- exports.helpArgs = { '--help': Boolean };
35
- const availableCommands = ['create', 'run', 'build', 'set', 'help', 'test', 'verify', 'convert'];
34
+ exports.helpArgs = { '--help': Boolean, '-h': '--help' };
35
+ const availableCommands = [
36
+ 'create',
37
+ 'run',
38
+ 'build',
39
+ 'set',
40
+ 'help',
41
+ 'test',
42
+ 'verify',
43
+ 'convert',
44
+ 'rename',
45
+ 'pack',
46
+ 'snapshot',
47
+ ];
36
48
  exports.helpMessages = {
37
49
  help: `${chalk_1.default.bold('Usage:')} blueprint ${chalk_1.default.cyan('help')} [${chalk_1.default.yellow('command')}]
38
50
 
@@ -54,7 +66,7 @@ ${chalk_1.default.cyan('--type')} <type> - specifies the template type to use wh
54
66
 
55
67
  ${chalk_1.default.bold('List of available types:')}
56
68
  ${exports.templateTypes.map((t) => `${chalk_1.default.cyan(t.value)} - ${t.name}`).join('\n')}`,
57
- run: `${chalk_1.default.bold('Usage:')} blueprint ${chalk_1.default.cyan('run')} ${chalk_1.default.yellow('[script name]')} ${chalk_1.default.gray('[flags]')}
69
+ run: `${chalk_1.default.bold('Usage:')} blueprint ${chalk_1.default.cyan('run')} ${chalk_1.default.yellow('[script name]')} ${chalk_1.default.gray('[flags]')} ${chalk_1.default.gray('[...args]')}
58
70
 
59
71
  Runs a script from the scripts directory.
60
72
 
@@ -67,7 +79,12 @@ ${chalk_1.default.cyan('--custom-version')} - API version (v2, v4)
67
79
  ${chalk_1.default.cyan('--custom-key')} - API key (v2 only)
68
80
  ${chalk_1.default.cyan('--custom-type')} - network type (custom, mainnet, testnet)
69
81
  ${chalk_1.default.cyan('--tonconnect')}, ${chalk_1.default.cyan('--deeplink')}, ${chalk_1.default.cyan('--mnemonic')} - deployer options
70
- ${chalk_1.default.cyan('--tonscan')}, ${chalk_1.default.cyan('--tonviewer')}, ${chalk_1.default.cyan('--toncx')}, ${chalk_1.default.cyan('--dton')} - explorer (default: tonviewer)`,
82
+ ${chalk_1.default.cyan('--tonscan')}, ${chalk_1.default.cyan('--tonviewer')}, ${chalk_1.default.cyan('--toncx')}, ${chalk_1.default.cyan('--dton')} - explorer (default: tonviewer)
83
+ ${chalk_1.default.gray('[...args]')} (array of strings, optional) - Arguments passed directly to the script.
84
+
85
+ ${chalk_1.default.bold('Examples:')}
86
+ blueprint run ${chalk_1.default.yellow('deployCounter')} ${chalk_1.default.cyan('--testnet')} ${chalk_1.default.cyan('--tonconnect')}
87
+ blueprint run ${chalk_1.default.yellow('incrementCounter')} ${chalk_1.default.cyan('--testnet')} ${chalk_1.default.cyan('--tonconnect')} ${chalk_1.default.gray('0.05 1')}`,
71
88
  build: `${chalk_1.default.bold('Usage:')} blueprint ${chalk_1.default.cyan('build')} ${chalk_1.default.yellow('[contract name]')} ${chalk_1.default.gray('[flags]')}
72
89
 
73
90
  Builds the specified contract according to the respective .compile.ts file. For Tact contracts, all generated files will be placed in the ${chalk_1.default.cyan('build/<contract name>')} folder.
@@ -78,9 +95,15 @@ ${chalk_1.default.cyan('--all')} - builds all available contracts.`,
78
95
 
79
96
  ${chalk_1.default.bold('Available keys:')}
80
97
  - ${chalk_1.default.cyan('func')} - overrides @ton-community/func-js-bin version.`,
81
- test: `${chalk_1.default.bold('Usage:')} blueprint ${chalk_1.default.cyan('test')} [...args]
98
+ test: `${chalk_1.default.bold('Usage:')} blueprint ${chalk_1.default.cyan('test')} ${chalk_1.default.yellow('[--gas-report|-g ...args]')}
99
+ Runs ${chalk_1.default.green('npm test [...args]')}, which by default executes ${chalk_1.default.green('jest')}
100
+
101
+ ${chalk_1.default.bold('Options:')}
102
+ ${chalk_1.default.cyan('--gas-report')}, ${chalk_1.default.cyan('-g')} - Run tests and compare with the last snapshot's metrics
82
103
 
83
- Runs ${chalk_1.default.green('npm test [...args]')}, which by default executes ${chalk_1.default.green('jest')}.`,
104
+ ${chalk_1.default.bold('SEE ALSO')}
105
+ ${chalk_1.default.cyan('blueprint snapshot')}
106
+ `,
84
107
  verify: `${chalk_1.default.bold('Usage:')} blueprint ${chalk_1.default.cyan('verify')} ${chalk_1.default.yellow('[contract name]')} ${chalk_1.default.gray('[flags]')}
85
108
 
86
109
  Verifies a deployed contract on ${chalk_1.default.underline('https://verifier.ton.org')}.
@@ -94,4 +117,19 @@ ${chalk_1.default.cyan('--custom-type')} - network type (mainnet, testnet)`,
94
117
  convert: `${chalk_1.default.bold('Usage:')} blueprint ${chalk_1.default.cyan('convert')} ${chalk_1.default.yellow('[path to build script]')}
95
118
 
96
119
  Attempts to convert a legacy bash build script to a Blueprint compile wrapper.`,
120
+ rename: `${chalk_1.default.bold('Usage:')} blueprint ${chalk_1.default.cyan('rename')} ${chalk_1.default.yellow('[old contract name (PascalCase)]')} ${chalk_1.default.yellow('[new contract name (PascalCase)]')}
121
+
122
+ Renames contract by exact matching in wrappers, scripts, tests and contracts folders.`,
123
+ pack: `${chalk_1.default.bold('Usage:')} blueprint ${chalk_1.default.cyan('pack')}
124
+
125
+ Builds and prepares a publish-ready package of contract wrappers.
126
+
127
+ ${chalk_1.default.bold('Flags:')}
128
+ ${chalk_1.default.cyan('--no-warn')}, ${chalk_1.default.cyan('-n')} - ignore warnings about modifying tsconfig.json, package.json, and removing the dist directory.`,
129
+ snapshot: `${chalk_1.default.bold('Usage:')} blueprint ${chalk_1.default.cyan('snapshot')} ${chalk_1.default.yellow('[--label=<comment>|-l=<comment>]')}
130
+
131
+ Run with gas usage and cells' sizes collected and write a new snapshot
132
+
133
+ ${chalk_1.default.bold('SEE ALSO')}
134
+ ${chalk_1.default.cyan('blueprint test --gas-report')}`,
97
135
  };
@@ -14,10 +14,6 @@ const template_1 = require("../template");
14
14
  const utils_2 = require("../utils");
15
15
  const build_1 = require("../build");
16
16
  const constants_1 = require("./constants");
17
- function toSnakeCase(v) {
18
- const r = v.replace(/[A-Z]/g, (sub) => '_' + sub.toLowerCase());
19
- return r[0] === '_' ? r.substring(1) : r;
20
- }
21
17
  async function createFile(templatePath, realPath, replaces) {
22
18
  const template = (await (0, promises_1.readFile)(templatePath)).toString('utf-8');
23
19
  const lines = template.split('\n');
@@ -84,21 +80,14 @@ const create = async (args, ui) => {
84
80
  return;
85
81
  }
86
82
  const name = (0, Runner_1.extractFirstArg)(localArgs) ?? (await ui.input('Contract name (PascalCase)'));
87
- if (name.length === 0)
88
- throw new Error(`Cannot create a contract with an empty name`);
89
- if (name.toLowerCase() === 'contract') {
90
- throw new Error(`Cannot create a contract with the reserved name 'contract'. Please choose a different name.`);
91
- }
92
- if (!(0, utils_2.isPascalCase)(name)) {
93
- throw new Error(`Contract name '${name}' is not in PascalCase. Please try ${(0, utils_2.toPascalCase)(name)}.`);
94
- }
83
+ (0, utils_2.assertValidContractName)(name);
95
84
  const which = (await (0, utils_2.selectOption)(constants_1.templateTypes, {
96
85
  ui,
97
86
  msg: 'What type of contract do you want to create?',
98
87
  hint: localArgs['--type'],
99
88
  })).value;
100
89
  const [lang, template] = which.split('-');
101
- const snakeName = toSnakeCase(name);
90
+ const snakeName = (0, utils_2.toSnakeCase)(name);
102
91
  const contractPath = path_1.default.join('contracts', snakeName + '.' + getFileExtension(lang));
103
92
  const replaces = {
104
93
  name,
package/dist/cli/help.js CHANGED
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.help = exports.additionalHelpMessages = void 0;
4
4
  exports.buildHelpMessage = buildHelpMessage;
5
+ const Runner_1 = require("./Runner");
5
6
  const constants_1 = require("./constants");
6
7
  exports.additionalHelpMessages = {};
7
8
  function buildHelpMessage(cmd = '') {
@@ -15,7 +16,7 @@ function buildHelpMessage(cmd = '') {
15
16
  return cmd in effectiveHelpMessages ? effectiveHelpMessages[cmd] : effectiveHelpMessages['help'];
16
17
  }
17
18
  const help = async (args, ui) => {
18
- const cmd = args._.length >= 2 ? args._[1].toLowerCase() : '';
19
+ const cmd = (0, Runner_1.extractFirstArg)(args)?.toLowerCase();
19
20
  const helpMessage = buildHelpMessage(cmd);
20
21
  ui.write(helpMessage);
21
22
  };
@@ -0,0 +1,2 @@
1
+ import { Runner } from './Runner';
2
+ export declare const pack: Runner;
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.pack = void 0;
7
+ const child_process_1 = require("child_process");
8
+ const path_1 = __importDefault(require("path"));
9
+ const arg_1 = __importDefault(require("arg"));
10
+ const fs_1 = require("fs");
11
+ const constants_1 = require("./constants");
12
+ const build_1 = require("../build");
13
+ const paths_1 = require("../paths");
14
+ const utils_1 = require("../utils");
15
+ const compile_1 = require("../compile/compile");
16
+ const CompilerConfig_1 = require("../compile/CompilerConfig");
17
+ const compile_tact_1 = require("../compile/tact/compile.tact");
18
+ async function correctTsConfig() {
19
+ if (!(0, fs_1.existsSync)(paths_1.TYPESCRIPT_CONFIG)) {
20
+ throw new Error('TypeScript config does not exist. Ensure the command runs in the correct environment.');
21
+ }
22
+ const tsConfig = JSON.parse(await fs_1.promises.readFile(paths_1.TYPESCRIPT_CONFIG, 'utf-8'));
23
+ const newConfig = {
24
+ ...tsConfig,
25
+ compilerOptions: {
26
+ ...tsConfig.compilerOptions,
27
+ outDir: 'dist',
28
+ declaration: true,
29
+ esModuleInterop: true,
30
+ },
31
+ include: (0, utils_1.distinct)([...(tsConfig.include ?? []), 'package.ts']),
32
+ };
33
+ await fs_1.promises.writeFile(paths_1.TYPESCRIPT_CONFIG, JSON.stringify(newConfig, null, 2), 'utf8');
34
+ }
35
+ async function getContractWrapperPath(contract) {
36
+ const config = await (0, compile_1.getCompilerConfigForContract)(contract);
37
+ if ((0, CompilerConfig_1.isCompilableConfig)(config)) {
38
+ return `./wrappers/${contract}`;
39
+ }
40
+ else {
41
+ const contractConfig = (0, compile_tact_1.extractContractConfig)(config, contract);
42
+ return `./${contractConfig.output}/${contract}_${contract}`;
43
+ }
44
+ }
45
+ async function generatePackageEntryPoint() {
46
+ const contracts = await (0, utils_1.findContracts)();
47
+ let entryPoint = 'import { Cell } from "@ton/core"\n';
48
+ for (const contract of contracts) {
49
+ const wrapperPath = await getContractWrapperPath(contract);
50
+ entryPoint += `import * as ${contract} from '${wrapperPath}';\n`;
51
+ entryPoint += `export { ${contract} };\n`;
52
+ const buildArtifactPath = path_1.default.join(paths_1.BUILD_DIR, `${contract}.compiled.json`);
53
+ const buildArtifact = JSON.parse(await fs_1.promises.readFile(buildArtifactPath, 'utf8'));
54
+ entryPoint += `export const ${contract}Code = Cell.fromHex("${buildArtifact.hex}");\n`;
55
+ }
56
+ await fs_1.promises.writeFile(paths_1.PACKAGE_ENTRY_POINT, entryPoint, 'utf8');
57
+ }
58
+ async function correctPackageJson() {
59
+ const packageJson = JSON.parse(await fs_1.promises.readFile(paths_1.PACKAGE_JSON, 'utf8'));
60
+ const newPackageJson = {
61
+ ...packageJson,
62
+ main: 'dist/package.js',
63
+ files: ['dist/**/*'],
64
+ };
65
+ await fs_1.promises.writeFile(paths_1.PACKAGE_JSON, JSON.stringify(newPackageJson, null, 2));
66
+ }
67
+ const pack = async (args, ui, context) => {
68
+ const localArgs = (0, arg_1.default)({
69
+ '--no-warn': Boolean,
70
+ '-n': '--no-warn',
71
+ ...constants_1.helpArgs,
72
+ });
73
+ if (localArgs['--help']) {
74
+ ui.write(constants_1.helpMessages['pack']);
75
+ return;
76
+ }
77
+ if (!localArgs['--no-warn']) {
78
+ ui.write('🚨 WARNING: This command may modify tsconfig.json, package.json, and remove the dist directory. Make sure these files are committed to your repository. If you wish to ignore this warning, use the -n or --no-warn flag.');
79
+ const answer = await ui.input('Are you sure you want to continue? (y/N)');
80
+ if (answer.toLowerCase() !== 'y') {
81
+ ui.write('Aborting...');
82
+ return;
83
+ }
84
+ }
85
+ ui.write('🔨 Building contracts... Please wait...');
86
+ await (0, build_1.buildAll)(ui);
87
+ ui.write('📦 Generating package entry point...');
88
+ await generatePackageEntryPoint();
89
+ ui.write('🛠️ Updating tsconfig.json...');
90
+ await correctTsConfig();
91
+ ui.write('🏗️ Building package...');
92
+ await fs_1.promises.rm(path_1.default.join(process.cwd(), 'dist'), { recursive: true, force: true });
93
+ (0, child_process_1.execSync)(`tsc`, { stdio: 'inherit' });
94
+ ui.write('📝 Updating package.json...');
95
+ await correctPackageJson();
96
+ ui.write('🎉 Package is ready and packed successfully! You can now publish it 🚀');
97
+ };
98
+ exports.pack = pack;
@@ -0,0 +1,6 @@
1
+ import { Runner } from './Runner';
2
+ export declare function renameExactIfRequired(str: string, replaces: Record<string, string>): {
3
+ isRenamed: boolean;
4
+ newValue: string;
5
+ };
6
+ export declare const rename: Runner;
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.rename = void 0;
7
+ exports.renameExactIfRequired = renameExactIfRequired;
8
+ const arg_1 = __importDefault(require("arg"));
9
+ const fs_1 = require("fs");
10
+ const path_1 = __importDefault(require("path"));
11
+ const paths_1 = require("../paths");
12
+ const Runner_1 = require("./Runner");
13
+ const utils_1 = require("../utils");
14
+ const constants_1 = require("./constants");
15
+ const build_1 = require("./build");
16
+ function renameExactIfRequired(str, replaces) {
17
+ let renamedString = str;
18
+ let isRenamed = false;
19
+ for (const [oldValue, newValue] of Object.entries(replaces)) {
20
+ const regex = new RegExp(`\\b${oldValue}\\b`, 'g');
21
+ if (regex.test(renamedString)) {
22
+ isRenamed = true;
23
+ renamedString = renamedString.replace(regex, newValue);
24
+ }
25
+ }
26
+ return { isRenamed, newValue: renamedString };
27
+ }
28
+ // Introduced this class to prevent fails in the middle of renaming, when the part is renamed and part is not
29
+ class RenameContext {
30
+ constructor(replaces) {
31
+ this.replaces = replaces;
32
+ this.effects = [];
33
+ }
34
+ async prepareRenameExactOccurrencesInDirectory(directory) {
35
+ if (!(0, fs_1.existsSync)(directory)) {
36
+ return;
37
+ }
38
+ const dir = await fs_1.promises.readdir(directory, { recursive: true, withFileTypes: true });
39
+ await Promise.all(dir.map(async (dir) => {
40
+ if (!dir.isFile()) {
41
+ return;
42
+ }
43
+ const filePath = path_1.default.join(dir.path, dir.name);
44
+ await this.prepareRenameContentInFile(filePath);
45
+ const pathRenameResult = renameExactIfRequired(dir.name, this.replaces);
46
+ if (pathRenameResult.isRenamed) {
47
+ this.effects.push(() => fs_1.promises.rename(path_1.default.join(dir.path, dir.name), path_1.default.join(dir.path, pathRenameResult.newValue)));
48
+ }
49
+ }));
50
+ }
51
+ async prepareRenameContentInFile(filePath) {
52
+ const content = await fs_1.promises.readFile(filePath, 'utf8');
53
+ const { isRenamed, newValue } = renameExactIfRequired(content, this.replaces);
54
+ if (isRenamed) {
55
+ this.effects.push(() => fs_1.promises.writeFile(filePath, newValue, 'utf8'));
56
+ }
57
+ }
58
+ async applyEffects() {
59
+ for (const effect of this.effects) {
60
+ await effect();
61
+ }
62
+ }
63
+ }
64
+ const rename = async (args, ui, context) => {
65
+ const localArgs = (0, arg_1.default)(constants_1.helpArgs);
66
+ if (localArgs['--help']) {
67
+ ui.write(constants_1.helpMessages['rename']);
68
+ return;
69
+ }
70
+ const oldName = await (0, build_1.selectContract)(ui, (0, Runner_1.extractFirstArg)(localArgs));
71
+ const newName = (0, Runner_1.extractSecondArg)(localArgs) ?? (await ui.input('New contract name (PascalCase)'));
72
+ (0, utils_1.assertValidContractName)(newName);
73
+ const contracts = await (0, utils_1.findContracts)();
74
+ if (contracts.includes(newName)) {
75
+ ui.write(`Contract with name ${newName} already exists.`);
76
+ process.exit(1);
77
+ }
78
+ const modifiers = [
79
+ (name) => name,
80
+ utils_1.toSnakeCase,
81
+ utils_1.toLowerCase,
82
+ (name) => `deploy${name}`,
83
+ (name) => `${name}_${name}`,
84
+ (name) => `increment${name}`,
85
+ (name) => `${(0, utils_1.toLowerCase)(name)}ConfigToCell`,
86
+ (name) => `${name}Config`,
87
+ ];
88
+ const replaces = Object.fromEntries(modifiers.map((modifier) => [modifier(oldName), modifier(newName)]));
89
+ ui.setActionPrompt('Renaming in progress...');
90
+ const renameContext = new RenameContext(replaces);
91
+ for (const directory of [paths_1.SCRIPTS_DIR, paths_1.WRAPPERS_DIR, paths_1.CONTRACTS_DIR, paths_1.TESTS_DIR, paths_1.COMPILABLES_DIR]) {
92
+ await renameContext.prepareRenameExactOccurrencesInDirectory(directory);
93
+ }
94
+ if ((0, fs_1.existsSync)(paths_1.TACT_ROOT_CONFIG)) {
95
+ await renameContext.prepareRenameContentInFile(paths_1.TACT_ROOT_CONFIG);
96
+ }
97
+ await renameContext.applyEffects();
98
+ ui.clearActionPrompt();
99
+ ui.write('Contract successfully renamed!');
100
+ };
101
+ exports.rename = rename;
@@ -0,0 +1 @@
1
+ export {};