Giter Site home page Giter Site logo

xssnick / tonutils-go Goto Github PK

View Code? Open in Web Editor NEW
378.0 16.0 75.0 7.25 MB

TON SDK Library in pure Golang for interacting with The Open Network ecosystem using native protocols, such as ADNL, RLDP and etc.

License: Apache License 2.0

Go 100.00%
ton blockchain golang golang-library telegram toncoin sdk sdk-go

tonutils-go's Introduction

tonutils-go

Based on TON Coverage

Golang library for interacting with TON blockchain.

This library is native golang implementation of ADNL and lite protocol. It works as connection pool and can be connected to multiple lite servers in the same time, balancing is done on lib side.

It is concurrent safe and can be used from multiple goroutines under high workloads.

All main TON protocols are implemented: ADNL, DHT, RLDP, Overlays, HTTP-RLDP, etc.


If you love this library and want to support its development you can donate any amount of coins to this ton address ☺️ EQBx6tZZWa2Tbv6BvgcvegoOQxkRrVaBVwBOoW85nbP37_Go

How to use

You can find usage examples in example directory

You can also join our Telegram group and ask any questions :)

Connection

You can get list of public lite servers from official TON configs:

  • Mainnet - https://ton.org/global.config.json
  • Testnet - https://ton-blockchain.github.io/testnet-global.config.json

from liteservers section, you need to convert int to ip and take port and key.

Or you can run your own full node, see TON docs.

You can connect like that:

client := liteclient.NewConnectionPool()

configUrl := "https://ton-blockchain.github.io/testnet-global.config.json"
err := client.AddConnectionsFromConfigUrl(context.Background(), configUrl)
if err != nil {
    panic(err)
}
api := ton.NewAPIClient(client)
api = api.WithRetry() // if you want automatic retries with failover to another node

Since client implements connection pool, it can be the chance that some block is not yet applied on one of the nodes, so it can lead to errors. If you want to bound all requests of operation to single node, use

ctx := client.StickyContext(context.Background())

And pass this context to methods.

Wallet

You can use existing wallet or generate new one using wallet.NewSeed(), wallet will be initialized by the first message sent from it. This library will deploy and initialize wallet contract if it is not initialized yet.

You can also send any message to any contract using w.Send method, it accepts tlb.InternalMessage structure, you can dive into w.Transfer implementation and see how it works.

Example of basic usage:

words := strings.Split("birth pattern ...", " ")

w, err := wallet.FromSeed(api, words, wallet.V3)
if err != nil {
    panic(err)
}

balance, err := w.GetBalance(context.Background(), block)
if err != nil {
    panic(err)
}

if balance.Nano().Uint64() >= 3000000 {
    addr := address.MustParseAddr("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N")
    err = w.Transfer(context.Background(), addr, tlb.MustFromTON("0.003"), "Hey bro, happy birthday!")
    if err != nil {
        panic(err)
    }
}

You can find full working example at example/wallet/main.go

Contracts

Here is the description of features which allow us to trigger contract's methods

Using GET methods

Let's imagine that we have contract with method

cell mult(int a, int b) method_id {
  return begin_cell().store_uint(a * b, 64).end_cell();
}

We can trigger it and get result this way:

// api = initialized ton.APIClient, see Connection in readme

// we need fresh block info to run get methods
block, err := api.CurrentMasterchainInfo(context.Background())
if err != nil {
    panic(err)
}

// contract address
addr := address.MustParseAddr("kQB3P0cDOtkFDdxB77YX-F2DGkrIszmZkmyauMnsP1gg0inM")

// run get method `mult` of contract with int arguments 7 and 8
res, err := api.RunGetMethod(context.Background(), block, addr, "mult", 7, 8)
if err != nil {
    // if contract exit code != 0 it will be treated as an error too
    panic(err)
}

// we are sure that return value is 1 cell, we can directly cast it and parse
val, err := res.MustCell(0).BeginParse().LoadUInt(64)
if err != nil {
    panic(err)
}

// prints 56
println(val)

Send external message

Using messages, you can interact with contracts to modify state. For example, it can be used to interact with wallet and send transactions to others.

You can send message to contract like that:

// message body
data := cell.BeginCell().
    MustStoreUInt(777, 64).
    EndCell()

// contract address
addr := address.MustParseAddr("kQBkh8dcas3_OB0uyFEDdVBBSpAWNEgdQ66OYF76N4cDXAFQ")

// send external message, processing fees will be taken from contract
err = api.SendExternalMessage(context.Background(), addr, data)
if err != nil {
    panic(err)
}

You can find full working example at example/external-message/main.go

Deploy

Contracts can be deployed using wallet's method DeployContract, you should pass 3 cells there: contract code, contract initial data, message body.

