@ton/blueprint 0.13.0 → 0.14.1

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 CHANGED
@@ -5,6 +5,18 @@ 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.14.1] - 2023-12-01
9
+
10
+ ### Fixed
11
+
12
+ - Fixed test templates (added missing imports)
13
+
14
+ ## [0.14.0] - 2023-11-23
15
+
16
+ ### Added
17
+
18
+ - Added `verify` command to quickly verify contracts on [verifier.ton.org](https://verifier.ton.org)
19
+
8
20
  ## [0.13.0] - 2023-11-05
9
21
 
10
22
  ### Added
package/README.md CHANGED
@@ -103,7 +103,25 @@ Run in terminal:   `npx blueprint help`   or   `yarn blueprint he
103
103
  2. Implement a deployment script in `scripts/deploy<CONTRACT>.ts`
104
104
  3. Rely on the wrapper TypeScript class from `wrappers/<CONTRACT>.ts` to initialize the contract
105
105
 
106
- &nbsp;
106
+ ## Plugins
107
+
108
+ Blueprint has a plugin system to allow the community to develop their own additions for the ecosystem without the need to change blueprint's code.
109
+
110
+ In order to use plugins, create a `blueprint.config.ts` file in the root of your project with something like this:
111
+ ```typescript
112
+ import { Config } from '@ton/blueprint';
113
+ import { ScaffoldPlugin } from 'blueprint-scaffold';
114
+
115
+ export const config: Config = {
116
+ plugins: [new ScaffoldPlugin()],
117
+ };
118
+ ```
119
+ (This example shows how to add the [scaffold](https://github.com/1IxI1/blueprint-scaffold) plugin)
120
+
121
+ It is important that the config is exported, is named `config`, and is not `default` exported.
122
+
123
+ Here are some of the plugins developed by the community:
124
+ - [scaffold](https://github.com/1IxI1/blueprint-scaffold) - allows developers to quickly create a simple dapp automatically using the wrappers' code
107
125
 
108
126
  ## Contributors
109
127
 
@@ -1,2 +1,8 @@
1
- import { Runner } from './Runner';
1
+ import { Args, Runner } from './Runner';
2
+ import { UIProvider } from '../ui/UIProvider';
3
+ export declare function selectCompile(ui: UIProvider, args: Args): Promise<{
4
+ module: any;
5
+ name: string;
6
+ path: string;
7
+ }>;
2
8
  export declare const build: Runner;
package/dist/cli/build.js CHANGED
@@ -3,12 +3,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.build = void 0;
6
+ exports.build = exports.selectCompile = void 0;
7
7
  const utils_1 = require("../utils");
8
8
  const arg_1 = __importDefault(require("arg"));
9
9
  const build_1 = require("../build");
10
+ async function selectCompile(ui, args) {
11
+ return await (0, utils_1.selectFile)(await (0, utils_1.findCompiles)(), {
12
+ ui,
13
+ hint: args._.length > 1 && args._[1].length > 0 ? args._[1] : undefined,
14
+ import: false,
15
+ });
16
+ }
17
+ exports.selectCompile = selectCompile;
10
18
  const build = async (args, ui) => {
11
- require('ts-node/register');
12
19
  const localArgs = (0, arg_1.default)({
13
20
  '--all': Boolean,
14
21
  });
@@ -16,11 +23,7 @@ const build = async (args, ui) => {
16
23
  await (0, build_1.buildAll)();
17
24
  }
18
25
  else {
19
- const sel = await (0, utils_1.selectFile)(await (0, utils_1.findCompiles)(), {
20
- ui,
21
- hint: args._.length > 1 && args._[1].length > 0 ? args._[1] : undefined,
22
- import: false,
23
- });
26
+ const sel = await selectCompile(ui, args);
24
27
  await (0, build_1.buildOne)(sel.name, ui);
25
28
  }
26
29
  };
package/dist/cli/cli.js CHANGED
@@ -35,6 +35,7 @@ const create_1 = require("./create");
35
35
  const run_1 = require("./run");
36
36
  const build_1 = require("./build");
37
37
  const test_1 = require("./test");
38
+ const verify_1 = require("./verify");
38
39
  const help_1 = require("./help");
39
40
  const InquirerUIProvider_1 = require("../ui/InquirerUIProvider");
40
41
  const Runner_1 = require("./Runner");
@@ -45,6 +46,7 @@ const runners = {
45
46
  build: build_1.build,
46
47
  test: test_1.test,
47
48
  help: help_1.help,
49
+ verify: verify_1.verify,
48
50
  };
49
51
  async function main() {
50
52
  var _a;
package/dist/cli/help.js CHANGED
@@ -14,7 +14,8 @@ List of available commands:
14
14
  - run
15
15
  - build
16
16
  - help
17
- - test`,
17
+ - test
18
+ - verify`,
18
19
  create: `Usage: blueprint create [contract name] [flags]
19
20
 
20
21
  Creates a new contract together with supporting files according to a template.
@@ -47,6 +48,12 @@ Flags:
47
48
  test: `Usage: blueprint test
48
49
 
49
50
  Just runs \`npm test\`, which by default runs \`jest\`.`,
51
+ verify: `Usage: blueprint verify [contract name] [flags]
52
+
53
+ Builds a contract (similar to build command) and verifies it on https://verifier.ton.org. The contract must be already deployed on the network. If the contract's name is not specified on the command line, it will be asked interactively.
54
+
55
+ Flags:
56
+ --mainnet, --testnet - specifies the network on which to verify the contract. If not specified on the command line, it will be asked interactively.`
50
57
  };
