ton-society / grants-and-bounties

TON Foundation invites talent to imagine and realize projects that have the potential to integrate with the daily lives of users.
https://ton.org/grants
281 stars 128 forks source link

Tact String Library #413

Closed 0kenx closed 5 months ago

0kenx commented 7 months ago

Summary

Enhance the Tact language for TON smart contracts with a focus on string handling.

Context

Why it's Important

It is crucial when developing projects that require handling string messages. This library aims to minimize the time developers spend building with Tact by streamlining string handling.

Problem Showcase

Currently, when a string input is passed to a contract's receiver function, it cannot be directly used for numbers or addresses. For example, inputs like "1235" or a TON address in string form ("EQ...qe") require conversion to a buffer or slice. Then, it's necessary to calculate in 8-bit increments to convert these to hexadecimal values.

Proposed Solution

Develop extended functions that can be easily integrated into contracts, allowing for direct use. These functions will enable developers to efficiently manage string inputs without the need for complex conversions.

For this bounty we would focus on short strings that fit within a single slice (without refs).

Examples

Here's a non-exhaustive list of methods we plan to introduce in the Tact string library.

String comparison & verification

fun ss_compare(self: String, other: String): Int;
fun ss_starts_with(self: String, prefix: String): Bool;
fun ss_ends_with(self: String, suffix: String): Bool;
fun ss_contains(self: String, sub: String): Bool;

fun ss_find(self: String, sub: String): Int;

fun ss_is_ascii(self: String): Bool;
fun ss_is_alnum(self: String): Bool;
fun ss_is_alpha(self: String): Bool;
fun ss_is_digit(self: String): Bool;
fun ss_is_hex(self: String): Bool;
fun ss_is_lower(self: String): Bool;
fun ss_is_upper(self: String): Bool;

String manipulation

fun ss_lsplit_before(self: String, pos: Int): String;
fun ss_lsplit_after(self: String, pos: Int): String;
fun ss_rsplit_before(self: String, pos: Int): String;
fun ss_rsplit_after(self: String, pos: Int): String;

fun ss_remove_prefix(self: String, prefix: String): String;
fun ss_remove_suffix(self: String, suffix: String): String;

String encoding/decoding

fun ss_parse_uint(self: Slice, base: Int): Int;
fun ss_parse_tokens(self: String): Int;
fun ss_parse_address(self: String): Address;
fun ss_address_to_hex(self: String, prefix: Bool): String;

fun to_hex(self: Address, prefix: Bool): String;
fun to_hex(self: Int, nibbles: Int, prefix: Bool): String;
fun to_hex(self: Slice, prefix: Bool): String;

fun toBase64(self: Slice): Slice; // The inverse of Tact's `fromBase64` function.

References

  1. Solidity string library.
  2. Python string library.
  3. C++ string library.

Estimate suggested reward

tonMaxi commented 7 months ago

1

howardpen9 commented 7 months ago

Important library that can help Inscription team safe more times!

howardpen9 commented 7 months ago

One example code:


/// Converts an integer to a hex string.
/// `num` is the integer to convert.
/// `nibbles` is the number of nibbles(4-bits, or a hex character) the output should have.
/// `prefix` is whether the output should have a "0x" prefix.
/// If `nibbles` is less than the number of nibbles needed to represent `num`, the output will have the MSBs truncated.
/// If `nibbles` is 0 then the output will have the minimum number of nibbles needed to represent `num`.
/// If `nibbles` is greater than the number of nibbles needed to represent `num`, the output will be left-padded with 0s.
/// Returns a `Slice` that can be converted to a `String` using `asString()`.

extends fun int_to_hex(self: Int, nibbles: Int, prefix: Bool): Slice {
    let b: Builder = beginCell();
    if (prefix) {
        b = b.storeUint(12408, 16); // "0x"
    }
    if (nibbles == 0) {
        let n: Int = self;
        while (n > 0) {
            n = n >> 4;
            nibbles = nibbles + 1;
        }
    }
    let i: Int = 0;
    while (i < nibbles) {
        let nibble: Int = (self >> (4 * (nibbles - i - 1))) & 0xF;
        if (nibble < 10) {
            b = b.storeUint(nibble + 48, 8); // 0-9
        } else {
            b = b.storeUint(nibble + 87, 8); // a-f
        }
        i = i + 1;
    }
    return b.asSlice();
}
delovoyhomie commented 6 months ago

@0kenx, we need use cases for library functions. Building libraries with speculative utility usually doesn't work out well. Please suggest some compelling use cases.

delovoyhomie commented 6 months ago

@0kenx, Could I ask you to contact me via Telegram @delovoyslava?

0kenx commented 5 months ago

This is foundational work which would allow developers to handle strings in Tact more effectively. Potential use cases include:

String manipulation is the basis of all programming languages and effort shall be invested into the development of a good string library.

anton-trunov commented 5 months ago

Let me just comment on the first bullet:

Parsing string messages as call parameters in smart contracts. For example, users can send message "Send 12 tokens to [address]" to transfer tokens.

Isn't it should be the dapp frontend's responsibility to provide this info in a structured machine-readable way (while performing input validation)? Otherwise we are going to include too much logic in a smart contract consuming a lot of gas. Summoning @Gusarich to provide more feedback on this.

Gusarich commented 5 months ago

Isn't it should be the dapp frontend's responsibility to provide this info in a structured machine-readable way (while performing input validation)? Otherwise we are going to include too much logic in a smart contract consuming a lot of gas.

I totally agree. This also applies to the third point:

Generate dynamic comment strings for TON/Jetton transfers. For example, DEXes can display "Swap 100 AA for 25 TON" as a comment of a swap outbound transfer.

It's more efficient and safe to store such log messages in binary format and parse on front-end or back-end.

0kenx commented 5 months ago

It's up to the dapp developer to decide what he wants. We need to provide a toolbox containing all necessary tools.

tactfunc commented 5 months ago

need this library

0kenx commented 4 months ago

For anyone interested: https://github.com/microcosm-labs/tact-libs/blob/main/utils/string.tact