You can find example here

Account info and transactions

You can get full account information including balance, stored data and even code using GetAccount method. You can also get account's list of transactions with all details.

Example:

// TON Foundation account
addr := address.MustParseAddr("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N")

account, err := api.GetAccount(context.Background(), b, addr)
if err != nil {
    log.Fatalln("get account err:", err.Error())
    return
}

// Balance: ACTIVE
fmt.Printf("Status: %s\n", account.State.Status)
// Balance: 66559946.09 TON
fmt.Printf("Balance: %s TON\n", account.State.Balance.String())
if account.Data != nil { // Can be nil if account is not active
    // Data: [0000003829a9a31772c9ed6b62a6e2eba14a93b90462e7a367777beb8a38fb15b9f33844d22ce2ff]
    fmt.Printf("Data: %s\n", account.Data.Dump())
}

// load last 15 transactions
list, err := api.ListTransactions(context.Background(), addr, 15, account.LastTxLT, account.LastTxHash)
if err != nil {
    // In some cases you can get error:
    // lite server error, code XXX: cannot compute block with specified transaction: lt not in db
    // it means that current lite server does not store older data, you can query one with full history
    log.Printf("send err: %s", err.Error())
    return
}

// oldest = first in list
for _, t := range list {
    // Out: 620.9939549 TON, To [EQCtiv7PrMJImWiF2L5oJCgPnzp-VML2CAt5cbn1VsKAxLiE]
    // In: 494.521721 TON, From EQB5lISMH8vLxXpqWph7ZutCS4tU4QdZtrUUpmtgDCsO73JR 
    // ....
    fmt.Println(t.String())
}

You can find extended working example at example/account-state/main.go

NFT

You can mint, transfer, and get NFT information using nft.ItemClient and nft.CollectionClient, like that:

api := ton.NewAPIClient(client)

nftAddr := address.MustParseAddr("EQDuPc-3EoqH72Gd6M45vmFsktQ8AzqaN14mweJhCjxg0d_b")
item := nft.NewItemClient(api, nftAddr)

nftData, err := item.GetNFTData(context.Background())
if err != nil {
    panic(err)
}

// get info about our nft's collection
collection := nft.NewCollectionClient(api, nftData.CollectionAddress)
collectionData, err := collection.GetCollectionData(context.Background())
if err != nil {
    panic(err)
}

fmt.Println("Collection addr      :", nftData.CollectionAddress.String())
fmt.Println("    content          :", collectionData.Content.(*nft.ContentOffchain).URI)
fmt.Println("    owner            :", collectionData.OwnerAddress.String())
fmt.Println("    minted items num :", collectionData.NextItemIndex)
fmt.Println()
fmt.Println("NFT addr         :", nftAddr.String())
fmt.Println("    initialized  :", nftData.Initialized)
fmt.Println("    owner        :", nftData.OwnerAddress.String())
fmt.Println("    index        :", nftData.Index)

if nftData.Initialized {
    // get full nft's content url using collection method that will merge base url with nft's data
    nftContent, err := collection.GetNFTContent(context.Background(), nftData.Index, nftData.Content)
    if err != nil {
        panic(err)
    }
    fmt.Println("    part content :", nftData.Content.(*nft.ContentOffchain).URI)
    fmt.Println("    full content :", nftContent.(*nft.ContentOffchain).URI)
} else {
    fmt.Println("    empty content")
}

You can find full examples at example/nft-info/main.go and example/nft-mint/main.go

Jettons

You can get information about jetton, get jetton wallet and its balance, transfer jettons, and burn them using jetton.Client like that:

// jetton contract address
contract := address.MustParseAddr("EQAbMQzuuGiCne0R7QEj9nrXsjM7gNjeVmrlBZouyC-SCLlO")
master := jetton.NewJettonMasterClient(api, contract)

// get information about jetton
data, err := master.GetJettonData(context.Background())
if err != nil {
    log.Fatal(err)
}

log.Println("total supply:", data.TotalSupply.Uint64())

// get jetton wallet for account
ownerAddr := address.MustParseAddr("EQC9bWZd29foipyPOGWlVNVCQzpGAjvi1rGWF7EbNcSVClpA")
jettonWallet, err := master.GetJettonWallet(context.Background(), ownerAddr)
if err != nil {
    log.Fatal(err)
}

jettonBalance, err := jettonWallet.GetBalance(context.Background())
if err != nil {
    log.Fatal(err)
}
log.Println("balance:", jettonBalance.String())

You can find full example, which also contains transfer, at example/jettons/main.go

DNS

You can get information about domains, get connected wallet and any other records, transfer domain, edit and do any other nft compatible operations using dns.Client like that:

