Skip to content

feat(sharding): put in agreement with styleguide and port code samples to Tolk#2131

Draft
aigerimu wants to merge 2 commits intomainfrom
contract-sharding-polishing
Draft

feat(sharding): put in agreement with styleguide and port code samples to Tolk#2131
aigerimu wants to merge 2 commits intomainfrom
contract-sharding-polishing

Conversation

@aigerimu
Copy link
Copy Markdown
Contributor

closes #500

@aigerimu aigerimu requested a review from a team as a code owner April 24, 2026 20:16
@aigerimu aigerimu marked this pull request as draft April 24, 2026 20:16
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 51a40f60-d2ba-430a-a06b-4c24e789bcf1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch contract-sharding-polishing

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

This comment has been minimized.

@mintlify
Copy link
Copy Markdown

mintlify Bot commented Apr 24, 2026

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
mintlify-ton-docs 🟢 Ready View Preview Apr 24, 2026, 8:19 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clear docs updates in contract-dev/techniques/contract-sharding.mdx: please apply the couple of inline suggestions to keep terminology casing and code formatting consistent.

Comment on lines +22 to +28
## NFT and jetton examples

Consider NFTs: the collection acts as the parent contract, and each NFT item is a child contract. The key in this case is the item index, and only the collection can set the initial owner.

For jettons, the parent contract is the minter, and the child contracts are user wallets. The key is the user's smart contract address, and the value is the user's token balance.

Both patterns follow the same principle: each key maps to a separate contract. In jetton protocols, there is a unique contract per user, while in NFT collections, there is one contract per item (by index) that is shared across all users.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[HIGH] Casing of “jetton” at sentence start

Suggested change
## NFT and jetton examples
Consider NFTs: the collection acts as the parent contract, and each NFT item is a child contract. The key in this case is the item index, and only the collection can set the initial owner.
For jettons, the parent contract is the minter, and the child contracts are user wallets. The key is the user's smart contract address, and the value is the user's token balance.
Both patterns follow the same principle: each key maps to a separate contract. In jetton protocols, there is a unique contract per user, while in NFT collections, there is one contract per item (by index) that is shared across all users.

Please leave a reaction 👍/👎 to this suggestion to improve future reviews for everyone!

Comment on lines +12 to +14
## Child contract address by key

The contract address depends on the initial data provided in [`StateInit`](/foundations/messages/deploy). To ensure that a child contract can be accessed using only the key, the initial data includes the key but does not include the associated value. As a result, the address of the child contract can be determined from the key alone.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[HIGH] Missing code font for StateInit

The identifier StateInit is referenced inside link text on this page without being consistently treated as a code-styled identifier across revisions. The style guide requires code identifiers to appear in code font with exact casing so they are visually distinct from surrounding prose and easy to scan. Using plain text for an identifier like StateInit reduces clarity, especially for readers skimming for specific technical terms. Keeping identifier formatting consistent across the docs also improves searchability and reduces the chance of misreading the term.

Suggested change
## Child contract address by key
The contract address depends on the initial data provided in [`StateInit`](/foundations/messages/deploy). To ensure that a child contract can be accessed using only the key, the initial data includes the key but does not include the associated value. As a result, the address of the child contract can be determined from the key alone.
## Child contract address by key
The contract address depends on the initial data provided in [`StateInit`](/foundations/messages/deploy). To ensure that a child contract can be accessed using only the key, the initial data includes the key but does not include the associated value. As a result, the address of the child contract can be determined from the key alone.

Please leave a reaction 👍/👎 to this suggestion to improve future reviews for everyone!

seqno: uint64,
todoChildCode: cell,
): AutoDeployAddress {
val childStorage: TodoChildStorage = {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the child StateInit only includes seqno, so two parents using the same child code and sequence number can derive the same child address. it also contradicts the caution above, because the child has no stored parent address to verify

to fix it is to include the parent address in the child's initial storage, pass it into address derivation, and verify it in the child handler

val msg = lazy TodoParentMessage.fromSlice(in.body);

match (msg) {
DeployAnother => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DeployAnother is accepted from any sender and spends ton("0.1") from the parent. If copied as-is, anyone can repeatedly trigger child deployments and drain the parent balance

either mark this handler as demo-only, or add authorization. For example:

 struct TodoParentStorage {
+    // Initialize this field in the parent's StateInit during deployment.
+    adminAddress: address
     numChildren: uint64 = 0
     // Parent must know the child contract code to deploy new instances.
     todoChildCode: cell
 }
         DeployAnother => {
             var storage = lazy TodoParentStorage.load();
+            assert (in.senderAddress == storage.adminAddress) throw 0xFFFF;
             storage.numChildren += 1;


// Send a message to the auto-calculated address and attach
// the child code and initial data so the child is deployed.
val deployMsg = createMessage({
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the createMessage call omits explicit bounce behavior. The Tolk message docs say createMessage should specify bounce behavior, and the full examples in this repo do so

https://docs.ton.org/languages/tolk/features/message-handling#bouncemode-in-createmessage

I'd add an explicit mode, for example:

             val deployMsg = createMessage({
+                bounce: BounceMode.Only256BitsOfBody,
                 dest: calcDeployedTodoChild(
                     storage.numChildren,
                     storage.todoChildCode,
                 ),

Some protocols need to store a lot of information in contracts, for example, token contracts with many users. In TON, there is a limit on how much can be stored in a single contract. The solution is to split the data across multiple contracts.

In such protocols, there is a child contract that initially contains the information identified by a key. In some protocols, it is important to know the Parent contract, which acts as the information manager.
Each such contract, referred to as a child contract, is associated with a key that allows direct access to the required contract and its data. Some protocols also introduce a _parent_ contract that coordinates child contracts.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"a key that allows direct access to the required contract and its data" repeats "contract" a bit awkwardly. it makes sense to read cleaner as something like: a key that uniquely determines its address

self.numChildren = 0;
}
fun onInternalMessage(in: InMessage) {
val msg = lazy TodoChildMessage.fromSlice(in.body);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, but I think the else branch in the match below may not be reachable as written. Since TodoChildMessage = Identify is a single-variant union, lazy TodoChildMessage.fromSlice(in.body) should throw on any body that doesn't parse as Identify - including an empty body, since the opcode parse fails first

If the intent is to ignore empty top-up messages, the empty-body check probably needs to happen before the lazy fromSlice call for instance

 fun onInternalMessage(in: InMessage) {
+    if (in.body.isEmpty()) {
+        return;
+    }
     val msg = lazy TodoChildMessage.fromSlice(in.body);

it definitely worths double-checking against actual Tolk semantics

match (msg) {
DeployAnother => {
var storage = lazy TodoParentStorage.load();
storage.numChildren += 1;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor point: numChildren increments before the send, and the send uses SEND_MODE_IGNORE_ERRORS. If the action fails to queue, the counter still bumps, so the next deploy uses seqno = N+2 and leaves a gap

absolutely not a bug if contiguous numbering doesn't matter, but might be worth a sentence in the prose or a short comment in the sample so readers don't assume sequence numbers are guaranteed dense :)

@github-actions
Copy link
Copy Markdown
Contributor

To fix the formatting issues:

  1. Install necessary dependencises: npm ci
  2. Then, run this command:
    npm run fmt:some -- contract-dev/techniques/contract-sharding.mdx

Alternatively, a maintainer can comment /fmt in this PR to auto-apply fixes in a new commit from the bot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Contract dev > Contract sharding] Add more examples and code with different scale & scope

2 participants