Skip to content

Commit

Permalink
feat(docs): compilation report (#1309)
Browse files Browse the repository at this point in the history
* feat(docs): compilation report

* fix: correction of the `queryId` description in Jetton
  • Loading branch information
novusnota authored Jan 16, 2025
1 parent 1756916 commit f1da93a
Show file tree
Hide file tree
Showing 11 changed files with 187 additions and 39 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added onchain metadata creation for NFTs and Jettons to the cookbook: PR [#1236](https://github.com/tact-lang/tact/pull/1236)
- Document that identifiers cannot start with `__gen` or `__tact`, and cannot contain Unicode characters apart from the small subset `a-zA-Z0-9_`: PR [#1312](https://github.com/tact-lang/tact/pull/1312)
- Added signatures for map methods, such as `.get()`, `.exists()`, `.set()`, `.replace()`, `.replaceGet()`, `.del()`, `.isEmpty()`, `.deepEquals()`, `.asCell()`: PR [#1352](https://github.com/tact-lang/tact/pull/1352)
- Added a compilation-related page with the description of the compilation report: PR [#1309](https://github.com/tact-lang/tact/pull/1309)

### Release contributors

Expand Down
5 changes: 3 additions & 2 deletions docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,11 @@ export default defineConfig({
label: 'Going places',
translations: { 'zh-CN': '前往各地' },
attrs: { class: 'sidebar-separator' },
link: 'book/deploy#',
link: 'book/compile#',
},
{ slug: 'book/deploy' },
{ slug: 'book/compile' },
{ slug: 'book/debug' },
{ slug: 'book/deploy' },
{ slug: 'book/upgrades' },
{ slug: 'book/import' },
{ slug: 'book/config' },
Expand Down
24 changes: 16 additions & 8 deletions docs/src/content/docs/book/cells.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -178,14 +178,22 @@ contract SerializationExample {

There, `^cell`, `^builder` and `^slice` in [TL-B][tlb] syntax mean the reference to [`Cell{:tact}`](#cells), [`Builder{:tact}`](#builders) and [`Slice{:tact}`](#slices) values respectively, while the `remainder<…>` of `cell`, `builder` or `slice` tells that the given value would be stored as a `Slice{:tact}` directly and not as a reference.

Now, to give a real-world example, imagine that you need to notice and react to inbound [jetton][jetton] transfers in your smart contract. The appropriate [Message][message] structure for doing so would look something like this:
Now, to give a real-world example, imagine that you need to notice and react to inbound [Jetton][jetton] transfers in your smart contract. The appropriate [Message][message] structure for doing so would look like this:

```tact /remaining/
message(0x7362d09c) JettonTransferNotification {
queryId: Int as uint64; // arbitrary request number to prevent replay attacks
amount: Int as coins; // amount of jettons transferred
sender: Address; // address of the sender of the jettons
forwardPayload: Slice as remaining; // optional custom payload
// Unique identifier used to trace transactions across multiple contracts
// Defaults to 0, which means we don't mark messages to trace their chains
queryId: Int as uint64 = 0;
// Amount of Jettons transferred
amount: Int as coins;
// Address of the sender of the Jettons
sender: Address;
// Optional custom payload
forwardPayload: Slice as remaining;
}
```

Expand All @@ -197,9 +205,9 @@ receive(msg: JettonTransferNotification) {
}
```

Upon receiving a [jetton][jetton] transfer notification message, its cell body is converted into a [`Slice{:tact}`](#slices) and then parsed as a `JettonTransferNotification{:tact}` [Message][message]. At the end of this process, the `forwardPayload` will have all the remaining data of the original message cell.
Upon receiving a [Jetton][jetton] transfer notification message, its cell body is converted into a [`Slice{:tact}`](#slices) and then parsed as a `JettonTransferNotification{:tact}` [Message][message]. At the end of this process, the `forwardPayload` will have all the remaining data of the original message cell.

Here, it's not possible to violate the [jetton][jetton] standard by placing the `forwardPayload: Slice as remaining` field in any other position in the `JettonTransferNotification{:tact}` [Message][message]. That's because Tact prohibits usage of `as remaining{:tact}` for any but the last field of the [Structs][struct] and [Messages][message] to prevent misuse of the contract storage and reduce gas consumption.
Here, it's not possible to violate the [Jetton][jetton] standard by placing the `forwardPayload: Slice as remaining` field in any other position in the `JettonTransferNotification{:tact}` [Message][message]. That's because Tact prohibits usage of `as remaining{:tact}` for any but the last field of the [Structs][struct] and [Messages][message] to prevent misuse of the contract storage and reduce gas consumption.

:::note

Expand Down Expand Up @@ -436,10 +444,10 @@ let areSlicesNotEqual = aSlice.hash() != bSlice.hash(); // false
[struct]: /book/structs-and-messages#structs
[message]: /book/structs-and-messages#messages
[recv]: /book/contracts#receiver-functions
[jetton]: /cookbook/jettons

[tvm]: https://docs.ton.org/learn/tvm-instructions/tvm-overview
[tlb]: https://docs.ton.org/develop/data-formats/tl-b-language
[jetton]: https://docs.ton.org/develop/dapps/asset-processing/jettons
[sha-2]: https://en.wikipedia.org/wiki/SHA-2#Hash_standard

[quadtree]: https://en.wikipedia.org/wiki/Quadtree
Expand Down
125 changes: 125 additions & 0 deletions docs/src/content/docs/book/compile.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
---
title: Compilation
description: "How to compile Tact smart contracts, as well as details on the build artifacts that are provided."
prev:
link: /book/message-mode
label: Message mode
---

import { Badge } from '@astrojs/starlight/components';

:::note

This page is still being written as per [#1136](https://github.com/tact-lang/tact/issues/1136).

:::

The Tact compiler can produce various outputs, ranging from the [BoC](/book/cells#cells-boc) of the compiled contract to various supplementary files, such as the [compilation report](#report) or the [contract package](/ref/evolution/otp-006), which is the JSON file with the `.pkg` extension that one can use to [verify the smart contract's origin](https://verifier.ton.org).

With the proper [configuration settings](/book/config), you can customize the behavior of the compiler to skip the generation of some or all the [build artifacts](#artifacts), and even control the addition or exclusion of [getters for supported interfaces](/book/contracts#interfaces).

Since the Tact compiler is a command-line program, some of the configuration settings can be set directly. When inside a folder with a Tact-based project, such as one created using the [Blueprint](https://github.com/ton-org/blueprint) or from the [tact-template](https://github.com/tact-lang/tact-template), refer to the `npx tact --help{:shell}` command for further instructions.

## Build artifacts {#artifacts}

A number of build artifacts can be produced per compilation of each contract. Some of the artifacts can be omitted using [configuration settings](/book/config).

The location of the artifacts depends on the [`output`](/book/config#projects-output) field of the [`tact.config.json`](/book/config). In [Blueprint][bp]-based projects, `output` is not used and all generated files are always placed in `build/ProjectName/`.

### Compilation report, `.md` {#report}

Every markdown compilation report first features the name of the contract that it was prepared for and then the byte size of the contract compiled to [BoC](/book/cells#cells-boc).

The following sub-headings describe the respective sections of the `.md` report.

#### Structures {#structures}

<Badge text="Written as '# Types' prior to Tact 1.6" variant="tip" size="medium"/><p/>

The first section introduces the present structures, i.e. some of the [composite types](/book/types#composite-types), [Structs and Messages](/book/structs-and-messages) that are declared or imported directly in the contract code, as well as those exposed from the Core standard library.

Along with the number of structures present, each of the [Structs][struct] and [Messages][message] are described with their respective signatures and [TL-B schemas][tlb], which include the [message opcodes](/book/structs-and-messages#message-opcodes).

For example:

```md
Total structures: 10

### StateInit
TL-B: `_ code:^cell data:^cell = StateInit`
Signature: `StateInit{code:^cell,data:^cell}`

### Deploy
TL-B: `deploy#946a98b6 queryId:uint64 = Deploy`
Signature: `Deploy{queryId:uint64}`

...etc.
```

[TL-B schema][tlb] of each [Message][message] contains its opcode, which is handy for looking up the auto-generated opcodes as well as confirming the [manually provided ones](/book/structs-and-messages#message-opcodes). For example, the following [Message][message] declarations:

```tact
message GeneratedOpcode { }
message(0x12345678) ManuallySpecifiedOpcode { }
```

translate to their respective [TL-B schemas][tlb]:

```md
### GeneratedOpcode
TL-B: `generated_opcode#6dfea180 = GeneratedOpcode`
Signature: `GeneratedOpcode{}`

### ManuallySpecifiedOpcode
TL-B: `manually_specified_opcode#12345678 = ManuallySpecifiedOpcode`
Signature: `ManuallySpecifiedOpcode{}`
```

where `6dfea180` and `12345678` specified after the `#` in the [constructor definitions](https://docs.ton.org/v3/documentation/data-formats/tlb/tl-b-language#constructors) are opcodes written in hexadecimal notation, representing $32$-bit unsigned integers. Thus, the automatically generated `6dfea180` opcode of the `GeneratedOpcode{:tact}` [Message][message] represents the decimal value of $1845404032$, and the manually provided `12345678` opcode of the `ManuallySpecifiedOpcode{:tact}` [Message][message] represents the decimal value of $305419896$, and **not** $12345678$ as it might appear.

#### Get methods {#getters}

This section specifies the number of available get methods or [getter functions](/book/functions#getter-functions) and outlines them with their argument names, if any.

For example:

```md
Total get methods: 2

## lshift
Argument: x

## gas
```

There, the `lshift(){:tact}` getter has a single argument `x`, whereas the `gas(){:tact}` getter has no arguments.

#### Exit codes {#exit-codes}

This section lists all default [exit codes](/book/exit-codes), as well as those generated from the error messages of the [`require(){:tact}`](/ref/core-debug#require), along with those messages for your convenience.

For example:

```md
* 2: Stack underflow
* 3: Stack overflow
...etc.
* 135: Code of a contract was not found
* 42933: Hey, I'm the error message of require()
```

There, the [exit codes](/book/exit-codes) in the range from $0$ to $255$ are those reserved by TON Blockchain or Tact compiler, while the exit code of $42933$ is produced by the respective call to the [`require(){:tact}`](/ref/core-debug#require) function.

#### Trait inheritance diagram {#trait-diagram}

This section shows a [Mermaid][mm] diagram of inherited [traits](/book/types#traits), including the [`BaseTrait{:tact}`](/ref/core-base).

#### Contract inheritance diagram {#contract-diagram}

This section shows a [Mermaid][mm] diagram of [contract](/book/contracts) dependencies, i.e. the [traits](/book/types#traits) that it depends upon.

[struct]: /book/structs-and-messages#structs
[message]: /book/structs-and-messages#messages

[tlb]: https://docs.ton.org/develop/data-formats/tl-b-language
[mm]: https://mermaid.js.org/
4 changes: 2 additions & 2 deletions docs/src/content/docs/book/debug.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Debugging Tact contracts
description: "Various ways to reveal problems or bugs in Tact code"
title: Debugging and testing
description: "Various ways to reveal and prevent problems or bugs in Tact code"
---

import { LinkCard, CardGrid, Steps, Tabs, TabItem } from '@astrojs/starlight/components';
Expand Down
3 changes: 0 additions & 3 deletions docs/src/content/docs/book/deploy.mdx
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
---
title: Deployment
description: "Common ways to deploy Tact contracts to testnet or mainnet of TON Blockchain"
prev:
link: /book/message-mode
label: Message mode
---

Tact Deployer is a small library that integrates with [TON Verifier](https://verifier.ton.org) that allows you to deploy your contracts safely using your favorite wallet without needing to manage keys or deploy contracts manually. Tact Deployer also automatically verifies your contract's source code and you can be sure that your compiler is not compromised.
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/book/exit-codes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ The range from $256$ to $65535$ is free for developer-defined exit codes.

## Table of exit codes {#table}

The following table lists exit codes with an origin (where it can occur) and a short description for each. The table doesn't list the exit code of the [`require()`](/ref/core-debug#require), as it generates it depending on the concrete `error` message [String][p].
The following table lists exit codes with an origin (where it can occur) and a short description for each. The table doesn't list the exit code of the [`require()`](/ref/core-debug#require), as it generates it depending on the concrete `error` message [String][p]. To see such exit codes, refer to the [Exit codes section of the compilation report](/book/compile#exit-codes).

Exit code | Origin | Brief description
:------------ | :---------------------------------- | :----------------
Expand Down
6 changes: 5 additions & 1 deletion docs/src/content/docs/book/structs-and-messages.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { Badge } from '@astrojs/starlight/components';

Tact supports a number of [primitive data types](/book/types#primitive-types) that are tailored for smart contract use. However, using individual means of storage often becomes cumbersome, so there are [Structs](#structs) and [Messages](#messages) which allow combining types together.

After a successful compilation, Tact produces a [compilation report](/book/compile), which features all the declared [Structs](#structs) and [Messages](#messages), including those from the Core standard library. See the [Structures section of the compilation report](/book/compile#structures) for details.

:::caution

**Warning**: Currently circular types are **not** possible. This means that Struct/Message **A** can't have a field of a Struct/Message **B** that has a field of the Struct/Message **A**.
Expand Down Expand Up @@ -107,7 +109,9 @@ message Add {

Messages are almost the same thing as [Structs](#structs) with the only difference that Messages have a 32-bit integer header in their serialization containing their unique numeric id, commonly referred to as an _opcode_ (operation code). This allows Messages to be used with [receivers](/book/receive) since the contract can tell different types of messages apart based on this id.

Tact automatically generates those unique ids (opcodes) for every received Message, but this can be manually overwritten:
Tact automatically generates those unique ids (opcodes) for every received Message, which can be observed in the [Structures section of the compilation report](/book/compile#structures).

Besides, opcodes can be overwritten manually:

```tact
// This Message overwrites its unique id (opcode) with 0x7362d09c
Expand Down
16 changes: 12 additions & 4 deletions docs/src/content/docs/cookbook/jettons.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,21 @@ Common examples of working with Jettons.

### Accepting Jetton transfer

Transfer notification message have the following structure.
Transfer notification message have the following structure:

```tact
message(0x7362d09c) JettonTransferNotification {
queryId: Int as uint64;
// Unique identifier used to trace transactions across multiple contracts
// Defaults to 0, which means we don't mark messages to trace their chains
queryId: Int as uint64 = 0;
// Amount of Jettons transferred
amount: Int as coins;
// Address of the sender of the Jettons
sender: Address;
// Optional custom payload
forwardPayload: Slice as remaining;
}
```
Expand Down Expand Up @@ -216,7 +224,7 @@ contract Example with Deployable {
to: self.myJettonWalletAddress,
value: JettonTransferGas,
body: JettonTransfer{
// To prevent replay attacks
// Unique identifier used to trace transactions across multiple contracts
queryId: 42,
// Jetton amount to transfer
amount: msg.amount,
Expand Down Expand Up @@ -311,7 +319,7 @@ contract Example with Deployable {
to: self.myJettonWalletAddress,
value: JettonBurnGas,
body: JettonBurn{
// To prevent replay attacks
// Unique identifier used to trace transactions across multiple contracts
queryId: 42,
// Jetton amount you want to burn
amount: msg.amount,
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/ref/core-common.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ Field | Type | Description
`bounced` | [`Bool{:tact}`][bool] | [Bounced](https://ton.org/docs/learn/overviews/addresses#bounceable-vs-non-bounceable-addresses) flag of the incoming message.
`sender` | [`Address{:tact}`][p] | Internal address of the sender on the TON blockchain.
`value` | [`Int{:tact}`][int] | Amount of [nanoToncoins](/book/integers#nanotoncoin) in a message.
`raw` | [`Slice{:tact}`][slice] | The remainder of the message as a [`Slice{:tact}`][slice]. It follows [internal message layout](https://docs.ton.org/develop/smart-contracts/messages#message-layout) of TON starting from the destination [`Address{:tact}`][p] (`dest:MsgAddressInt ` in [TL-B notation](https://docs.ton.org/develop/data-formats/tl-b-language)).
`raw` | [`Slice{:tact}`][slice] | The remainder of the message as a [`Slice{:tact}`][slice]. It follows [internal message layout](https://docs.ton.org/develop/smart-contracts/messages#message-layout) of TON starting from the destination [`Address{:tact}`][p] (`MsgAddressInt` in [TL-B notation](https://docs.ton.org/develop/data-formats/tl-b-language)).

Usage example:

Expand Down
38 changes: 21 additions & 17 deletions src/generator/writeReport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,45 @@ export function writeReport(ctx: CompilerContext, pkg: PackageFileFormat) {
const w = new Writer();
const abi = JSON.parse(pkg.abi) as ContractABI;
w.write(`
# TACT Compilation Report
# Tact compilation report
Contract: ${pkg.name}
BOC Size: ${Buffer.from(pkg.code, "base64").length} bytes
BoC Size: ${Buffer.from(pkg.code, "base64").length} bytes
`);
w.append();

// Types
w.write(`# Types`);
w.write("Total Types: " + abi.types!.length);
// Structures
w.write(`## Structures (Structs and Messages)`);
w.write("Total structures: " + abi.types!.length);
w.append();
for (const t of abi.types!) {
const tt = getType(
ctx,
t.name.endsWith("$Data") ? t.name.slice(0, -5) : t.name,
);
w.write(`## ${t.name}`);
w.write(`TLB: \`${tt.tlb!}\``);
w.write(`### ${t.name}`);
w.write(`TL-B: \`${tt.tlb!}\``);
w.write(`Signature: \`${tt.signature!}\``);
w.append();
}

// Get methods
w.write(`# Get Methods`);
w.write("Total Get Methods: " + abi.getters!.length);
w.write(`## Get methods`);
w.write("Total get methods: " + abi.getters!.length);
w.append();
for (const t of abi.getters!) {
w.write(`## ${t.name}`);
for (const arg of t.arguments!) {
w.write(`Argument: ${arg.name}`);
if (t.arguments!.length === 0) {
w.write(`No arguments`);
} else {
for (const arg of t.arguments!) {
w.write(`Argument: ${arg.name}`);
}
}
w.append();
}

// Error Codes
w.write(`# Error Codes`);
// Exit codes
w.write(`## Exit codes`);
Object.entries(abi.errors!).forEach(([t, abiError]) => {
w.write(`* ${t}: ${abiError.message}`);
});
Expand All @@ -52,8 +56,8 @@ export function writeReport(ctx: CompilerContext, pkg: PackageFileFormat) {
const t = getType(ctx, pkg.name);
const writtenEdges: Set<string> = new Set();

// Trait Inheritance Diagram
w.write(`# Trait Inheritance Diagram`);
// Trait inheritance diagram
w.write(`## Trait inheritance diagram`);
w.append();
w.write("```mermaid");
w.write("graph TD");
Expand All @@ -75,8 +79,8 @@ export function writeReport(ctx: CompilerContext, pkg: PackageFileFormat) {

writtenEdges.clear();

// Contract Dependency Diagram
w.write(`# Contract Dependency Diagram`);
// Contract dependency diagram
w.write(`## Contract dependency diagram`);
w.append();
w.write("```mermaid");
w.write("graph TD");
Expand Down

0 comments on commit f1da93a

Please sign in to comment.