resolver := dns.NewDNSClient(api, dns.RootContractAddr(api))

domain, err := resolver.Resolve(context.Background(), "alice.ton")
if err != nil {
    panic(err)
}

log.Println("domain points to wallet address:", domain.GetWalletRecord())

You can find full example at example/dns/main.go

Records

You could get any record of the domain using domain.GetRecord("record name") method, it will return cell that will contain data structure depends on record type. Alternatively you can also use domain.GetWalletRecord() and domain.GetSiteRecord(), they will return already parsed data, like address for wallet, or slice of bytes with type for site.

To set domain records you can use domain.BuildSetRecordPayload("record name", dataCell), or predefined BuildSetSiteRecordPayload(adnlAddress), BuildSetWalletRecordPayload(walletAddress). It will generate body cell that you need to send to domain contract from the owner's wallet.

Example:

// get root dns address from network config
root, err := dns.RootContractAddr(api)
if err != nil {
    panic(err)
}

resolver := dns.NewDNSClient(api, root)
domainInfo, err := resolver.Resolve(ctx, "utils.ton")
if err != nil {
    panic(err)
}

// prepare transaction payload cell which will change site address record
body := domainInfo.BuildSetSiteRecordPayload(adnlAddr)

// w = wallet.FromSeed("domain owner seed phrase")
err = w.Send(context.Background(), &wallet.Message{
  Mode: 1, // pay fees separately (from balance, not from amount)
  InternalMessage: &tlb.InternalMessage{
    Bounce:  true, // return amount in case of processing error
    DstAddr: domainInfo.GetNFTAddress(), // destination is domain contract
    Amount:  tlb.MustFromTON("0.03"),
    Body:    body,
  },
}, true)
if err != nil {
    panic(err)
}

// done! now record of your site is changed!

Cells

Work with cells is very similar to FunC cells:

builder := cell.BeginCell().MustStoreUInt(0b10, 2).
    MustStoreUInt(0b00, 2). // src addr_none
    MustStoreAddr(addr).    // dst addr
    MustStoreCoins(0)       // import fee 0

builder.MustStoreUInt(0b11, 2). // has state init as cell
    MustStoreRef(cell.BeginCell().
        MustStoreUInt(0b00, 2).                     // no split depth, no special
        MustStoreUInt(1, 1).MustStoreRef(code).     // with code
        MustStoreUInt(1, 1).MustStoreRef(initData). // with data
        MustStoreUInt(0, 1).                        // no libs
    EndCell()).
    MustStoreUInt(0, 1). // slice data
    MustStoreUInt(0, 1)  // 1 bit as body, cause its required

result := builder.EndCell()

// {bits_size}[{hex_data}]
//  279[8800b18cc741b244e114685e1a9e9dc835bff5c157a32a38df49e87b71d0f0d29ba418] -> {
//    5[30] -> {
//      0[],
//      8[01]
//    }
//  }

fmt.Println(result.Dump())

Load from cell:

slice := someCell.BeginParse()
wc := slice.MustLoadUInt(8)
data := slice.MustLoadSlice(256)

There are 2 types of methods Must and regular. The difference is that in case of error, Must will panic, but regular will just return error, so use Must only when you are sure that your data fits max cell size and other conditions

To debug cells you can use Dump() and DumpBits() methods of cell, they will return string with beautifully formatted cells and their refs tree

BoC

Sometimes it is needed to import or export cell, for example to transfer over the network, for this, Bag of Cells serialization format is exists.

You can simply export cell using ToBOC() method of cell, and import it using cell.FromBOC(bytes).

Example of use can be found in tests or in transfer-url-for-qr example

Proofs

You can create proof from cell by constructing skeleton of references you want to keep, all other cells not presented in path will be pruned.

sk := cell.CreateProofSkeleton()
sk.ProofRef(0).ProofRef(1)

// Tips:
//   you could also do SetRecursive() on needed ref to add all its child cells to proof
//   you can merge 2 proof skeletons using Merge

merkleProof, err := someCell.CreateProof(sk)
if err != nil {
    t.Fatal(err)
}

fmt.Println(merkleProof.Dump())

To check proof you could use cell.CheckProof(merkleProof, hash) method, or cell.UnwrapProof(merkleProof, hash) if you want to continue to read proof body.

TLB Loader

You can also load cells to structures, similar to JSON, using tags. You can find more details in comment-description of tlb.LoadFromCell method

Example:

type ShardState struct {
    _               Magic      `tlb:"#9023afe2"`
    GlobalID        int32      `tlb:"## 32"`
    ShardIdent      ShardIdent `tlb:"."`
    Seqno           uint32     `tlb:"## 32"`
    OutMsgQueueInfo *cell.Cell `tlb:"^"`
    Accounts        struct {
        ShardAccounts *cell.Dictionary `tlb:"dict 256"`
    } `tlb:"^"`
}

