Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(stdlib): Slice.loadBasechainAddress() and Slice.skipBasechainAddress() #2395

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions dev-docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Standard Library

- Added `forceWorkchain()` function: PR [#2387](https://github.com/tact-lang/tact/pull/2387)
- Added `Slice.loadBasechainAddress()` and `Slice.skipBasechainAddress()` functions: PR [#2395](https://github.com/tact-lang/tact/pull/2395)

### Docs

Expand Down
6 changes: 5 additions & 1 deletion docs/src/content/docs/ref/core-addresses.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import { Badge } from '@astrojs/starlight/components';

`Address{:tact}` represents a standard [smart contract address](https://docs.ton.org/v3/concepts/dive-into-ton/ton-blockchain/smart-contract-addresses#address-of-smart-contract) on TON Blockchain.

See also: [`myAddress(){:tact}` function in the context and state reference](/ref/core-contextstate#myaddress).
See also:

* [`myAddress(){:tact}` function in the context and state reference](/ref/core-contextstate#myaddress).
* Address-oriented extension functions for [`Builder{:tact}`][builder] and [`Slice{:tact}`][slice] types on their reference page: [Cells, Builders and Slices](/ref/core-cells).

## newAddress

Expand Down Expand Up @@ -413,6 +416,7 @@ fun example() {
[int]: /book/integers
[nano]: /book/integers#nanotoncoin
[cell]: /book/cells#cells
[builder]: /book/cells#builders
[slice]: /book/cells#slices
[struct]: /book/structs-and-messages#structs
[opt]: /book/optionals
Expand Down
54 changes: 54 additions & 0 deletions docs/src/content/docs/ref/core-cells.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,59 @@ s1.skipAddress();
let fizz: Int = s1.loadUint(32); // 42
```

### Slice.loadBasechainAddress

<Badge text="Available since Tact 1.6.4" variant="tip" size="medium"/><p/>

```tact
extends mutates fun loadBasechainAddress(self: Slice): BasechainAddress;
```

Extension mutation function for the [`Slice{:tact}`][slice] type.

Loads and returns a [`BasechainAddress{:tact}`][base-addr] [struct][struct] from the [`Slice{:tact}`][slice].

Attempts to load such [`BasechainAddress{:tact}`][base-addr] struct when [`Slice{:tact}`][slice] doesn't contain it throw an exception with [exit code 8](/book/exit-codes#8): `Cell overflow`.

Attempts to load more data than [`Slice{:tact}`][slice] contains throw an exception with [exit code 9](/book/exit-codes#9): `Cell underflow`.
Comment on lines +1209 to +1211
Copy link
Member

Choose a reason for hiding this comment

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

What happens when a slice that is not a valid basechain addess is passed?

Copy link
Member Author

@novusnota novusnota Mar 18, 2025

Choose a reason for hiding this comment

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

Exit code 9.

Actually, I may have found a bunch of long-standing typos — the exit code 8 doesn't seem right for the .load functions.

Double-checking those now.

UPD: They're incorrect

Copy link
Member

Choose a reason for hiding this comment

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

This should be documented


Usage example:

```tact
let s: Slice = beginCell()
.storeBasechainAddress(BasechainAddress{ hash: null })
.asSlice();
let fizz: BasechainAddress = s.loadBasechainAddress();
```

### Slice.skipBasechainAddress

<Badge text="Available since Tact 1.6.4" variant="tip" size="medium"/><p/>

```tact
extends mutates fun skipBasechainAddress(self: Slice);
```

Extension mutation function for the [`Slice{:tact}`][slice] type.

Skips a [`BasechainAddress{:tact}`][base-addr] [struct][struct] from the [`Slice{:tact}`][slice].

Attempts to skip such [`BasechainAddress{:tact}`][base-addr] struct when [`Slice{:tact}`][slice] doesn't contain it throw an exception with [exit code 8](/book/exit-codes#8): `Cell overflow`.

Attempts to skip more data than [`Slice{:tact}`][slice] contains throw an exception with [exit code 9](/book/exit-codes#9): `Cell underflow`.

Usage example:

```tact
let s1: Slice = beginCell()
.storeBasechainAddress(BasechainAddress{ hash: null })
.storeUint(42, 32)
.asSlice();

s1.skipBasechainAddress();
let fizz: Int = s1.loadUint(32); // 42
```

### Slice.loadRef

```tact
Expand Down Expand Up @@ -2040,6 +2093,7 @@ fun cautiousParse(payload: Slice): TripleAxe? {
[message]: /book/structs-and-messages#messages
[null]: /book/optionals

[base-addr]: /ref/core-addresses#basechainaddress
[std-repr]: /book/cells#cells-representation

[tlb]: https://docs.ton.org/develop/data-formats/tl-b-language
Expand Down
859 changes: 460 additions & 399 deletions src/stdlib/stdlib.ts

Large diffs are not rendered by default.

82 changes: 80 additions & 2 deletions src/stdlib/stdlib/std/internal/address.tact
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ asm( -> 1 0) extends mutates fun loadAddress(self: Slice): Address { LDMSGADDR }
/// }
/// ```
///
/// See: https://docs.tact-lang.org/ref/core-cells#sliceloadaddress
/// See: https://docs.tact-lang.org/ref/core-cells#sliceskipaddress
///
asm extends mutates fun skipAddress(self: Slice) { LDMSGADDR NIP }

Expand Down Expand Up @@ -307,12 +307,15 @@ inline fun contractBasechainAddress(s: StateInit): BasechainAddress {
///
/// ```tact
/// fun example() {
/// let addr: BasechainAddress = newBasechainAddress(0x83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8);
/// let addr: BasechainAddress =
/// newBasechainAddress(0x83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8);
/// let b: Builder = beginCell();
/// let b2: Builder = b.storeBasechainAddress(addr);
/// }
/// ```
///
/// See: https://docs.tact-lang.org/ref/core-cells#builderstorebasechainaddress
///
extends fun storeBasechainAddress(self: Builder, address: BasechainAddress): Builder {
if (address.hash == null) {
return self.storeUint(0, 2); // 0b00
Expand All @@ -323,6 +326,81 @@ extends fun storeBasechainAddress(self: Builder, address: BasechainAddress): Bui
.storeUint(address.hash!!, 256);
}

/// Extension mutation function for the `Slice` type. Available since Tact 1.6.4.
///
/// Loads and returns a `BasechainAddress` struct from the `Slice`.
///
/// Attempts to load such `BasechainAddress` struct when `Slice` doesn't contain it throw an exception with [exit code 8]: `Cell overflow`.
///
/// Attempts to load more data than `Slice` contains throw an exception with [exit code 9]: `Cell underflow`.
///
/// ```tact
/// fun example() {
/// let s: Slice = beginCell()
/// .storeBasechainAddress(BasechainAddress{ hash: null })
/// .asSlice();
/// let fizz: BasechainAddress = s.loadBasechainAddress();
/// }
/// ```
///
/// See:
/// * https://docs.tact-lang.org/ref/core-cells#sliceloadbasechainaddress
/// * https://docs.tact-lang.org/book/exit-codes
///
/// [exit code 8]: https://docs.tact-lang.org/book/exit-codes#8
/// [exit code 9]: https://docs.tact-lang.org/book/exit-codes#9
///
extends mutates fun loadBasechainAddress(self: Slice): BasechainAddress {
// addr_none
if (self.loadUint(2) == 0) {
return BasechainAddress{ hash: null };
}

// addr_std, skip the remaining prefix bits
self.skipBits(9);
Copy link
Member

Choose a reason for hiding this comment

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

what happens when we have workchains other than basechain?

// Load the hash (account ID)
return BasechainAddress{ hash: self.loadUint(256) };
}

/// Extension mutation function for the `Slice` type. Available since Tact 1.6.4.
///
/// Skips a `BasechainAddress` struct from the `Slice`.
///
/// Attempts to skip such `BasechainAddress` struct when `Slice` doesn't contain it throw an exception with [exit code 8]: `Cell overflow`.
///
/// Attempts to skip more data than `Slice` contains throw an exception with [exit code 9]: `Cell underflow`.
///
/// ```tact
/// fun example() {
/// let s1: Slice = beginCell()
/// .storeBasechainAddress(BasechainAddress{ hash: null })
/// .storeUint(42, 32)
/// .asSlice();
///
/// s1.skipBasechainAddress();
/// let fizz: Int = s1.loadUint(32); // 42
/// }
/// ```
///
/// See:
/// * https://docs.tact-lang.org/ref/core-cells#sliceskipbasechainaddress
/// * https://docs.tact-lang.org/book/exit-codes
///
/// [exit code 8]: https://docs.tact-lang.org/book/exit-codes#8
/// [exit code 9]: https://docs.tact-lang.org/book/exit-codes#9
///
extends mutates fun skipBasechainAddress(self: Slice) {
// addr_std
if (self.loadUint(2) != 0) {
// 9 bits of the remaining prefix since first 2 are loaded already
// plus 256 bits of the hash (account ID)
self.skipBits(265);
}

// addr_none, nothing to load since its 2 bits
// are loaded in the if statement above
}

/// Global function. Available since Tact 1.6.3.
///
/// Checks whether the `address` is in the basechain, i.e., its chain ID is 0. If it is not, throws an exception with exit code 138: `Not a basechain address`.
Expand Down
Loading