Skip to content

Commit

Permalink
Merge pull request #47 from xssnick/rc
Browse files Browse the repository at this point in the history
RC, v1 preparation
  • Loading branch information
xssnick authored Jul 16, 2022
2 parents 469af68 + 527718b commit 4a4adaa
Show file tree
Hide file tree
Showing 67 changed files with 3,131 additions and 1,113 deletions.
69 changes: 49 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,56 @@
# tonutils-go
![Coverage](https://img.shields.io/badge/Coverage-64.6%25-yellow)
[![Based on TON][ton-svg]][ton]
![Coverage](https://img.shields.io/badge/Coverage-71.5%25-brightgreen)

Golang library for interacting with TON blockchain.

This library is native golang implementation of ADNL and lite protocol. It works like connection pool and can be connected to multiple lite servers in the same time, balancing is done on lib side.
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.

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

If you have any questions or suggestions you can join our Telegram chat https://t.me/tonutils
If you love this library and want to support its development you can donate any amount of coins to this ton address ☺️
`EQBx6tZZWa2Tbv6BvgcvegoOQxkRrVaBVwBOoW85nbP37_Go`

**This library is under active development**, so more cool features will come soon! Also, if you have some idea of useful functionality, just open issue with description or even pull request!
### How to use
- [Connection](#Connection)
- [Wallet](#Wallet)
- [Transfer](#Wallet)
- [Balance](#Wallet)
- [Accounts](#Account-info-and-transactions)
- [List transactions](#Account-info-and-transactions)
- [Contracts](#Contracts)
- [Use get methods](#Using-GET-methods)
- [Send external message](#Send-external-message)
- [Cells](#Cells)
- [Create](#Cells)
- [Parse](#Cells)
- [TLB Loader](#TLB-Loader)
- [Custom reconnect policy](#Custom-reconnect-policy)
- [Features to implement](#Features-to-implement)

## How to use
You can find full usage examples in **example** directory

You can find usage examples in **[example](https://github.com/xssnick/tonutils-go/tree/master/example)** directory

You also can join our **[Telegram group](https://t.me/tonutils)** and ask any questions :)

### Connection
You can get list of public lite servers from official TON configs:
* Mainnet - https://ton-blockchain.github.io/global.config.json
* Testnet - https://ton-blockchain.github.io/testnet-global.config.json
* Mainnet - `https://ton-blockchain.github.io/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:
```golang
// initialize new client
client := liteclient.NewClient()
// connect to lite server, can be connected to multiple servers in the same time
err := client.Connect(context.Background(),
"65.21.74.140:46427",
"JhXt7H1dZTgxQTIyGiYV4f9VUARuDxFl/1kVBjLSMB8=")
client := liteclient.NewConnectionPool()

configUrl := "https://ton-blockchain.github.io/testnet-global.config.json"
err := client.AddConnectionsFromConfigUrl(context.Background(), configUrl)
if err != nil {
panic(err)
}

// initialize ton api lite connection wrapper
api := ton.NewAPIClient(client)
```
### Wallet
Expand Down Expand Up @@ -66,7 +81,7 @@ if balance.NanoTON().Uint64() >= 3000000 {
}
```
You can find full working example at `example/wallet/main.go`
### Interacting with contracts
### Contracts
Here are the description of features which allow us to trigger contract's methods

#### Using GET methods
Expand Down Expand Up @@ -201,7 +216,13 @@ result := builder.EndCell()
// }

fmt.Println(result.Dump())
```

Load from cell:
```golang
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
Expand Down Expand Up @@ -254,8 +275,16 @@ client.SetOnDisconnect(func(addr, serverKey string) {
* ✅ Get transactions
* ✅ Deploy contracts
* ✅ Wallet operations
* Payment processing
* ✅ Cell dictionaries support
* ✅ MustLoad methods
* ✅ Parse global config json
* Event subscriptions
* Parse global config json
* Payment channels
* DNS
* Merkle proofs


<!-- Badges -->
[ton-svg]: https://img.shields.io/badge/Based%20on-TON-blue
[ton]: https://ton.org
[tg-chat]: https://t.me/tonutils
9 changes: 4 additions & 5 deletions address/addr.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"fmt"

"github.com/sigurn/crc16"
"github.com/xssnick/tonutils-go/utils"
)

type Address struct {
Expand Down Expand Up @@ -68,18 +67,18 @@ func (a *Address) FlagsToByte() (flags byte) {
// TODO check this magic...
flags = 0b00010001
if !a.flags.bounceable {
utils.SetBit(&flags, 6)
setBit(&flags, 6)
}
if a.flags.testnet {
utils.SetBit(&flags, 7)
setBit(&flags, 7)
}
return flags
}

func parseFlags(data byte) flags {
return flags{
bounceable: !utils.HasBit(data, 6),
testnet: utils.HasBit(data, 7),
bounceable: !hasBit(data, 6),
testnet: hasBit(data, 7),
}
}

Expand Down
17 changes: 17 additions & 0 deletions address/bit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package address

// TODO add length checks and panic on errors

func setBit(n *byte, pos uint) {
*n |= 1 << pos
}

func clearBit(n *byte, pos uint) {
mask := ^(1 << pos)
*n &= byte(mask)
}

func hasBit(n byte, pos uint) bool {
val := n & (1 << pos)
return val > 0
}
8 changes: 4 additions & 4 deletions utils/bit_test.go → address/bit_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package utils
package address

import "testing"

Expand All @@ -15,7 +15,7 @@ func TestClearBit(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ClearBit(tt.args.n, tt.args.pos)
clearBit(tt.args.n, tt.args.pos)
})
}
}
Expand All @@ -42,7 +42,7 @@ func TestHasBit(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := HasBit(tt.args.n, tt.args.pos); got != tt.want {
if got := hasBit(tt.args.n, tt.args.pos); got != tt.want {
t.Errorf("HasBit() = %v, want %v", got, tt.want)
}
})
Expand All @@ -62,7 +62,7 @@ func TestSetBit(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
SetBit(tt.args.n, tt.args.pos)
setBit(tt.args.n, tt.args.pos)
})
}
}
13 changes: 8 additions & 5 deletions example/account-state/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import (
)

func main() {
client := liteclient.NewClient()
client := liteclient.NewConnectionPool()

// connect to mainnet lite server
err := client.Connect(context.Background(), "135.181.140.212:13206", "K0t3+IWLOXHYMvMcrGZDPs+pn58a17LFbnXoQkKc2xw=")
err := client.AddConnection(context.Background(), "135.181.140.212:13206", "K0t3+IWLOXHYMvMcrGZDPs+pn58a17LFbnXoQkKc2xw=")
if err != nil {
log.Fatalln("connection err: ", err.Error())
return
Expand All @@ -23,16 +23,19 @@ func main() {
// initialize ton api lite connection wrapper
api := ton.NewAPIClient(client)

// if we want to route all requests to the same node, we can use it
ctx := client.StickyContext(context.Background())

// we need fresh block info to run get methods
b, err := api.GetMasterchainInfo(context.Background())
b, err := api.GetMasterchainInfo(ctx)
if err != nil {
log.Fatalln("get block err:", err.Error())
return
}

addr := address.MustParseAddr("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N")

res, err := api.GetAccount(context.Background(), b, addr)
res, err := api.GetAccount(ctx, b, addr)
if err != nil {
log.Fatalln("get account err:", err.Error())
return
Expand All @@ -59,7 +62,7 @@ func main() {
}

// load transactions in batches with size 15
list, err := api.ListTransactions(context.Background(), addr, 15, lastLt, lastHash)
list, err := api.ListTransactions(ctx, addr, 15, lastLt, lastHash)
if err != nil {
log.Printf("send err: %s", err.Error())
return
Expand Down
6 changes: 3 additions & 3 deletions example/block-scan/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import (

"github.com/xssnick/tonutils-go/address"
"github.com/xssnick/tonutils-go/liteclient"
"github.com/xssnick/tonutils-go/liteclient/tlb"
"github.com/xssnick/tonutils-go/tlb"
"github.com/xssnick/tonutils-go/ton"
)

func main() {
client := liteclient.NewClient()
client := liteclient.NewConnectionPool()

// connect to mainnet lite server
err := client.Connect(context.Background(), "135.181.140.212:13206", "K0t3+IWLOXHYMvMcrGZDPs+pn58a17LFbnXoQkKc2xw=")
err := client.AddConnection(context.Background(), "135.181.140.212:13206", "K0t3+IWLOXHYMvMcrGZDPs+pn58a17LFbnXoQkKc2xw=")
if err != nil {
log.Fatalln("connection err: ", err.Error())
return
Expand Down
20 changes: 14 additions & 6 deletions example/detailed/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ import (
)

func main() {
client := liteclient.NewClient()
client := liteclient.NewConnectionPool()

servers := map[string]string{
"65.21.74.140:46427": "JhXt7H1dZTgxQTIyGiYV4f9VUARuDxFl/1kVBjLSMB8=",
"135.181.140.212:13206": "K0t3+IWLOXHYMvMcrGZDPs+pn58a17LFbnXoQkKc2xw=",
// ...
}

// we can connect to any number of servers, load balancing is done inside library
for addr, key := range servers {
// connect to testnet lite server
err := client.Connect(context.Background(), addr, key)
// connect to lite server
err := client.AddConnection(context.Background(), addr, key)
if err != nil {
log.Fatalln("connection err: ", err.Error())
return
Expand All @@ -48,7 +48,7 @@ func main() {
}
*/

res, err := api.RunGetMethod(context.Background(), b, address.MustParseAddr("kQB3P0cDOtkFDdxB77YX-F2DGkrIszmZkmyauMnsP1gg0inM"), "mult", 7, 8)
res, err := api.RunGetMethod(context.Background(), b, address.MustParseAddr("kQBL2_3lMiyywU17g-or8N7v9hDmPCpttzBPE2isF2GTziky"), "mult", 7, uint64(8))
if err != nil {
log.Fatalln("run get method err:", err.Error())
return
Expand All @@ -59,14 +59,22 @@ func main() {

for _, c := range res {
switch res := c.(type) {
case *cell.Cell: // it is used for slice also
case *cell.Cell:
sz, payload, err := res.BeginParse().RestBits()
if err != nil {
println("ERR", err.Error())
return
}

fmt.Printf("RESP CELL %d bits, hex(%s)\n", sz, hex.EncodeToString(payload))
case *cell.Slice:
sz, payload, err := res.RestBits()
if err != nil {
println("ERR", err.Error())
return
}

fmt.Printf("RESP SLICE %d bits, hex(%s)\n", sz, hex.EncodeToString(payload))
case *big.Int:
fmt.Println("RESP BIG INT", res.Uint64())
case uint64:
Expand Down
20 changes: 12 additions & 8 deletions example/external-message/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/xssnick/tonutils-go/address"
"github.com/xssnick/tonutils-go/liteclient"
"github.com/xssnick/tonutils-go/tlb"
"github.com/xssnick/tonutils-go/ton"
"github.com/xssnick/tonutils-go/tvm/cell"
)
Expand Down Expand Up @@ -40,10 +41,10 @@ Or you can at least add some coins to contract address
*/

func main() {
client := liteclient.NewClient()
client := liteclient.NewConnectionPool()

// connect to testnet lite server
err := client.Connect(context.Background(), "65.21.74.140:46427", "JhXt7H1dZTgxQTIyGiYV4f9VUARuDxFl/1kVBjLSMB8=")
// connect to mainnet lite server
err := client.AddConnection(context.Background(), "135.181.140.212:13206", "K0t3+IWLOXHYMvMcrGZDPs+pn58a17LFbnXoQkKc2xw=")
if err != nil {
log.Fatalln("connection err: ", err.Error())
return
Expand All @@ -60,23 +61,26 @@ func main() {
}

// call method to get seqno of contract
res, err := api.RunGetMethod(context.Background(), block, address.MustParseAddr("kQBkh8dcas3_OB0uyFEDdVBBSpAWNEgdQ66OYF76N4cDXAFQ"), "get_total")
res, err := api.RunGetMethod(context.Background(), block, address.MustParseAddr("kQBL2_3lMiyywU17g-or8N7v9hDmPCpttzBPE2isF2GTziky"), "get_total")
if err != nil {
log.Fatalln("run get method err:", err.Error())
return
}

seqno := res[0].(uint64)
total := res[1].(uint64)
seqno := res[0].(int64)
total := res[1].(int64)

log.Printf("Current seqno = %d and total = %d", seqno, total)

data := cell.BeginCell().
MustStoreUInt(seqno, 64).
MustStoreInt(seqno, 64).
MustStoreUInt(1, 16). // add 1 to total
EndCell()

err = api.SendExternalMessage(context.Background(), address.MustParseAddr("kQBkh8dcas3_OB0uyFEDdVBBSpAWNEgdQ66OYF76N4cDXAFQ"), data)
err = api.SendExternalMessage(context.Background(), &tlb.ExternalMessage{
DstAddr: address.MustParseAddr("kQBL2_3lMiyywU17g-or8N7v9hDmPCpttzBPE2isF2GTziky"),
Body: data,
})
if err != nil {
// FYI: it can fail if not enough balance on contract
log.Printf("send err: %s", err.Error())
Expand Down
8 changes: 4 additions & 4 deletions example/nft-info/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import (
)

func main() {
client := liteclient.NewClient()
client := liteclient.NewConnectionPool()

// connect to mainnet lite server
err := client.Connect(context.Background(), "135.181.140.212:13206", "K0t3+IWLOXHYMvMcrGZDPs+pn58a17LFbnXoQkKc2xw=")
err := client.AddConnection(context.Background(), "135.181.140.212:13206", "K0t3+IWLOXHYMvMcrGZDPs+pn58a17LFbnXoQkKc2xw=")
if err != nil {
log.Fatalln("connection err: ", err.Error())
return
Expand All @@ -38,13 +38,13 @@ func main() {
return
}

collectionAddr, err := res[2].(*cell.Cell).BeginParse().LoadAddr()
collectionAddr, err := res[2].(*cell.Slice).LoadAddr()
if err != nil {
log.Fatalln("addr err:", err.Error())
return
}

ownerAddr, err := res[3].(*cell.Cell).BeginParse().LoadAddr()
ownerAddr, err := res[3].(*cell.Slice).LoadAddr()
if err != nil {
log.Fatalln("addr err:", err.Error())
return
Expand Down
Loading

0 comments on commit 4a4adaa

Please sign in to comment.