type ShardIdent struct {
    PrefixBits  int8   `tlb:"## 6"`
    WorkchainID int32  `tlb:"## 32"`
    ShardPrefix uint64 `tlb:"## 64"`
}

var state ShardState
if err = tlb.LoadFromCell(&state, cl.BeginParse()); err != nil {
    panic(err)
}

TLB Serialize

Its also possible to serialize structures back to cells using tlb.ToCell, see build NFT mint message for example.

Custom reconnect policy

By default, standard reconnect method will be used - c.DefaultReconnect(3*time.Second, 3) which will do 3 tries and wait 3 seconds after each.

But you can use your own reconnection logic, this library support callbacks, in this case OnDisconnect callback can be used, you can set it like this:

client.SetOnDisconnect(func(addr, serverKey string) {
	// ... do something
})

Features to implement

  • ✅ Support cell and slice as arguments to run get method
  • ✅ Reconnect on failure
  • ✅ Get account state method
  • ✅ Send external message
  • ✅ Get transactions
  • ✅ Deploy contracts
  • ✅ Wallet operations
  • ✅ Cell dictionaries support
  • ✅ MustLoad methods
  • ✅ Parse global config json
  • ✅ Jettons
  • ✅ DNS
  • ✅ ADNL UDP Client/Server
  • ✅ ADNL TCP Client/Server
  • ✅ RLDP Client/Server
  • ✅ TON Sites Client/Server
  • ✅ DHT Client
  • ✅ Merkle proofs validation and creation
  • ✅ Overlays
  • ✅ TL Parser/Serializer
  • ✅ TL-B Parser/Serializer
  • ✅ Payment channels
  • ✅ Liteserver proofs automatic validation
  • DHT Server
  • TVM

tonutils-go's People

Contributors

actions-user avatar andreypfau avatar andreyxdd avatar aspite avatar awesome-doge avatar dependabot[bot] avatar gituser543 avatar hotid avatar iam047801 avatar jemeraldo avatar jokly avatar klim0v avatar kosrk avatar krigga avatar logvik avatar mr-tron avatar pr0n1x avatar sashaitx avatar stfy avatar truecarry avatar xssnick avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tonutils-go's Issues

`Merkle proofs automatic validation` feature

Hi!

In readme file, Merkle proofs automatic validation is not checked.
But I see Merkle proofs are checked in the features.
How can I make sure that Merkle proofs work on my integration?

Beat Regards.

How do we get user wallet from this backend ?

func getWallet(api *ton.APIClient) *wallet.Wallet {
	words := strings.Split("birth pattern then forest walnut then phrase walnut fan pumpkin pattern then cluster blossom verify then forest velvet pond fiction pattern collect then then", " ")
	w, err := wallet.FromSeed(api, words, wallet.V3)
	if err != nil {
		panic(err)
	}
	return w
}

any correct flow for this ?
How do I get user wallet without user insert this seed

User-friendly to Raw adress conversion/comparison

Hi! In a nutshell, i'm building an app that detect transactions from specific addresses set by user so i need to compare user-friendly addresses (that user send to app) with raw addresses from subscription channel:

senderUFAddr := "sender-user-friendly-address"

go api.SubscribeOnTransactions(context.Background(), receiverAddress, lastProcessedLT, transactions)

for tx := range transactions {
	if tx.IO.In == nil || tx.IO.In.Msg == nil {
		fmt.Println("not an in message")
		continue
	}

	internalMessage, ok := tx.IO.In.Msg.(*tlb.InternalMessage)
	if !ok {
		fmt.Println("not an internal message")
		continue
	}

	senderRawAddr := internalMessage.SrcAddr.String()

	lastProcessedLT = tx.LT
}

How can i do this comparison with tonutils or i have to implement it on my own?

Question about BuildTransferPayload

Hi all, I have a question about jetton message.
If I have a cell that contains lots of additional payload, when I use BuildTransferPayloadV2, cell will be parsed and every data will be appended to the msg_body.