51
58
  exports.additionalHelpMessages = {};
52
59
  const help = async (args, ui) => {
package/dist/cli/run.js CHANGED
@@ -4,7 +4,6 @@ exports.run = void 0;
4
4
  const createNetworkProvider_1 = require("../network/createNetworkProvider");
5
5
  const utils_1 = require("../utils");
6
6
  const run = async (args, ui) => {
7
- require('ts-node/register');
8
7
  const { module: mod } = await (0, utils_1.selectFile)(await (0, utils_1.findScripts)(), {
9
8
  ui,
10
9
  hint: args._.length > 1 && args._[1].length > 0 ? args._[1] : undefined,
package/dist/cli/test.js CHANGED
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.test = void 0;
4
4
  const child_process_1 = require("child_process");
5
5
  const test = async () => {
6
- require('ts-node/register');
7
6
  (0, child_process_1.execSync)('npm test', { stdio: 'inherit' });
8
7
  };
9
8
  exports.test = test;
@@ -0,0 +1,2 @@
1
+ import { Runner } from './Runner';
2
+ export declare const verify: Runner;
@@ -0,0 +1,179 @@
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.verify = void 0;
7
+ const core_1 = require("@ton/core");
8
+ const compile_1 = require("../compile/compile");
9
+ const path_1 = __importDefault(require("path"));
10
+ const createNetworkProvider_1 = require("../network/createNetworkProvider");
11
+ const build_1 = require("./build");
12
+ const backends = {
13
+ mainnet: {
14
+ verifierRegistry: core_1.Address.parse('EQDS0AW7NV1w3nFwx-mmryfpH4OGQ3PXnoFGOJA_8PTHuLrw'),
15
+ backends: [
16
+ 'https://ton-source-prod-1.herokuapp.com',
17
+ 'https://ton-source-prod-2.herokuapp.com',
18
+ 'https://ton-source-prod-3.herokuapp.com',
19
+ ],
20
+ id: 'orbs.com',
21
+ },
22
+ testnet: {
23
+ verifierRegistry: core_1.Address.parse('EQB--CRXUbqYbqJKScEWeOrnk0TKJxB071M-WwvMpMEvq6Ed'),
24
+ backends: ['https://ton-source-prod-testnet-1.herokuapp.com'],
25
+ id: 'orbs-testnet',
26
+ },
27
+ };
28
+ function removeRandom(els) {
29
+ return els.splice(Math.floor(Math.random() * els.length), 1)[0];
30
+ }
31
+ class VerifierRegistry {
32
+ constructor(address) {
33
+ this.address = address;
34
+ }
35
+ async getVerifiers(provider) {
36
+ const res = await provider.get('get_verifiers', []);
37
+ const item = res.stack.readCell();
38
+ const c = item.beginParse();
39
+ const d = c.loadDict(core_1.Dictionary.Keys.BigUint(256), {
40
+ serialize: () => {
41
+ throw undefined;
42
+ },
43
+ parse: (s) => s,
44
+ });
45
+ return Array.from(d.values()).map((v) => {
46
+ const admin = v.loadAddress();
47
+ const quorom = v.loadUint(8);
48
+ const pubKeyEndpoints = v.loadDict(core_1.Dictionary.Keys.BigUint(256), core_1.Dictionary.Values.Uint(32));
49
+ return {
50
+ admin: admin,
51
+ quorum: quorom,
52
+ pubKeyEndpoints: new Map(Array.from(pubKeyEndpoints).map(([k, v]) => [k, v])),
53
+ name: v.loadRef().beginParse().loadStringTail(),
54
+ url: v.loadRef().beginParse().loadStringTail(),
55
+ };
56
+ });
57
+ }
58
+ }
59
+ const verify = async (args, ui) => {
60
+ const sel = await (0, build_1.selectCompile)(ui, args);
61
+ const networkProvider = await (0, createNetworkProvider_1.createNetworkProvider)(ui, false);
62
+ const sender = networkProvider.sender();
63
+ const senderAddress = sender.address;
64
+ if (senderAddress === undefined) {
65
+ throw new Error('Sender address needs to be known');
66
+ }
67
+ const network = networkProvider.network();
68
+ if (network === 'custom') {
69
+ throw new Error('Cannot use custom network');
70
+ }
71
+ const addr = await ui.input('Deployed contract address');
72
+ const result = await (0, compile_1.doCompile)(sel.name);
73
+ let src;
74
+ const fd = new FormData();
75
+ if (result.lang === 'func') {
76
+ for (const f of result.snapshot) {
77
+ fd.append(f.filename, new Blob([f.content]), path_1.default.basename(f.filename));
78
+ }
79
+ src = {
80
+ compiler: 'func',
81
+ compilerSettings: {
82
+ funcVersion: result.version,
83
+ commandLine: '-SPA ' + result.targets.join(' '),
84
+ },
85
+ knownContractAddress: addr,
86
+ knownContractHash: result.code.hash().toString('base64'),
87
+ sources: result.snapshot.map((s) => ({
88
+ includeInCommand: result.targets.includes(s.filename),
89
+ isEntrypoint: result.targets.includes(s.filename),
90
+ isStdLib: false,
91
+ hasIncludeDirectives: true,
92
+ folder: path_1.default.dirname(s.filename),
93
+ })),
94
+ senderAddress: senderAddress.toString(),
95
+ };
96
+ }
97
+ else if (result.lang === 'tact') {
98
+ let pkg = undefined;
99
+ for (const [k, v] of result.fs) {
100
+ if (k.endsWith('.pkg')) {
101
+ pkg = {
102
+ name: k,
103
+ content: v,
104
+ };
105
+ break;
106
+ }
107
+ }
108
+ if (pkg === undefined) {
109
+ throw new Error('Could not find .pkg in compilation results');
110
+ }
111
+ fd.append(path_1.default.basename(pkg.name), new Blob([pkg.content]), path_1.default.basename(pkg.name));
112
+ src = {
113
+ compiler: 'tact',
114
+ compilerSettings: {
115
+ tactVersion: '',
116
+ },
117
+ knownContractAddress: addr,
118
+ knownContractHash: result.code.hash().toString('base64'),
119
+ sources: [
120
+ {
121
+ includeInCommand: true,
122
+ isEntrypoint: false,
123
+ isStdLib: false,
124
+ hasIncludeDirectives: false,
125
+ folder: '',
126
+ },
127
+ ],
128
+ senderAddress: senderAddress.toString(),
129
+ };
130
+ }
131
+ else {
132
+ // future proofing
133
+ throw new Error('Unsupported language ' + result.lang);
134
+ }
135
+ fd.append('json', new Blob([JSON.stringify(src)], {
136
+ type: 'application/json',
137
+ }), 'blob');
138
+ const backend = backends[network];
139
+ const verifierRegistry = networkProvider.open(new VerifierRegistry(backend.verifierRegistry));
140
+ const verifier = (await verifierRegistry.getVerifiers()).find((v) => v.name === backend.id);
141
+ if (verifier === undefined) {
142
+ throw new Error('Could not find verifier');
143
+ }
144
+ const remainingBackends = [...backend.backends];
145
+ const sourceResponse = await fetch(removeRandom(remainingBackends) + '/source', {
146
+ method: 'POST',
147
+ body: fd,
148
+ });
149
+ if (sourceResponse.status !== 200) {
150
+ throw new Error('Could not compile on backend:\n' + (await sourceResponse.text()));
151
+ }
152
+ const sourceResult = await sourceResponse.json();
153
+ if (sourceResult.compileResult.result !== 'similar') {
154
+ throw new Error('The code is not similar');
155
+ }
156
+ let msgCell = sourceResult.msgCell;
157
+ let acquiredSigs = 1;
158
+ while (acquiredSigs < verifier.quorum) {
159
+ const signResponse = await fetch(removeRandom(remainingBackends) + '/sign', {
160
+ method: 'POST',
161
+ body: JSON.stringify({
162
+ messageCell: msgCell,
163
+ }),
164
+ });
165
+ if (signResponse.status !== 200) {
166
+ throw new Error('Could not compile on backend:\n' + (await signResponse.text()));
167
+ }
168
+ const signResult = await signResponse.json();
169
+ msgCell = signResult.msgCell;
170
+ acquiredSigs++;
171
+ }
172
+ const c = core_1.Cell.fromBoc(Buffer.from(msgCell.data))[0];
173
+ await networkProvider.sender().send({
174
+ to: backend.verifierRegistry,
175
+ value: (0, core_1.toNano)('0.5'),
176
+ body: c,
177
+ });
178
+ };
179
+ exports.verify = verify;
@@ -1,8 +1,12 @@
1
1
  /// <reference types="node" />
2
+ import { SourcesArray } from '@ton-community/func-js';
2
3
  import { Cell } from '@ton/core';
3
4
  export type FuncCompileResult = {
4
5
  lang: 'func';
5
6
  code: Cell;
7
+ targets: string[];
8
+ snapshot: SourcesArray;
9
+ version: string;
6
10
  };
7
11
  export type TactCompileResult = {
8
12
  lang: 'tact';
@@ -36,7 +36,6 @@ const compiler_1 = require("@tact-lang/compiler");
36
36
  const OverwritableVirtualFileSystem_1 = require("./OverwritableVirtualFileSystem");
37
37
  async function getCompilerConfigForContract(name) {
38
38
  var _a;
39
- require('ts-node/register');
40
39
  const mod = await (_a = path_1.default.join(paths_1.WRAPPERS_DIR, name + '.compile.ts'), Promise.resolve().then(() => __importStar(require(_a))));
41
40
  if (typeof mod.compile !== 'object') {
42
41
  throw new Error(`Object 'compile' is missing`);
@@ -47,9 +46,19 @@ async function doCompileFunc(config) {
47
46
  const cr = await (0, func_js_1.compileFunc)(config);
48
47
  if (cr.status === 'error')
49
48
  throw new Error(cr.message);
49
+ let targets = [];
50
+ if (config.targets) {
51
+ targets = config.targets;
52
+ }
53
+ else if (Array.isArray(config.sources)) {
54
+ targets = config.sources.map((s) => s.filename);
55
+ }
50
56
  return {
51
57
  lang: 'func',
52
58
  code: core_1.Cell.fromBase64(cr.codeBoc),
59
+ targets,
60
+ snapshot: cr.snapshot,
61
+ version: (await (0, func_js_1.compilerVersion)()).funcVersion,
53
62
  };
54
63
  }
55
64
  function findTactBoc(fs) {
@@ -1,3 +1,3 @@
1
1
  import { UIProvider } from '../ui/UIProvider';
2
2
  import { NetworkProvider } from './NetworkProvider';
3
- export declare function createNetworkProvider(ui: UIProvider): Promise<NetworkProvider>;
3
+ export declare function createNetworkProvider(ui: UIProvider, allowCustom?: boolean): Promise<NetworkProvider>;
@@ -196,9 +196,10 @@ async function createMnemonicProvider(client, ui) {
196
196
  });
197
197
  }
198
198
  class NetworkProviderBuilder {
199
- constructor(args, ui) {
199
+ constructor(args, ui, allowCustom = true) {
200
200
  this.args = args;
201
201
  this.ui = ui;
202
+ this.allowCustom = allowCustom;
202
203
  }
203
204
  async chooseNetwork() {
204
205
  let network = (0, utils_1.oneOrZeroOf)({
@@ -207,7 +208,11 @@ class NetworkProviderBuilder {
207
208
  custom: this.args['--custom'] !== undefined,
208
209
  });
209
210
  if (!network) {
210
- network = await this.ui.choose('Which network do you want to use?', ['mainnet', 'testnet', 'custom'], (c) => c);
211
+ let nets = ['mainnet', 'testnet'];
212
+ if (this.allowCustom) {
213
+ nets = ['mainnet', 'testnet', 'custom'];
214
+ }
215
+ network = await this.ui.choose('Which network do you want to use?', nets, (c) => c);
211
216
  if (network === 'custom') {
212
217
  const defaultCustomEndpoint = 'http://localhost:8081/';
213
218
  this.args['--custom'] = (await this.ui.input(`Provide a custom API v2 endpoint (default is ${defaultCustomEndpoint})`)).trim();
@@ -216,6 +221,9 @@ class NetworkProviderBuilder {
216
221
  this.args['--custom'] += 'jsonRPC';
217
222
  }
218
223
  }
224
+ if (network === 'custom' && !this.allowCustom) {
225
+ throw new Error('Custom network is not allowed');
226
+ }
219
227
  return network;
220
228
  }
221
229
  chooseExplorer() {
@@ -304,8 +312,8 @@ class NetworkProviderBuilder {
304
312
  return new NetworkProviderImpl(tc, sender, network, explorer, this.ui);
305
313
  }
306
314
  }
307
- async function createNetworkProvider(ui) {
315
+ async function createNetworkProvider(ui, allowCustom = true) {
308
316
  const args = (0, arg_1.default)(argSpec);
309
- return await new NetworkProviderBuilder(args, ui).build();
317
+ return await new NetworkProviderBuilder(args, ui, allowCustom).build();
310
318
  }
311
319
  exports.createNetworkProvider = createNetworkProvider;
@@ -1,5 +1,5 @@
1
1
  {{name}}.spec.ts
2
- import { Blockchain, SandboxContract } from '@ton/sandbox';
2
+ import { Blockchain, SandboxContract, TreasuryContract } from '@ton/sandbox';
3
3
  import { Cell, toNano } from '@ton/core';
4
4
  import { {{name}} } from '../wrappers/{{name}}';
5
5
  import '@ton/test-utils';
@@ -1,5 +1,5 @@
1
1
  {{name}}.spec.ts
2
- import { Blockchain, SandboxContract } from '@ton/sandbox';
2
+ import { Blockchain, SandboxContract, TreasuryContract } from '@ton/sandbox';
3
3
  import { Cell, toNano } from '@ton/core';
4
4
  import { {{name}} } from '../wrappers/{{name}}';
5
5
  import '@ton/test-utils';
@@ -1,5 +1,5 @@
1
1
  {{name}}.spec.ts
2
- import { Blockchain, SandboxContract } from '@ton/sandbox';
2
+ import { Blockchain, SandboxContract, TreasuryContract } from '@ton/sandbox';
3
3
  import { toNano } from '@ton/core';
4
4
  import { {{name}} } from '../wrappers/{{name}}';
5
5
  import '@ton/test-utils';
@@ -1,5 +1,5 @@
1
1
  {{name}}.spec.ts
2
- import { Blockchain, SandboxContract } from '@ton/sandbox';
2
+ import { Blockchain, SandboxContract, TreasuryContract } from '@ton/sandbox';
3
3
  import { toNano } from '@ton/core';
4
4
  import { {{name}} } from '../wrappers/{{name}}';
5
5
  import '@ton/test-utils';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ton/blueprint",
3
- "version": "0.13.0",
3
+ "version": "0.14.1",
4
4
  "description": "Framework for development of TON smart contracts",
5
5
  "main": "dist/index.js",
6
6
  "bin": "./dist/cli/cli.js",