Skip to content

Commit f8fa439

Browse files
feat(stdlib): add setData() (#3402)
1 parent 65ae172 commit f8fa439

File tree

13 files changed

+293
-184
lines changed

13 files changed

+293
-184
lines changed

dev-docs/CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ The documentation changelog is kept separately: [CHANGELOG-DOCS](./CHANGELOG-DOC
1313

1414
- Reordered arguments of the `__tact_store_address_opt` function to optimize gas consumption: PR [#3333](https://github.com/tact-lang/tact/pull/3333)
1515

16+
### Standard Library
17+
18+
- Added `setData()` function: PR [#3402](https://github.com/tact-lang/tact/pull/3402)
19+
20+
### Release contributors
21+
22+
- [skywardboundd](https://github.com/skywardboundd)
23+
- [Novus Nota](https://github.com/novusnota)
24+
1625
## [1.6.13] - 2025-05-29
1726

1827
### Language features

docs/src/content/docs/cookbook/single-communication.mdx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,6 @@ contract Sample(
158158
// which forwards the remaining value back to the sender
159159
receive() { cashback(sender()) }
160160
}
161-
162-
asm fun setData(data: Cell) { c4 POP }
163161
```
164162

165163
:::tip[Hey there!]

docs/src/content/docs/cookbook/upgrades.mdx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,6 @@ trait Upgradable with Ownable {
8484
8585
/// Change of code will be applied by the end of the current transaction.
8686
asm fun setCode(code: Cell) { SETCODE }
87-
88-
/// Change of data is immediate.
89-
asm fun setData(data: Cell) { c4 POP }
9087
```
9188

9289
## Time-locked upgrade
@@ -211,6 +208,4 @@ trait Upgradable with Ownable {
211208
}
212209
213210
asm fun setCode(code: Cell) { SETCODE }
214-
215-
asm fun setData(data: Cell) { c4 POP }
216211
```

docs/src/content/docs/ref/core-contextstate.mdx

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,55 @@ nativeReserve(ton("0.1"), ReserveExact | ReserveBounceIfActionFail);
362362
// amount of nanoToncoins to reserve
363363
```
364364

365+
### setData
366+
367+
<Badge text="Available since Tact 1.7" variant="tip" size="medium"/>
368+
<Badge text="DANGEROUS" title="Applies irreversible modifications to the contract — use only when you know what you are doing!" variant="danger" size="medium"/><p/>
369+
370+
```tact
371+
fun setData(data: Cell);
372+
```
373+
374+
Replaces the current contract's state data [`Cell{:tact}`][cell] with the new `data`. It is useful only in exceptional cases, such as contract upgrades, data migrations, or when processing external messages with a catch-all [`Slice{:tact}`][slice] receiver for maximum efficiency. Otherwise, do **not** use this function, as it immediately and permanently overrides the state with no ability to recover, which can result in the loss of funds and partial or full corruption of the contract's data.
375+
376+
:::caution
377+
378+
When using this function, make sure that all logical code branches within your receiver end with a call to the [`throw(0){:tact}`](/ref/core-debug#throw) function to terminate the execution of the contract early and prevent the automatic contract's data save implicitly added by Tact after the end of each receiver. Conversely, your manual changes to data made with this function will be lost.
379+
380+
:::
381+
382+
Usage example:
383+
384+
```tact {13}
385+
contract WalletV4(
386+
seqno: Int as uint32,
387+
// ...other parameters...
388+
) {
389+
// ...
390+
external(_: Slice) {
391+
// ...various prior checks...
392+
393+
acceptMessage();
394+
self.seqno += 1;
395+
396+
// Manually saving the contract's state
397+
setData(self.toCell());
398+
399+
// And halting the transaction to prevent a secondary save implicitly
400+
// added by Tact after the main execution logic of the receiver
401+
throw(0);
402+
}
403+
}
404+
```
405+
406+
:::note
407+
408+
Tact automatically saves the contract's state after the end of each receiver's logic even when `return{:tact}` statements are used for early termination. Thus, this function is almost never needed in regular contracts.
409+
410+
However, if you intend to use the `throw(0){:tact}` pattern to terminate the compute phase and save the state yourself or you want to replace the data when upgrading the contract, this function becomes useful. That said, make sure to double-check and test cover your every move such that the contract's data won't become corrupt or inadvertently gone.
411+
412+
:::
413+
365414
### commit
366415

367416
```tact
@@ -388,8 +437,6 @@ contract WalletV4(
388437
throw(42); // and this won't fail it
389438
}
390439
}
391-
392-
asm fun setData(data: Cell) { c4 POP }
393440
```
394441

395442
## Blockchain state

src/benchmarks/wallet-v4/tact/wallet-v4.tact

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,5 +235,3 @@ contract WalletV4(
235235
return self.extensions;
236236
}
237237
}
238-
239-
asm fun setData(data: Cell) { c4 POP }

src/benchmarks/wallet-v5/tact/wallet-v5.tact

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,4 +223,3 @@ asm fun setC5(outActions: Cell) { c5 POP }
223223
asm extends mutates fun checkAndRemoveAddExtensionPrefix(self: Slice): Bool { x{02} SDBEGINSQ }
224224
asm extends mutates fun checkAndRemoveDeleteExtensionPrefix(self: Slice): Bool { x{03} SDBEGINSQ }
225225
asm extends mutates fun checkAndRemoveSetSignAllowedPrefix(self: Slice): Bool { x{04} SDBEGINSQ }
226-
asm fun setData(data: Cell) { c4 POP }

src/stdlib/stdlib.ts

Lines changed: 185 additions & 159 deletions
Large diffs are not rendered by default.

src/stdlib/stdlib/std/internal/contract.tact

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ struct StateInit {
9999
///
100100
/// Checks if the given `address` corresponds to the contract address in the workchain ID 0 (basechain) derived from the `StateInit` `self`. Returns `true` if the addresses match and `false` otherwise.
101101
///
102-
/// This function works correctly only for basechain addresses. It may produce false positives or negatives if the specified `address` or the address derived from the `StateInit{:tact}` `self{:tact}` has a non-zero workchain ID.
102+
/// This function works correctly only for basechain addresses. It may produce false positives or negatives if the specified `address` or the address derived from the `StateInit` `self` has a non-zero workchain ID.
103103
///
104104
/// #### Usage
105105
///
@@ -457,3 +457,52 @@ asm fun setSeed(seed: Int) { SETRAND }
457457
/// See: https://docs.tact-lang.org/ref/core-contextstate#mycode
458458
///
459459
asm fun myCode(): Cell { MYCODE }
460+
461+
/// Global function. Available since Tact 1.7.0. DANGEROUS: Applies irreversible modifications to the contract — use only when you know what you are doing!
462+
///
463+
/// Replaces the current contract's state data [`Cell`][cell] with the new `data`. It is useful only in exceptional cases, such as contract upgrades, data migrations, or when processing external messages with a catch-all [`Slice`][slice] receiver for maximum efficiency. Otherwise, do **not** use this function, as it immediately and permanently overrides the state with no ability to recover, which can result in the loss of funds and partial or full corruption of the contract's data.
464+
///
465+
/// #### Caution
466+
///
467+
/// When using this function, make sure that all logical code branches within your receiver end with a call to the [`throw(0)`][throw] function to terminate the execution of the contract early and prevent the automatic contract's data save implicitly added by Tact after the end of each receiver. Conversely, your manual changes to data made with this function will be lost.
468+
///
469+
/// #### Usage example
470+
///
471+
/// ```tact {13}
472+
/// contract WalletV4(
473+
/// seqno: Int as uint32,
474+
/// // ...other parameters...
475+
/// ) {
476+
/// // ...
477+
/// external(_: Slice) {
478+
/// // ...various prior checks...
479+
///
480+
/// acceptMessage();
481+
/// self.seqno += 1;
482+
///
483+
/// // Manually saving the contract's state
484+
/// setData(self.toCell());
485+
///
486+
/// // And halting the transaction to prevent a secondary save implicitly
487+
/// // added by Tact after the main execution logic of the receiver
488+
/// throw(0);
489+
/// }
490+
/// }
491+
/// ```
492+
///
493+
/// #### Note
494+
///
495+
/// Tact automatically saves the contract's state after the end of each receiver's logic even when `return` statements are used for early termination. Thus, this function is almost never needed in regular contracts.
496+
///
497+
/// However, if you intend to use the `throw(0)` pattern to terminate the compute phase and save the state yourself or you want to replace the data when upgrading the contract, this function becomes useful. That said, make sure to double-check and test cover your every move such that the contract's data won't become corrupt or inadvertently gone.
498+
///
499+
/// #### See also
500+
///
501+
/// - https://docs.tact-lang.org/ref/core-contextstate#setdata
502+
/// - https://docs.tact-lang.org/ref/core-debug#throw
503+
///
504+
/// [cell]: https://docs.tact-lang.org/book/cells#cells
505+
/// [slice]: https://docs.tact-lang.org/book/cells#slices
506+
/// [throw]: https://docs.tact-lang.org/ref/core-debug#throw
507+
///
508+
asm fun setData(data: Cell) { c4 POP }

src/stdlib/stdlib/std/internal/send.tact

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -627,8 +627,6 @@ asm fun acceptMessage() { ACCEPT }
627627
/// throw(42); // and this won't fail it
628628
/// }
629629
/// }
630-
///
631-
/// asm fun setData(data: Cell) { c4 POP }
632630
/// ```
633631
///
634632
/// See: https://docs.tact-lang.org/ref/core-contextstate#commit

src/test/e2e-emulated/contract-state/explicit-setData-contract-parameters.tact

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,3 @@ contract Test(seqno: Int) {
1717
return self.seqno;
1818
}
1919
}
20-
21-
asm fun setData(data: Cell) { c4 POP }

0 commit comments

Comments
 (0)