Skip to content

Commit 1a9b826

Browse files
authored
Merge pull request #218 from xssnick/dev-v19
v1.9.9
2 parents c0b96b2 + 2ec0b5d commit 1a9b826

File tree

15 files changed

+379
-105
lines changed

15 files changed

+379
-105
lines changed

address/addr.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package address
22

33
import (
4+
"bytes"
45
"encoding/base64"
56
"encoding/binary"
67
"encoding/hex"
@@ -337,3 +338,7 @@ func (a *Address) Workchain() int32 {
337338
func (a *Address) Data() []byte {
338339
return a.data
339340
}
341+
342+
func (a *Address) Equals(b *Address) bool {
343+
return a.workchain == b.workchain && bytes.Equal(a.data, b.data)
344+
}

example/accept-payments/main.go

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/xssnick/tonutils-go/liteclient"
77
"github.com/xssnick/tonutils-go/tlb"
88
"github.com/xssnick/tonutils-go/ton"
9+
"github.com/xssnick/tonutils-go/ton/jetton"
910
"log"
1011
)
1112

@@ -26,19 +27,17 @@ func main() {
2627
}
2728

2829
// initialize ton api lite connection wrapper with full proof checks
29-
api := ton.NewAPIClient(client, ton.ProofCheckPolicySecure).WithRetry()
30+
api := ton.NewAPIClient(client, ton.ProofCheckPolicyFast).WithRetry()
3031
api.SetTrustedBlockFromConfig(cfg)
3132

32-
log.Println("fetching and checking proofs since config init block, it may take near a minute...")
3333
master, err := api.CurrentMasterchainInfo(context.Background()) // we fetch block just to trigger chain proof check
3434
if err != nil {
3535
log.Fatalln("get masterchain info err: ", err.Error())
3636
return
3737
}
38-
log.Println("master proof checks are completed successfully, now communication is 100% safe!")
3938

4039
// address on which we are accepting payments
41-
treasuryAddress := address.MustParseAddr("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N")
40+
treasuryAddress := address.MustParseAddr("EQAYqo4u7VF0fa4DPAebk4g9lBytj2VFny7pzXR0trjtXQaO")
4241

4342
acc, err := api.GetAccount(context.Background(), master, treasuryAddress)
4443
if err != nil {
@@ -58,10 +57,38 @@ func main() {
5857

5958
log.Println("waiting for transfers...")
6059

60+
// USDT master contract addr, but can be any jetton
61+
usdt := jetton.NewJettonMasterClient(api, address.MustParseAddr("EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs"))
62+
// get our jetton wallet address
63+
treasuryJettonWallet, err := usdt.GetJettonWalletAtBlock(context.Background(), treasuryAddress, master)
64+
if err != nil {
65+
log.Fatalln("get jetton wallet address err: ", err.Error())
66+
return
67+
}
68+
6169
// listen for new transactions from channel
6270
for tx := range transactions {
63-
// process transaction here
64-
log.Println(tx.String())
71+
// only internal messages can increase the balance
72+
if tx.IO.In != nil && tx.IO.In.MsgType == tlb.MsgTypeInternal {
73+
ti := tx.IO.In.AsInternal()
74+
src := ti.SrcAddr
75+
76+
// verify that event sender is our jetton wallet
77+
if ti.SrcAddr.Equals(treasuryJettonWallet.Address()) {
78+
var transfer jetton.TransferNotification
79+
if err = tlb.LoadFromCell(&transfer, ti.Body.BeginParse()); err == nil {
80+
// convert decimals to 6 for USDT (it can be fetched from jetton details too), default is 9
81+
amt := tlb.MustFromNano(transfer.Amount.Nano(), 6)
82+
83+
// reassign sender to real jetton sender instead of its jetton wallet contract
84+
src = transfer.Sender
85+
log.Println("received", amt.String(), "USDT from", src.String())
86+
}
87+
}
88+
89+
// show received ton amount
90+
log.Println("received", ti.Amount.String(), "TON from", src.String())
91+
}
6592

6693
// update last processed lt and save it in db
6794
lastProcessedLT = tx.LT

example/deploy-nft-collection/main.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,15 @@ func main() {
4545

4646
func getWallet(api ton.APIClientWrapped) *wallet.Wallet {
4747
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", " ")
48-
w, err := wallet.FromSeed(api, words, wallet.V3)
48+
w, err := wallet.FromSeed(api, words, wallet.V4R2)
4949
if err != nil {
5050
panic(err)
5151
}
5252
return w
5353
}
5454

5555
func getNFTCollectionCode() *cell.Cell {
56-
var hexBOC = "b5ee9c72410213010001fe000114ff00f4a413f4bcf2c80b0102016204020201200e030025bc82df6a2687d20699fea6a6a182de86a182c40202cd0a050201200706003d45af0047021f005778018c8cb0558cf165004fa0213cb6b12ccccc971fb0080201200908001b3e401d3232c084b281f2fff27420002d007232cffe0a33c5b25c083232c044fd003d0032c0326003ebd10638048adf000e8698180b8d848adf07d201800e98fe99ff6a2687d20699fea6a6a184108349e9ca829405d47141baf8280e8410854658056b84008646582a802e78b127d010a65b509e58fe59f80e78b64c0207d80701b28b9e382f970c892e000f18112e001718119026001f1812f82c207f97840d0c0b002801fa40304144c85005cf1613cb3fccccccc9ed5400a6357003d4308e378040f4966fa5208e2906a4208100fabe93f2c18fde81019321a05325bbf2f402fa00d43022544b30f00623ba9302a402de04926c21e2b3e6303250444313c85005cf1613cb3fccccccc9ed5400603502d33f5313bbf2e1925313ba01fa00d43028103459f0068e1201a44343c85005cf1613cb3fccccccc9ed54925f05e2020120120f0201201110002db4f47da89a1f481a67fa9a9a86028be09e008e003e00b0002fb5dafda89a1f481a67fa9a9a860d883a1a61fa61ff4806100043b8b5d31ed44d0fa40d33fd4d4d43010245f04d0d431d430d071c8cb0701cf16ccc98f34ea10e"
56+
var hexBOC = "b5ee9c724102140100021f000114ff00f4a413f4bcf2c80b0102016202030202cd04050201200e0f04e7d10638048adf000e8698180b8d848adf07d201800e98fe99ff6a2687d20699fea6a6a184108349e9ca829405d47141baf8280e8410854658056b84008646582a802e78b127d010a65b509e58fe59f80e78b64c0207d80701b28b9e382f970c892e000f18112e001718112e001f181181981e0024060708090201200a0b00603502d33f5313bbf2e1925313ba01fa00d43028103459f0068e1201a44343c85005cf1613cb3fccccccc9ed54925f05e200a6357003d4308e378040f4966fa5208e2906a4208100fabe93f2c18fde81019321a05325bbf2f402fa00d43022544b30f00623ba9302a402de04926c21e2b3e6303250444313c85005cf1613cb3fccccccc9ed54002c323401fa40304144c85005cf1613cb3fccccccc9ed54003c8e15d4d43010344130c85005cf1613cb3fccccccc9ed54e05f04840ff2f00201200c0d003d45af0047021f005778018c8cb0558cf165004fa0213cb6b12ccccc971fb008002d007232cffe0a33c5b25c083232c044fd003d0032c03260001b3e401d3232c084b281f2fff2742002012010110025bc82df6a2687d20699fea6a6a182de86a182c40043b8b5d31ed44d0fa40d33fd4d4d43010245f04d0d431d430d071c8cb0701cf16ccc980201201213002fb5dafda89a1f481a67fa9a9a860d883a1a61fa61ff480610002db4f47da89a1f481a67fa9a9a86028be09e008e003e00b01a500c6e"
5757
codeCellBytes, _ := hex.DecodeString(hexBOC)
5858

5959
codeCell, err := cell.FromBOC(codeCellBytes)
@@ -65,7 +65,7 @@ func getNFTCollectionCode() *cell.Cell {
6565
}
6666

6767
func getNFTItemCode() *cell.Cell {
68-
var hexBOC = "b5ee9c7241020d010001d0000114ff00f4a413f4bcf2c80b0102016203020009a11f9fe0050202ce050402012008060201200907001d00f232cfd633c58073c5b3327b552000113e910c1c2ebcb85360003b3b513434cffe900835d27080269fc07e90350c04090408f80c1c165b5b6002d70c8871c02497c0f83434c0c05c6c2497c0f83e903e900c7e800c5c75c87e800c7e800c3c00812ce3850c1b088d148cb1c17cb865407e90350c0408fc00f801b4c7f4cfe08417f30f45148c2ea3a1cc840dd78c9004f80c0d0d0d4d60840bf2c9a884aeb8c097c12103fcbc200b0a00727082108b77173505c8cbff5004cf1610248040708010c8cb055007cf165005fa0215cb6a12cb1fcb3f226eb39458cf17019132e201c901fb0001f65135c705f2e191fa4021f001fa40d20031fa00820afaf0801ba121945315a0a1de22d70b01c300209206a19136e220c2fff2e192218e3e821005138d91c85009cf16500bcf16712449145446a0708010c8cb055007cf165005fa0215cb6a12cb1fcb3f226eb39458cf17019132e201c901fb00104794102a375be20c0082028e3526f0018210d53276db103744006d71708010c8cb055007cf165005fa0215cb6a12cb1fcb3f226eb39458cf17019132e201c901fb0093303234e25502f003cc82807e"
68+
var hexBOC = "b5ee9c7241020d010001d0000114ff00f4a413f4bcf2c80b0102016202030202ce04050009a11f9fe00502012006070201200b0c02d70c8871c02497c0f83434c0c05c6c2497c0f83e903e900c7e800c5c75c87e800c7e800c3c00812ce3850c1b088d148cb1c17cb865407e90350c0408fc00f801b4c7f4cfe08417f30f45148c2ea3a1cc840dd78c9004f80c0d0d0d4d60840bf2c9a884aeb8c097c12103fcbc20080900113e910c1c2ebcb8536001f65135c705f2e191fa4021f001fa40d20031fa00820afaf0801ba121945315a0a1de22d70b01c300209206a19136e220c2fff2e192218e3e821005138d91c85009cf16500bcf16712449145446a0708010c8cb055007cf165005fa0215cb6a12cb1fcb3f226eb39458cf17019132e201c901fb00104794102a375be20a00727082108b77173505c8cbff5004cf1610248040708010c8cb055007cf165005fa0215cb6a12cb1fcb3f226eb39458cf17019132e201c901fb000082028e3526f0018210d53276db103744006d71708010c8cb055007cf165005fa0215cb6a12cb1fcb3f226eb39458cf17019132e201c901fb0093303234e25502f003003b3b513434cffe900835d27080269fc07e90350c04090408f80c1c165b5b60001d00f232cfd633c58073c5b3327b5520bf75041b"
6969
codeCellBytes, _ := hex.DecodeString(hexBOC)
7070

7171
codeCell, err := cell.FromBOC(codeCellBytes)
@@ -86,9 +86,9 @@ func getContractData(collectionOwnerAddr, royaltyAddr *address.Address) *cell.Ce
8686
// = Storage;
8787

8888
royalty := cell.BeginCell().
89-
MustStoreUInt(50, 16). // 5% royalty
90-
MustStoreUInt(1000, 16).
91-
MustStoreAddr(royaltyAddr).
89+
MustStoreUInt(5, 16). // 5% royalty
90+
MustStoreUInt(100, 16). // denominator
91+
MustStoreAddr(royaltyAddr). // fee addr destination
9292
EndCell()
9393

9494
// collection data

example/nft-mint/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func main() {
7070

7171
func getWallet(api *ton.APIClient) *wallet.Wallet {
7272
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", " ")
73-
w, err := wallet.FromSeed(api, words, wallet.V3)
73+
w, err := wallet.FromSeed(api, words, wallet.V4R2)
7474
if err != nil {
7575
panic(err)
7676
}

tlb/transaction.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package tlb
22

33
import (
4+
"encoding/hex"
45
"fmt"
56
"math/big"
67
"reflect"
@@ -290,17 +291,20 @@ func (t *Transaction) String() string {
290291
case TransactionDescriptionOrdinary:
291292
}
292293
if t.IO.In != nil {
294+
build += fmt.Sprintf("LT: %d", t.LT)
295+
293296
if t.IO.In.MsgType == MsgTypeInternal {
294297
in = t.IO.In.AsInternal().Amount.Nano()
295-
}
296298

297-
if in.Cmp(big.NewInt(0)) != 0 {
298299
intTx := t.IO.In.AsInternal()
299-
build += fmt.Sprintf("LT: %d, In: %s TON, From %s", t.LT, FromNanoTON(in).String(), intTx.SrcAddr)
300+
build += fmt.Sprintf(", In: %s TON, From %s", FromNanoTON(in).String(), intTx.SrcAddr)
300301
comment := intTx.Comment()
301302
if comment != "" {
302303
build += ", Comment: " + comment
303304
}
305+
} else if t.IO.In.MsgType == MsgTypeExternalIn {
306+
exTx := t.IO.In.AsExternalIn()
307+
build += ", ExternalIn, hash: " + hex.EncodeToString(exTx.Body.Hash())
304308
}
305309
}
306310

ton/block.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ type ShardInfo struct {
182182
ID *BlockIDExt `tl:"struct"`
183183
ShardBlock *BlockIDExt `tl:"struct"`
184184
ShardProof []*cell.Cell `tl:"cell optional 2"`
185-
ShardDescription *cell.Cell `tl:"bytes"`
185+
ShardDescription *cell.Cell `tl:"cell optional"`
186186
}
187187

188188
type BlockTransactions struct {
@@ -194,11 +194,11 @@ type BlockTransactions struct {
194194
}
195195

196196
type BlockTransactionsExt struct {
197-
ID *BlockIDExt `tl:"struct"`
198-
ReqCount int32 `tl:"int"`
199-
Incomplete bool `tl:"bool"`
200-
Transactions *cell.Cell `tl:"cell optional"`
201-
Proof []byte `tl:"bytes"`
197+
ID *BlockIDExt `tl:"struct"`
198+
ReqCount int32 `tl:"int"`
199+
Incomplete bool `tl:"bool"`
200+
Transactions []*cell.Cell `tl:"cell optional"`
201+
Proof []byte `tl:"bytes"`
202202
}
203203

204204
type BlockData struct {

ton/jetton/jetton.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package jetton
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"math/big"
78

@@ -19,6 +20,8 @@ type TonApi interface {
1920
SubscribeOnTransactions(workerCtx context.Context, addr *address.Address, lastProcessedLT uint64, channel chan<- *tlb.Transaction)
2021
}
2122

23+
var ErrInvalidTransfer = errors.New("transfer is not verified")
24+
2225
type MintPayloadMasterMsg struct {
2326
Opcode uint32 `tlb:"## 32"`
2427
QueryID uint64 `tlb:"## 64"`
@@ -34,6 +37,14 @@ type MintPayload struct {
3437
MasterMsg MintPayloadMasterMsg `tlb:"^"`
3538
}
3639

40+
type TransferNotification struct {
41+
_ tlb.Magic `tlb:"#7362d09c"`
42+
QueryID uint64 `tlb:"## 64"`
43+
Amount tlb.Coins `tlb:"."`
44+
Sender *address.Address `tlb:"addr"`
45+
ForwardPayload *cell.Cell `tlb:"either . ^"`
46+
}
47+
3748
type Data struct {
3849
TotalSupply *big.Int
3950
Mintable bool

ton/transactions.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,10 @@ func (c *APIClient) findLastTransactionByHash(ctx context.Context, addr *address
324324
}
325325

326326
if isOut {
327+
if transaction.IO.Out == nil {
328+
continue
329+
}
330+
327331
list, err := transaction.IO.Out.ToSlice()
328332
if err != nil {
329333
return nil, fmt.Errorf("cannot list out messages: %w", err)
@@ -334,6 +338,8 @@ func (c *APIClient) findLastTransactionByHash(ctx context.Context, addr *address
334338
return transaction, nil
335339
}
336340
}
341+
342+
continue
337343
} else {
338344
if transaction.IO.In == nil {
339345
continue

ton/wallet/address.go

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,17 @@ func GetStateInit(pubKey ed25519.PublicKey, version VersionConfig, subWallet uin
5555
switch ver {
5656
case HighloadV3:
5757
return nil, fmt.Errorf("use ConfigHighloadV3 for highload v3 spec")
58-
case V5R1:
59-
return nil, fmt.Errorf("use ConfigV5R1 for v5 spec")
58+
case V5R1Beta:
59+
return nil, fmt.Errorf("use ConfigV5R1Beta for V5 spec")
60+
case V5R1Final:
61+
return nil, fmt.Errorf("use ConfigV5R1Final for V5 spec")
6062
}
6163
case ConfigHighloadV3:
6264
ver = HighloadV3
63-
case ConfigV5R1:
64-
ver = V5R1
65+
case ConfigV5R1Beta:
66+
ver = V5R1Beta
67+
case ConfigV5R1Final:
68+
ver = V5R1Final
6569
}
6670

6771
code, ok := walletCode[ver]
@@ -84,18 +88,36 @@ func GetStateInit(pubKey ed25519.PublicKey, version VersionConfig, subWallet uin
8488
MustStoreSlice(pubKey, 256).
8589
MustStoreDict(nil). // empty dict of plugins
8690
EndCell()
87-
case V5R1:
88-
config := version.(ConfigV5R1)
91+
case V5R1Beta:
92+
config := version.(ConfigV5R1Beta)
8993

9094
data = cell.BeginCell().
91-
MustStoreUInt(0, 33). // seqno
92-
MustStoreInt(int64(config.NetworkGlobalID), 32).
93-
MustStoreInt(int64(config.Workchain), 8).
94-
MustStoreUInt(0, 8). // version of v5
95-
MustStoreUInt(uint64(subWallet), 32).
95+
MustStoreUInt(0, 33). // seqno
96+
MustStoreInt(int64(config.NetworkGlobalID), 32). // network id
97+
MustStoreInt(int64(config.Workchain), 8). // workchain
98+
MustStoreUInt(0, 8). // version of v5
99+
MustStoreUInt(uint64(subWallet), 32). // default 0
96100
MustStoreSlice(pubKey, 256).
97101
MustStoreDict(nil). // empty dict of plugins
98102
EndCell()
103+
case V5R1Final:
104+
config := version.(ConfigV5R1Final)
105+
106+
// Create WalletId instance
107+
walletId := V5R1ID{
108+
NetworkGlobalID: config.NetworkGlobalID, // -3 Testnet, -239 Mainnet
109+
WorkChain: config.Workchain,
110+
SubwalletNumber: uint16(subWallet),
111+
WalletVersion: 0, // Wallet Version
112+
}
113+
114+
data = cell.BeginCell().
115+
MustStoreBoolBit(true). // storeUint(1, 1) - boolean flag for context type
116+
MustStoreUInt(0, 32). // Sequence number, hardcoded as 0
117+
MustStoreUInt(uint64(walletId.Serialized()), 32). // Serializing WalletId into 32-bit integer
118+
MustStoreSlice(pubKey, 256). // Storing the public key
119+
MustStoreDict(nil). // Storing an empty plugins dictionary
120+
EndCell()
99121
case HighloadV2R2, HighloadV2Verified:
100122
data = cell.BeginCell().
101123
MustStoreUInt(uint64(subWallet), 32).

ton/wallet/integration_test.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ func Test_HighloadHeavyTransfer(t *testing.T) {
8686
func Test_V5HeavyTransfer(t *testing.T) {
8787
seed := strings.Split(_seed, " ")
8888

89-
w, err := FromSeed(api, seed, ConfigV5R1{
89+
w, err := FromSeed(api, seed, ConfigV5R1Final{
9090
NetworkGlobalID: MainnetGlobalID,
9191
})
9292
if err != nil {
@@ -114,7 +114,9 @@ func Test_V5HeavyTransfer(t *testing.T) {
114114
func Test_WalletTransfer(t *testing.T) {
115115
seed := strings.Split(_seed, " ")
116116

117-
for _, v := range []VersionConfig{ConfigV5R1{
117+
for _, v := range []VersionConfig{ConfigV5R1Final{
118+
NetworkGlobalID: TestnetGlobalID,
119+
}, ConfigV5R1Beta{
118120
NetworkGlobalID: TestnetGlobalID,
119121
}, V3R2, V4R2, HighloadV2R2, V3R1, V4R1, HighloadV2Verified, ConfigHighloadV3{
120122
MessageTTL: 120,
@@ -164,11 +166,22 @@ func Test_WalletTransfer(t *testing.T) {
164166
comment := randString(150)
165167
addr := address.MustParseAddr("EQA8aJTl0jfFnUZBJjTeUxu9OcbsoPBp9UcHE9upyY_X35kE")
166168
if balance.Nano().Uint64() >= 3000000 {
167-
err = w.Transfer(ctx, addr, tlb.MustFromTON("0.003"), comment, true)
169+
tr, err := w.BuildTransfer(addr, tlb.MustFromTON("0.003"), false, comment)
170+
if err != nil {
171+
t.Fatal("Build transfer err:", err.Error())
172+
return
173+
}
174+
175+
tx, _, err := w.SendManyWaitTransaction(ctx, []*Message{tr})
168176
if err != nil {
169177
t.Fatal("Transfer err:", err.Error())
170178
return
171179
}
180+
181+
if tx.OutMsgCount == 0 {
182+
t.Fatal("Out msg is 0:", ver)
183+
return
184+
}
172185
} else {
173186
t.Fatal("not enough balance")
174187
return

0 commit comments

Comments
 (0)