if err := root.StoreBuilder(builder); err != nil {

It may caused cell overflow error. So Im confused why not use store_ref() to store cell? or maybe I have a wrong thinking?

Thanks for everyone's answer.

ListTransactions() always fail with -400 error

I'm trying to load history transaction of an wallet, but always get this error:

send err: lite server error, code -400: cannot load block (0,8000000000000000,40278934):ED46C7BCB4EF18DD435C2F17A55DE395B4F8E49E3AF6BCCF856E4050DF317D5D:51BC46484CF32A99E74B8A5FD54A589EE3FC68E452CFA5C5538FB5C38E757261 with specified transaction: not in db

Anyone know how to fix it? Does that mean all lite servers are down?

Thanks.

I just use the example code to test this feature:
https://github.com/xssnick/tonutils-go/blob/master/example/account-state/main.go

Transactions in a block in the past

Hey Nick, I'm new to Ton Blockchain. Can I ask have you implemented a method to get all transactions of an exact block yet? With go-ethereum it looks like : block, err := client.BlockByNumber(context.Background(), big.NewInt(3000000)).
Hope I will get your answer soon :) thank you very much for this library.

Support wallet V3R1

Hey there,
is it possible to add support for V3R1 wallets? now only V3R2 implemented

case V3:
data = cell.BeginCell().
MustStoreUInt(0, 32). // seqno
MustStoreUInt(uint64(subWallet), 32). // sub wallet
MustStoreSlice(pubKey, 256).
EndCell()
case V4R2:
data = cell.BeginCell().
MustStoreUInt(0, 32). // seqno
MustStoreUInt(uint64(subWallet), 32).
MustStoreSlice(pubKey, 256).
MustStoreDict(nil). // empty dict of plugins
EndCell()
case HighloadV2R2:
data = cell.BeginCell().
MustStoreUInt(uint64(subWallet), 32).
MustStoreUInt(0, 64). // last cleaned
MustStoreSlice(pubKey, 256).
MustStoreDict(nil). // old queries
EndCell()

too small slice for this size

client := liteclient.NewClient()

err := client.Connect(context.Background(),
	"67.207.74.182:4924",
	"peJTw/arlRfssgTuf9BMypJzqOi7SXEqSPSWiEw2U1M=")
if err != nil {
	panic(err)
}
api := ton.NewAPIClient(client)
block, err := api.GetBlockInfo(context.Background())
if err != nil {
	Log.Fatalln("get block err:", err.Error())
	return
}

_, err = api.RunGetMethod(context.Background(), block, address.MustParseAddr("EQCunKWfEsKkdhSXZAEWDpck8rSvcIjV_V_6CBA6UsI0XRBu"), "get_nft_data")
if err != nil {
	Log.Errorf("run get method err: %s", err.Error())
	return
}
panic: too small slice for this size
goroutine 1 [running]:
github.com/xssnick/tonutils-go/tvm/cell.(*Builder).MustStoreSlice(...)
        .../github.com/xssnick/[email protected]/tvm/cell/builder.go:134
github.com/xssnick/tonutils-go/ton.(*APIClient).RunGetMethod(0xc000119f40, {0x70aec8, 0xc000016050}, 0xc000194140, 0xc0001820a0, {0x6d1222, 0xc}, {0x0, 0x0, 0x0})
        .../github.com/xssnick/[email protected]/ton/api.go:253 +0x1745
main.main()
        .../test/lite-client.go:29 +0x205
