@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.
- package/CHANGELOG.md +6 -0
- package/README.md +13 -8
- package/dist/cli/constants.js +10 -2
- package/dist/cli/create.d.ts +0 -4
- package/dist/cli/create.js +3 -21
- package/dist/compile/CompilerConfig.d.ts +8 -1
- package/dist/compile/compile.d.ts +13 -3
- package/dist/compile/compile.js +26 -0
- package/dist/index.d.ts +1 -1
- package/dist/templates/func/common/contracts/imports/stdlib.fc.template +885 -0
- package/dist/templates/func/not-separated-common/contracts/imports/stdlib.fc.template +885 -0
- package/dist/templates/tolk/common/compilables/compile.ts.template +9 -0
- package/dist/templates/tolk/counter/contracts/contract.tolk.template +71 -0
- package/dist/templates/tolk/counter/scripts/deploy.ts.template +22 -0
- package/dist/templates/tolk/counter/scripts/increment.ts.template +38 -0
- package/dist/templates/tolk/counter/tests/spec.ts.template +82 -0
- package/dist/templates/tolk/counter/wrappers/wrapper.ts.template +67 -0
- package/dist/templates/tolk/empty/contracts/contract.tolk.template +4 -0
- package/dist/templates/tolk/empty/scripts/deploy.ts.template +14 -0
- package/dist/templates/tolk/empty/tests/spec.ts.template +40 -0
- package/dist/templates/tolk/empty/wrappers/wrapper.ts.template +30 -0
- package/dist/templates/tolk/not-separated-common/wrappers/compile.ts.template +9 -0
- package/package.json +4 -2
|
@@ -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,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.
|
|
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": "
|
|
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",
|