@ton/blueprint 0.24.0 → 0.25.0-beta.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.
@@ -0,0 +1,9 @@
1
+ {{name}}.compile.ts
2
+ import { CompilerConfig } from '@ton/blueprint';
3
+
4
+ export const compile: CompilerConfig = {
5
+ lang: 'tolk',
6
+ entrypoint: '{{contractPath}}',
7
+ withStackComments: true, // Fift output will contain comments, if you wish to debug its output
8
+ experimentalOptions: '', // you can pass experimental compiler options here
9
+ };
@@ -0,0 +1,71 @@
1
+ {{snakeName}}.tolk
2
+ // simple counter contract in Tolk language
3
+
4
+ const OP_INCREASE = 0x7e8764ef; // arbitrary 32-bit number, equal to OP_INCREASE in wrappers/CounterContract.ts
5
+
6
+ // storage variables
7
+
8
+ // id is required to be able to create different instances of counters
9
+ // since addresses in TON depend on the initial state of the contract
10
+ global ctxID: int;
11
+ global ctxCounter: int;
12
+
13
+ // loadData populates storage variables from persistent storage
14
+ fun loadData() {
15
+ var ds = getContractData().beginParse();
16
+
17
+ ctxID = ds.loadUint(32);
18
+ ctxCounter = ds.loadUint(32);
19
+
20
+ ds.assertEndOfSlice();
21
+ }
22
+
23
+ // saveData stores storage variables as a cell into persistent storage
24
+ fun saveData() {
25
+ setContractData(
26
+ beginCell()
27
+ .storeUint(ctxID, 32)
28
+ .storeUint(ctxCounter, 32)
29
+ .endCell()
30
+ );
31
+ }
32
+
33
+ // onInternalMessage is the main entrypoint; it's called when a contract receives an internal message from other contracts
34
+ fun onInternalMessage(myBalance: int, msgValue: int, msgFull: cell, msgBody: slice) {
35
+ if (msgBody.isEndOfSlice()) { // ignore all empty messages
36
+ return;
37
+ }
38
+
39
+ var cs: slice = msgFull.beginParse();
40
+ val flags = cs.loadMessageFlags();
41
+ if (isMessageBounced(flags)) { // ignore all bounced messages
42
+ return;
43
+ }
44
+
45
+ loadData(); // here we populate the storage variables
46
+
47
+ val op = msgBody.loadMessageOp(); // by convention, the first 32 bits of incoming message is the op
48
+ val queryID = msgBody.loadMessageQueryId(); // also by convention, the next 64 bits contain the "query id", although this is not always the case
49
+
50
+ if (op == OP_INCREASE) {
51
+ val increaseBy = msgBody.loadUint(32);
52
+ ctxCounter += increaseBy;
53
+ saveData();
54
+ return;
55
+ }
56
+
57
+ throw 0xffff; // if the message contains an op that is not known to this contract, we throw
58
+ }
59
+
60
+ // get methods are a means to conveniently read contract data using, for example, HTTP APIs
61
+ // note that unlike in many other smart contract VMs, get methods cannot be called by other contracts
62
+
63
+ get currentCounter(): int {
64
+ loadData();
65
+ return ctxCounter;
66
+ }
67
+
68
+ get initialId(): int {
69
+ loadData();
70
+ return ctxID;
71
+ }
@@ -0,0 +1,22 @@
1
+ deploy{{name}}.ts
2
+ import { toNano } from '@ton/core';
3
+ import { {{name}} } from '../wrappers/{{name}}';
4
+ import { compile, NetworkProvider } from '@ton/blueprint';
5
+
6
+ export async function run(provider: NetworkProvider) {
7
+ const {{loweredName}} = provider.open(
8
+ {{name}}.createFromConfig(
9
+ {
10
+ id: Math.floor(Math.random() * 10000),
11
+ counter: 0,
12
+ },
13
+ await compile('{{name}}')
14
+ )
15
+ );
16
+
17
+ await {{loweredName}}.sendDeploy(provider.sender(), toNano('0.05'));
18
+
19
+ await provider.waitForDeploy({{loweredName}}.address);
20
+
21
+ console.log('ID', await {{loweredName}}.getID());
22
+ }
@@ -0,0 +1,38 @@
1
+ increment{{name}}.ts
2
+ import { Address, toNano } from '@ton/core';
3
+ import { {{name}} } from '../wrappers/{{name}}';
4
+ import { NetworkProvider, sleep } from '@ton/blueprint';
5
+
6
+ export async function run(provider: NetworkProvider, args: string[]) {
7
+ const ui = provider.ui();
8
+
9
+ const address = Address.parse(args.length > 0 ? args[0] : await ui.input('{{name}} address'));
10
+
11
+ if (!(await provider.isContractDeployed(address))) {
12
+ ui.write(`Error: Contract at address ${address} is not deployed!`);
13
+ return;
14
+ }
15
+
16
+ const {{loweredName}} = provider.open({{name}}.createFromAddress(address));
17
+
18
+ const counterBefore = await {{loweredName}}.getCounter();
19
+
20
+ await {{loweredName}}.sendIncrease(provider.sender(), {
21
+ increaseBy: 1,
22
+ value: toNano('0.05'),
23
+ });
24
+
25
+ ui.write('Waiting for counter to increase...');
26
+
27
+ let counterAfter = await {{loweredName}}.getCounter();
28
+ let attempt = 1;
29
+ while (counterAfter === counterBefore) {
30
+ ui.setActionPrompt(`Attempt ${attempt}`);
31
+ await sleep(2000);
32
+ counterAfter = await {{loweredName}}.getCounter();
33
+ attempt++;
34
+ }
35
+
36
+ ui.clearActionPrompt();
37
+ ui.write('Counter increased successfully!');
38
+ }
@@ -0,0 +1,82 @@
1
+ {{name}}.spec.ts
2
+ import { Blockchain, SandboxContract, TreasuryContract } from '@ton/sandbox';
3
+ import { Cell, toNano } from '@ton/core';
4
+ import { {{name}} } from '../wrappers/{{name}}';
5
+ import '@ton/test-utils';
6
+ import { compile } from '@ton/blueprint';
7
+
8
+ describe('{{name}}', () => {
9
+ let code: Cell;
10
+
11
+ beforeAll(async () => {
12
+ code = await compile('{{name}}');
13
+ });
14
+
15
+ let blockchain: Blockchain;
16
+ let deployer: SandboxContract<TreasuryContract>;
17
+ let {{loweredName}}: SandboxContract<{{name}}>;
18
+
19
+ beforeEach(async () => {
20
+ blockchain = await Blockchain.create();
21
+
22
+ {{loweredName}} = blockchain.openContract(
23
+ {{name}}.createFromConfig(
24
+ {
25
+ id: 0,
26
+ counter: 0,
27
+ },
28
+ code
29
+ )
30
+ );
31
+
32
+ deployer = await blockchain.treasury('deployer');
33
+
34
+ const deployResult = await {{loweredName}}.sendDeploy(deployer.getSender(), toNano('0.05'));
35
+
36
+ expect(deployResult.transactions).toHaveTransaction({
37
+ from: deployer.address,
38
+ to: {{loweredName}}.address,
39
+ deploy: true,
40
+ success: true,
41
+ });
42
+ });
43
+
44
+ it('should deploy', async () => {
45
+ // the check is done inside beforeEach
46
+ // blockchain and {{loweredName}} are ready to use
47
+ });
48
+
49
+ it('should increase counter', async () => {
50
+ const increaseTimes = 3;
51
+ for (let i = 0; i < increaseTimes; i++) {
52
+ console.log(`increase ${i + 1}/${increaseTimes}`);
53
+
54
+ const increaser = await blockchain.treasury('increaser' + i);
55
+
56
+ const counterBefore = await {{loweredName}}.getCounter();
57
+
58
+ console.log('counter before increasing', counterBefore);
59
+
60
+ const increaseBy = Math.floor(Math.random() * 100);
61
+
62
+ console.log('increasing by', increaseBy);
63
+
64
+ const increaseResult = await {{loweredName}}.sendIncrease(increaser.getSender(), {
65
+ increaseBy,
66
+ value: toNano('0.05'),
67
+ });
68
+
69
+ expect(increaseResult.transactions).toHaveTransaction({
70
+ from: increaser.address,
71
+ to: {{loweredName}}.address,
72
+ success: true,
73
+ });
74
+
75
+ const counterAfter = await {{loweredName}}.getCounter();
76
+
77
+ console.log('counter after increasing', counterAfter);
78
+
79
+ expect(counterAfter).toBe(counterBefore + increaseBy);
80
+ }
81
+ });
82
+ });
@@ -0,0 +1,67 @@
1
+ {{name}}.ts
2
+ import { Address, beginCell, Cell, Contract, contractAddress, ContractProvider, Sender, SendMode } from '@ton/core';
3
+
4
+ export type {{name}}Config = {
5
+ id: number;
6
+ counter: number;
7
+ };
8
+
9
+ export function {{loweredName}}ConfigToCell(config: {{name}}Config): Cell {
10
+ return beginCell().storeUint(config.id, 32).storeUint(config.counter, 32).endCell();
11
+ }
12
+
13
+ export const Opcodes = {
14
+ OP_INCREASE: 0x7e8764ef,
15
+ };
16
+
17
+ export class {{name}} implements Contract {
18
+ constructor(readonly address: Address, readonly init?: { code: Cell; data: Cell }) {}
19
+
20
+ static createFromAddress(address: Address) {
21
+ return new {{name}}(address);
22
+ }
23
+
24
+ static createFromConfig(config: {{name}}Config, code: Cell, workchain = 0) {
25
+ const data = {{loweredName}}ConfigToCell(config);
26
+ const init = { code, data };
27
+ return new {{name}}(contractAddress(workchain, init), init);
28
+ }
29
+
30
+ async sendDeploy(provider: ContractProvider, via: Sender, value: bigint) {
31
+ await provider.internal(via, {
32
+ value,
33
+ sendMode: SendMode.PAY_GAS_SEPARATELY,
34
+ body: beginCell().endCell(),
35
+ });
36
+ }
37
+
38
+ async sendIncrease(
39
+ provider: ContractProvider,
40
+ via: Sender,
41
+ opts: {
42
+ increaseBy: number;
43
+ value: bigint;
44
+ queryID?: number;
45
+ }
46
+ ) {
47
+ await provider.internal(via, {
48
+ value: opts.value,
49
+ sendMode: SendMode.PAY_GAS_SEPARATELY,
50
+ body: beginCell()
51
+ .storeUint(Opcodes.OP_INCREASE, 32)
52
+ .storeUint(opts.queryID ?? 0, 64)
53
+ .storeUint(opts.increaseBy, 32)
54
+ .endCell(),
55
+ });
56
+ }
57
+
58
+ async getCounter(provider: ContractProvider) {
59
+ const result = await provider.get('currentCounter', []);
60
+ return result.stack.readNumber();
61
+ }
62
+
63
+ async getID(provider: ContractProvider) {
64
+ const result = await provider.get('initialId', []);
65
+ return result.stack.readNumber();
66
+ }
67
+ }
@@ -0,0 +1,4 @@
1
+ {{snakeName}}.tolk
2
+ fun onInternalMessage(myBalance: int, msgValue: int, msgFull: cell, msgBody: slice) {
3
+
4
+ }
@@ -0,0 +1,14 @@
1
+ deploy{{name}}.ts
2
+ import { toNano } from '@ton/core';
3
+ import { {{name}} } from '../wrappers/{{name}}';
4
+ import { compile, NetworkProvider } from '@ton/blueprint';
5
+
6
+ export async function run(provider: NetworkProvider) {
7
+ const {{loweredName}} = provider.open({{name}}.createFromConfig({}, await compile('{{name}}')));
8
+
9
+ await {{loweredName}}.sendDeploy(provider.sender(), toNano('0.05'));
10
+
11
+ await provider.waitForDeploy({{loweredName}}.address);
12
+
13
+ // run methods on `{{loweredName}}`
14
+ }
@@ -0,0 +1,40 @@
1
+ {{name}}.spec.ts
2
+ import { Blockchain, SandboxContract, TreasuryContract } from '@ton/sandbox';
3
+ import { Cell, toNano } from '@ton/core';
4
+ import { {{name}} } from '../wrappers/{{name}}';
5
+ import '@ton/test-utils';
6
+ import { compile } from '@ton/blueprint';
7
+
8
+ describe('{{name}}', () => {
9
+ let code: Cell;
10
+
11
+ beforeAll(async () => {
12
+ code = await compile('{{name}}');
13
+ });
14
+
15
+ let blockchain: Blockchain;
16
+ let deployer: SandboxContract<TreasuryContract>;
17
+ let {{loweredName}}: SandboxContract<{{name}}>;
18
+
19
+ beforeEach(async () => {
20
+ blockchain = await Blockchain.create();
21
+
22
+ {{loweredName}} = blockchain.openContract({{name}}.createFromConfig({}, code));
23
+
24
+ deployer = await blockchain.treasury('deployer');
25
+
26
+ const deployResult = await {{loweredName}}.sendDeploy(deployer.getSender(), toNano('0.05'));
27
+
28
+ expect(deployResult.transactions).toHaveTransaction({
29
+ from: deployer.address,
30
+ to: {{loweredName}}.address,
31
+ deploy: true,
32
+ success: true,
33
+ });
34
+ });
35
+
36
+ it('should deploy', async () => {
37
+ // the check is done inside beforeEach
38
+ // blockchain and {{loweredName}} are ready to use
39
+ });
40
+ });
@@ -0,0 +1,30 @@
1
+ {{name}}.ts
2
+ import { Address, beginCell, Cell, Contract, contractAddress, ContractProvider, Sender, SendMode } from '@ton/core';
3
+
4
+ export type {{name}}Config = {};
5
+
6
+ export function {{loweredName}}ConfigToCell(config: {{name}}Config): Cell {
7
+ return beginCell().endCell();
8
+ }
9
+
10
+ export class {{name}} implements Contract {
11
+ constructor(readonly address: Address, readonly init?: { code: Cell; data: Cell }) {}
12
+
13
+ static createFromAddress(address: Address) {
14
+ return new {{name}}(address);
15
+ }
16
+
17
+ static createFromConfig(config: {{name}}Config, code: Cell, workchain = 0) {
18
+ const data = {{loweredName}}ConfigToCell(config);
19
+ const init = { code, data };
20
+ return new {{name}}(contractAddress(workchain, init), init);
21
+ }
22
+
23
+ async sendDeploy(provider: ContractProvider, via: Sender, value: bigint) {
24
+ await provider.internal(via, {
25
+ value,
26
+ sendMode: SendMode.PAY_GAS_SEPARATELY,
27
+ body: beginCell().endCell(),
28
+ });
29
+ }
30
+ }
@@ -0,0 +1,9 @@
1
+ {{name}}.compile.ts
2
+ import { CompilerConfig } from '@ton/blueprint';
3
+
4
+ export const compile: CompilerConfig = {
5
+ lang: 'tolk',
6
+ entrypoint: '{{contractPath}}',
7
+ withStackComments: true, // Fift output will contain comments, if you wish to debug its output
8
+ experimentalOptions: '', // you can pass experimental compiler options here
9
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ton/blueprint",
3
- "version": "0.24.0",
3
+ "version": "0.25.0-beta.1",
4
4
  "description": "Framework for development of TON smart contracts",
5
5
  "main": "dist/index.js",
6
6
  "bin": "./dist/cli/cli.js",
@@ -35,9 +35,11 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@tact-lang/compiler": "^1.4.0",
38
- "@ton-community/func-js": "^0.7.0",
38
+ "@ton-community/func-js": "0.8.0-beta.1",
39
+ "@ton/tolk-js": "^0.6.0",
39
40
  "@tonconnect/sdk": "^2.2.0",
40
41
  "arg": "^5.0.2",
42
+ "axios": "^1.7.7",
41
43
  "chalk": "^4.1.0",
42
44
  "dotenv": "^16.1.4",
43
45
  "inquirer": "^8.2.5",