> runmethod EQCunKWfEsKkdhSXZAEWDpck8rSvcIjV_V_6CBA6UsI0XRBu get_nft_data
[ 3][t 1][2022-04-29 00:52:24.707931189][lite-client.cpp:1324][!testnode]       requesting remote get-method execution for 0:AE9CA59F12C2A47614976401160E9724F2B4AF7088D5FD5FFA08103A52C2345D with respect to (-1,8000000000000000,20168059):A348D3D666FF09B2FAEC3F06419F7D9CA9F1B6CC4FF62D0E865C8040D05994CB:F14E06284BC7A8F525EC1BE4210A7D56EE9FECF58AB8466BB6F48D2FAB75D489 to run method get_nft_data with 0 parameters
[ 3][t 2][2022-04-29 00:52:24.767547252][lite-client.cpp:1998][!testnode]       got (partial) account state (637 bytes) with mode=31 for 0:AE9CA59F12C2A47614976401160E9724F2B4AF7088D5FD5FFA08103A52C2345D with respect to blocks (-1,8000000000000000,20168059):A348D3D666FF09B2FAEC3F06419F7D9CA9F1B6CC4FF62D0E865C8040D05994CB:F14E06284BC7A8F525EC1BE4210A7D56EE9FECF58AB8466BB6F48D2FAB75D489 and (0,8000000000000000,25326017):6FEB8CDE0C810A3CBC4FEEC5685103FD5FCEF48B7DDCEAF320668D153369796D:6DA394AC82E92EAD9F9770796F9260CB5B3C2CD2A91AEA2DEB075C5CC8AA6BF9
[ 3][t 2][2022-04-29 00:52:24.767826707][lite-client.cpp:2095][!testnode]       starting VM to run method `get_nft_data` (102351) of smart contract 0:AE9CA59F12C2A47614976401160E9724F2B4AF7088D5FD5FFA08103A52C2345D
arguments:  [ 102351 ]
result:  [ -1 0 CS{Cell{0295000000000000000080051eec1b065126304253957c14810c949f9902ec722d1aa47adb690497b32f7cd0027e457611181313ddf9812a3ef5d3b9dcf80c7f1bdb9ff8d57fc9e74a3ce93c46} bits: 64..331; refs: 0..0} CS{Cell{0295000000000000000080051eec1b065126304253957c14810c949f9902ec722d1aa47adb690497b32f7cd0027e457611181313ddf9812a3ef5d3b9dcf80c7f1bdb9ff8d57fc9e74a3ce93c46} bits: 331..598; refs: 0..0} C{62EDF0EE80EF8562B380870C568CB4965EBED8FFF0308548C263FB9FAD023020} ]
remote result (not to be trusted):  [ -1 0 CS{Cell{0295000000000000000080051eec1b065126304253957c14810c949f9902ec722d1aa47adb690497b32f7cd0027e457611181313ddf9812a3ef5d3b9dcf80c7f1bdb9ff8d57fc9e74a3ce93c46} bits: 64..331; refs: 0..0} CS{Cell{0295000000000000000080051eec1b065126304253957c14810c949f9902ec722d1aa47adb690497b32f7cd0027e457611181313ddf9812a3ef5d3b9dcf80c7f1bdb9ff8d57fc9e74a3ce93c46} bits: 331..598; refs: 0..0} C{62EDF0EE80EF8562B380870C568CB4965EBED8FFF0308548C263FB9FAD023020} ]
[ 3][t 2][2022-04-29 00:52:24.767973694][lite-client.cpp:1225][!testnode]       caching cell 62EDF0EE80EF8562B380870C568CB4965EBED8FFF0308548C263FB9FAD023020

"Failed to parse payload: recursive reference of cells" on "cell.FromBOC" call

Hello. I get error Failed to parse payload: recursive reference of cells when deserializing stateinit from mytonwallet (ton connect).
Error occurs on the call cell.FromBOC.
I believe this is a bug, because I was able to successfully parse this stateinit using tongo library as shown in the example.

Tonutils version: v1.8.9

Example:

package main

import (
  "encoding/base64"
  "fmt"
  "log"

  "github.com/tonkeeper/tongo/tonconnect"
  "github.com/xssnick/tonutils-go/tvm/cell"
)

func main() {
  // address
  // UQBe7yULTjbWZszDK2d8LOePumy9ZtGzwQgrXXfqjR1yM2fy

  // stateInit from mytonwallet
  stateInit := "te6ccsECFgEAAwQAAAAABQAwAD0AQgDEAMsBAwE9AXYBewGAAa8BtAG/AcQByQHYAecCCAJ/AsYCATQCAQBRAAAAACmpoxf4lk0qBZddWkKNjjqmudjY6QDlccYne0gS9zCGDH3x2kABFP8A9KQT9LzyyAsDAgEgCQQE+PKDCNcYINMf0x/THwL4I7vyZO1E0NMf0x/T//QE0VFDuvKhUVG68qIF+QFUEGT5EPKj+AAkpMjLH1JAyx9SMMv/UhD0AMntVPgPAdMHIcAAn2xRkyDXSpbTB9QC+wDoMOAhwAHjACHAAuMAAcADkTDjDQOkyMsfEssfy/8IBwYFAAr0AMntVABsgQEI1xj6ANM/MFIkgQEI9Fnyp4IQZHN0cnB0gBjIywXLAlAFzxZQA/oCE8tqyx8Syz/Jc/sAAHCBAQjXGPoA0z/IVCBHgQEI9FHyp4IQbm90ZXB0gBjIywXLAlAGzxZQBPoCFMtqEssfyz/Jc/sAAgBu0gf6ANTUIvkABcjKBxXL/8nQd3SAGMjLBcsCIs8WUAX6AhTLaxLMzMlz+wDIQBSBAQj0UfKnAgIBSBMKAgEgDAsAWb0kK29qJoQICga5D6AhhHDUCAhHpJN9KZEM5pA+n/mDeBKAG3gQFImHFZ8xhAIBIA4NABG4yX7UTQ1wsfgCAVgSDwIBIBEQABmvHfaiaEAQa5DrhY/AABmtznaiaEAga5Drhf/AAD2ynftRNCBAUDXIfQEMALIygfL/8nQAYEBCPQKb6ExgAubQAdDTAyFxsJJfBOAi10nBIJJfBOAC0x8hghBwbHVnvSKCEGRzdHK9sJJfBeAD+kAwIPpEAcjKB8v/ydDtRNCBAUDXIfQEMFyBAQj0Cm+hMbOSXwfgBdM/yCWCEHBsdWe6kjgw4w0DghBkc3RyupJfBuMNFRQAilAEgQEI9Fkw7UTQgQFA1yDIAc8W9ADJ7VQBcrCOI4IQZHN0coMesXCAGFAFywVQA88WI/oCE8tqyx/LP8mAQPsAkl8D4gB4AfoA9AQw+CdvIjBQCqEhvvLgUIIQcGx1Z4MesXCAGFAEywUmzxZY+gIZ9ADLaRfLH1Jgyz8gyYBA+wAGdYbenA=="

  _, err := tonconnect.ParseStateInit(stateInit)
  if err != nil {
    log.Fatal(err) 
  }
    // ok
  fmt.Println("parsed 1")

  stateInitBytes, err := base64.StdEncoding.DecodeString(stateInit)
  if err != nil {
    log.Fatal(err)
  }

  _, err = cell.FromBOC(stateInitBytes)
  if err != nil {
    log.Fatal(err) // failed to parse payload: recursive reference of cells
  }
  fmt.Println("parsed 2")

}

