Git Product home page Git Product logo

Comments (4)

jamescarter-le avatar jamescarter-le commented on July 30, 2024

I was able to figure this out for the following cases:

Where the Content Representation is On-chain content layout or Off-chain content layout
Where the data stored, is serialized in snake case

You can call HydrateFromUri to pull additional attributes that will provide missing data to the metadata.

    public record JettonMetadata
    {
        private const int DefaultDecimals = 9;
        private const string KeyHashUri = "70e5d7b6a29b392f85076fe15ca2f2053c56c2338728c4e33c9e8ddb1ee827cc";
        private const string KeyHashName = "82a3537ff0dbce7eec35d69edc3a189ee6f17d82f353a553f9aa96cb0be3ce89";
        private const string KeyHashDescription = "c9046f7a37ad0ea7cee73355984fa5428982f8b37c8f7bcec91f7ac71a7cd104";
        private const string KeyHashImage = "6105d6cc76af400325e94d588ce511be5bfdbb73b437dc51eca43917d7a43e3d";
        private const string KeyHashImageData = "d9a88ccec79eef59c84b671136a20ece4cd00caaad5bc47e2c208829154ee9e4";
        private const string KeyHashSymbol = "b76a7ca153c24671658335bbd08946350ffc621fa1c516e7123095d4ffd5c581";
        private const string KeyHashDecimals = "ee80fd2f1e03480e2282363596ee752d7bb27f50776b95086a0279189675923e";
        private const string KeyHashAmountStyle = "8b10e058ce46c44bc1ba139bc9761721e49170e2c0a176129250a70af053b700";
        private const string KeyHashRenderType = "d33ae06043036d0d1c3be27201ac15ee4c73da8cdb7c8f3462ce308026095ac0";

        private bool isHydrated = false;

        public string? Uri { get; init; }
        public string? Name { get; init; }
        public string? Description { get; init; }
        public string? Image { get; init; }
        public byte[]? ImageData { get; init; }
        public string? Symbol { get; init; }
        public int Decimals { get; init; } = DefaultDecimals;
        public string? RenderType { get; init; }
        public string? AmountStyle { get; init; }

        public bool CanHydrate => Uri != null && isHydrated == false;

        public static JettonMetadata ParseFromCell(Cell cell)
        {
            const int OnchainContent = 0;
            const int OffchainContent = 1;

            var slice = cell.BeginRead();
            var contentType = slice.LoadByte();

            if (contentType == OnchainContent)
            {
                return FromOnchainContent(slice);
            }
            else if (contentType == OffchainContent)
            {
                var uri = slice.LoadString();
                return new JettonMetadata { Uri = uri };
            }

            throw new Exception("Unsupported representation type");
        }

        private static JettonMetadata FromOnchainContent(Slice slice)
        {
            var onchainData = slice.LoadAndParseDict(
                256,
                x => Convert.ToHexString(x.LoadBytes(32)).ToLower(),
                x => x,
                StringComparer.Instance);

            var metadata = new JettonMetadata();

            string? GetString(string key)
            {
                if (onchainData.TryGetValue(key, out var value))
                {
                    var format = value.LoadByte();
                    return format switch
                    {
                        0 => value.LoadString(),
                        1 => throw new NotImplementedException("Not yet support Chunked Format https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md#specification"),
                        _ => throw new Exception("Unknown data serialization format https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md#specification")
                    };
                }

                return null;
            }

            int decimals = DefaultDecimals;
            var decimalsStr = GetString(KeyHashDecimals);
            if (decimalsStr != null)
                decimals = int.Parse(decimalsStr);

            metadata = new JettonMetadata
            {
                Decimals = decimals,
                Uri = GetString(KeyHashUri),
                Name = GetString(KeyHashName),
                Description = GetString(KeyHashDescription),
                Image = GetString(KeyHashImage),
                Symbol = GetString(KeyHashSymbol),
                AmountStyle = GetString(KeyHashAmountStyle),
                RenderType = GetString(KeyHashRenderType)
            };

            return metadata;
        }

        public async Task<JettonMetadata> HydrateFromUri(HttpClient? httpClient = null)
        {
            if(string.IsNullOrEmpty(Uri))
                throw new InvalidOperationException("Uri is not set");

            httpClient ??= new HttpClient();
            var stream = await httpClient.GetStreamAsync(Uri);
            var jsonDoc = JsonDocument.Parse(stream);

            var result = this with { isHydrated = true };

            if (string.IsNullOrEmpty(Name) && jsonDoc.RootElement.TryGetProperty("name", out var name))
                result = result with { Name = name.GetString() };
            if (string.IsNullOrEmpty(Description) && jsonDoc.RootElement.TryGetProperty("description", out var description))
                result = result with { Description = description.GetString() };
            if (string.IsNullOrEmpty(Image) && jsonDoc.RootElement.TryGetProperty("image", out var image))
                result = result with { Image = image.GetString() };
            if (string.IsNullOrEmpty(Symbol) && jsonDoc.RootElement.TryGetProperty("symbol", out var symbol))
                result = result with { Symbol = symbol.GetString() };
            if (string.IsNullOrEmpty(RenderType) && jsonDoc.RootElement.TryGetProperty("render_type", out var renderType))
                result = result with { RenderType = renderType.GetString() };
            if (string.IsNullOrEmpty(AmountStyle) && jsonDoc.RootElement.TryGetProperty("amount_style", out var amountStyle))
                result = result with { AmountStyle = amountStyle.GetString() };

            return result;
        }
    }
}

public class StringComparer : IEqualityComparer<string>
{
    public static readonly StringComparer Instance = new();
    public bool Equals(string? x, string? y) => x == y;
    public int GetHashCode(string obj) => obj.GetHashCode();
}

from tonlib.net.

justdmitry avatar justdmitry commented on July 30, 2024

Hi,
Yes, using ...ParseDict with slices is the correct way, your solution seems fine in general.
Also, please note that v0.21.3 introduced ...ParseDictRef function that gives you value as Cell (instead of Slice), which is useful when dict is stored inside dict (or other cases when dict_set_ref in FunC had been used).

Some minor fixes in your code:

else if (contentType == OffchainContent)
{
    var uri = slice.LoadString();
    return new JettonMetadata { Uri = uri };
} 

I think it should be slice.LoadStringSnake() here, and in 0 => value.LoadString(), too.

BTW, LoadStringSnake takes a parameter to omit/skip check of 0x00 prefix (which I saw missing in many places).

.

httpClient ??= new HttpClient();

Don't forget to dispose self-created HttpClient

from tonlib.net.

jamescarter-le avatar jamescarter-le commented on July 30, 2024

Oh nice thanks for the tips!

Yes temp HttpClient should be disposed. Appreciate the library.

Off topic: Is there a way to subscribe to Blocks/Transactions using tonlib, such that I can grab Transactions for Accounts as they are emitted?

from tonlib.net.

justdmitry avatar justdmitry commented on July 30, 2024

Is there a way to subscribe to Blocks/Transactions using tonlib, such that I can grab Transactions for Accounts as they are emitted?

No, there is no such functionality in tonlib.
You need either to use some HTTP API (https://docs.ton.org/develop/dapps/apis/) that supports blocks/transactions streaming, or request account tx/status every several seconds.

from tonlib.net.

Related Issues (6)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.