failed to check proof: should have 2 roots

Some litenodes were updated in testnet yesterday and now we get this error on GetBlockShardsInfo():
v.1.8.9

failed to check proof: should have 2 roots

v1.9.0 return timeout

Could you please take a look?

Issue sending JETTON USDT - "failed to run get_wallet_address method: contract exit code: -256 (contract is not initialized)"

Context

  • I am hoping to have a script that sends USDT to other users (New and Old wallets)
  • However, when I am trying to do that, I got this weird error

Logs

{"time":"2024-06-11T00:45:29.191189+08:00","level":"ERROR","msg":"error sending USDT","error":"failed to run get_wallet_address method: contract exit code: -256 (contract is not initialized)"}

Error Message

failed to run get_wallet_address method: contract exit code: -256 (contract is not initialized

Code

	seeds := strings.Split(mnemonic, " ")
	w, err := wallet.FromSeed(api, seeds, wallet.V4R2)
	if err != nil {
		return nil, err
	}

        destAddr, err := address.ParseAddr(to)
	if err != nil {
		return nil, err
	}

	token := jetton.NewJettonMasterClient(c.api, address.MustParseAddr(contract.ContractAddress))

	// find our jetton wallet
        // where the error happens and I am not sure why? 
        // it used to work :( 
	tokenWallet, err := token.GetJettonWallet(ctx, w.WalletAddress())
	if err != nil {
		return nil, err
	}

	tokenBalance, err := tokenWallet.GetBalance(ctx)
	if err != nil {
		return nil, err
	}

	amountTokens, err := tlb.FromDecimal(amount, 0)
	if err != nil {
		return nil, err
	}

	if tokenBalance.Int64() < amountTokens.Nano().Int64() {
		slog.Warn("token balance", "balance", tokenBalance.Int64(), "amount", amountTokens.Nano().Int64())
		return nil, ErrHotWalletInsufficientBalance
	}

	comment, err := wallet.CreateCommentCell(reqID)
	if err != nil {
		return nil, err
	}

	// address of receiver's wallet (not token wallet, just usual)
	transferPayload, err := tokenWallet.BuildTransferPayloadV2(destAddr, destAddr, amountTokens, tlb.ZeroCoins, comment, nil)
	if err != nil {
		return nil, err
	}

	msg := wallet.SimpleMessage(tokenWallet.Address(), amountTokens, transferPayload)

	tx, _, err := w.SendWaitTransaction(ctx, msg)
	if err != nil {
		return nil, err
	}

	return tx, nil

How can we get all transaction fees?

Hi,
How can we get all transaction fees like it declared in tonlib_api.tl ?

raw.transaction address:accountAddress utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 storage_fee:int64 other_fee:int64 in_msg:raw.message out_msgs:vector = raw.Transaction;

While using tonlib client we always can get fee:int64 storage_fee:int64 other_fee:int64, but tonutils lib provides only TotalFees or I didn't find...

Could you please clarify this moment for me?

parse Tact map

How to parse the map in Tact using Golang when I call get method
ts script:
async getMap(provider: ContractProvider) {
let builder = new TupleBuilder();
let source = (await provider.get('map', builder.build())).stack;
let result = Dictionary.loadDirect(Dictionary.Keys.BigInt(257), Dictionary.Values.BigInt(257), source.readCellOpt());
return result;
}

Fix policy ProofCheckPolicyFast with multiple existing shards

Hey! Could you please check, current example is not work. Probably this happened because multiple shard became alive in mainnet. I found out that error appears in this switch case with zero value in ab variable. Could you please research it and maybe adding another case to this switch case would fix the problem? And then I switched mode to ProofCheckPolicyUnsafe, checks was skipped and everything works well. Is it safe to use ProofCheckPolicyUnsafe?

always get "Transfer err:failed to get account state: adnl request timeout, node 185.86.79.9:4701",why this happen?

here is my code

var messages []*wallet.Message
messages = append(messages, &wallet.Message{
    Mode: 1,
    InternalMessage: &tlb.InternalMessage{
    Bounce:  false,
    DstAddr: addr,
    Amount:  amount,
    Body:    comment,
  },
})
// send tx and wait for it
tx, block, err := w.SendManyWaitTransaction(context.Background(), messages)
if err != nil {
	global.LOG.Sugar().Error("Transfer err:", err.Error())
	return err
}

如何设置excess地址

` token := jetton.NewJettonMasterClient(api, address.MustParseAddr(contractAddrStr))

	// find our jetton wallet
	tokenWallet, err := token.GetJettonWallet(context.Background(), wallet.WalletAddress())
	if err != nil {
		return nil, err
	}
	comment, err := pkgTonWallet.CreateCommentCell(memo)
	if err != nil {
		return nil, err
	}
	transferPayload, err := tokenWallet.BuildTransferPayloadV2(address.MustParseAddr(toStr), address.MustParseAddr(responseToStr), tlb.MustFromNano(value, tokenDecimal), tlb.MustFromTON("0.000000001"), comment, nil)
	if err != nil {
		return nil, err
	}
	unsignedTx = pkgTonWallet.SimpleMessage(tokenWallet.Address(), tlb.MustFromTON("0.05"), transferPayload)

`

构建交易,发现excess地址永远是jetton收款地址(to),如何将excess设置成发送方地址(from)?

Add new real world usage examples

More examples = easier to understand.

The task is to add more real world examples, DEX Swap (dedust for example), NFT put on sale, DNS auction, NFT sale buy and etc. For the contract message serialization please use struct with tlb tags, for the cleaner cell building (example).

If you want to add an example, leave a comment here with a small description about what you are going to add, i'll approve it with (👍) and you can start. This task can be done by many developers (each one can add own example).

When you are done, just make PR to master branch, and leave a comment here.

GetBalance Error

An error occurred when I tried to parse out the address through the mnemonic phrase and obtain the address balance.

I tried using the mainnet configuration file, and errors occurred with the specified node.

2024/01/31 22:11:19 GetBalance err: failed to get account state: lite server error, code 651: too big masterchain block seqno exit status 1

This is why?

how to parse jetton transaction to get jetton token amount and comment?

when transfer jetton, i noticed the jetton amount and comment has been included in transferPayload then included into message body, my purpose is to parse tx from ListTransactions, this sdk is not supported to get jetton amount and comment, so i try to parse message body to find out ,i can get the message body by 'body:=tx.IO.In.AsInternal().Body', but how to parse the body to readable text?

Optimize raptorq/discmath package and cover with tests

RaptorQ package (used for forward error correction encoding) is used by RLDP protocol, which is one of the building blocks of TON communication, it is used by such components as Storage, Proxy, Nodes. Current implementation is not so efficient as it could be, for example maps usage can be replaced to arrays/slices.

The task is to optimize raptorq+discmath packages, cover them with unit tests and add benchmarks. Please don't use CGO or external libraries.

You could start from benchmarks to understand current behaviour and speed. Usage example can be found in rldp package.

If you want to take the task, leave a comment here (time estimation will be a plus). When you are done, just make PR to dev-v19 branch, and leave a comment here.

Accessing validator-engine-console with ADNL client

I am trying to use ConnectionPool for accessing validator-engine-console with more or less the following code:

tl.Register(ValidatorStats{}, "engine.validator.getStats = engine.validator.Stats")
...
client := liteclient.NewConnectionPool()

// Read key files etc...

err = client.AddConnection(
	context.Background(),
	"NODE_IP:PORT",
	serverKeyPubB64,
	clinetKeyFull,
)
if err != nil {
	panic(err)
}
fmt.Println("Connected to server")

var resp tl.Serializable
err = client.QueryADNL(context.Background(), ValidatorStats{}, &resp)
if err != nil {
	panic(err)
}
...

What I am getting in response is engine.validator.ControlQueryError with Code:602 Message:forbidden.

Running validator-engine-console against the same host with the same keys works fine.

So my question is if it is possible to use the library to access validator-engine-console, or is it strictly specific to lite server? If so if you could point me to some resources to understand the specificity I would be very grateful (I am not proficient enough in c++ to figure it out quickly from Ton codebase).

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.