From 7d687bd91e42500af5dda69ade8ad5feffb49485 Mon Sep 17 00:00:00 2001 From: Oleg Baranov Date: Fri, 21 Jun 2024 14:44:52 +0400 Subject: [PATCH 01/26] Fixed BlockTransactionsExt multiroot cell --- ton/block.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ton/block.go b/ton/block.go index 48d57c43..09c139d7 100644 --- a/ton/block.go +++ b/ton/block.go @@ -194,11 +194,11 @@ type BlockTransactions struct { } type BlockTransactionsExt struct { - ID *BlockIDExt `tl:"struct"` - ReqCount int32 `tl:"int"` - Incomplete bool `tl:"bool"` - Transactions *cell.Cell `tl:"cell optional"` - Proof []byte `tl:"bytes"` + ID *BlockIDExt `tl:"struct"` + ReqCount int32 `tl:"int"` + Incomplete bool `tl:"bool"` + Transactions []*cell.Cell `tl:"cell optional"` + Proof []byte `tl:"bytes"` } type BlockData struct { From 5e5a79713849d77e244ab94ec29342dd2860fff0 Mon Sep 17 00:00:00 2001 From: AugustineAurelius Date: Sat, 22 Jun 2024 15:39:14 +0300 Subject: [PATCH 02/26] rewrite cell dump to builder --- tvm/cell/cell.go | 63 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/tvm/cell/cell.go b/tvm/cell/cell.go index 10e3dbe0..392d4763 100644 --- a/tvm/cell/cell.go +++ b/tvm/cell/cell.go @@ -147,51 +147,78 @@ func (c *Cell) DumpBits(limitLength ...int) string { func (c *Cell) dump(deep int, bin bool, limitLength uint64) string { sz, data, _ := c.BeginParse().RestBits() - var val string + builder := strings.Builder{} + if bin { for _, n := range data { - val += fmt.Sprintf("%08b", n) + builder.WriteString(fmt.Sprintf("%08b", n)) } if sz%8 != 0 { - val = val[:uint(len(val))-(8-(sz%8))] + tmp := builder.String() + builder.Reset() + builder.WriteString(tmp[:uint(len(tmp))-(8-(sz%8))]) } } else { - val = strings.ToUpper(hex.EncodeToString(data)) + tmp := make([]byte, len(data)*2) + hex.Encode(tmp, data) + builder.WriteString(strings.ToUpper(string(tmp))) + if sz%8 <= 4 && sz%8 > 0 { - // fift hex - val = val[:len(val)-1] + "_" + tmp := builder.String() + builder.Reset() + builder.WriteString(tmp[:len(tmp)-1]) + builder.WriteByte('_') + } } - str := strings.Repeat(" ", deep) + fmt.Sprint(sz) + "[" + val + "]" + val := builder.String() + builder.Reset() + builder.WriteString(strings.Repeat(" ", deep)) + builder.WriteString(strconv.FormatUint(uint64(sz), 10)) + builder.WriteByte('[') + builder.WriteString(val) + builder.WriteByte(']') + if c.levelMask.GetLevel() > 0 { - str += fmt.Sprintf("{%d}", c.levelMask.GetLevel()) + builder.WriteByte('{') + builder.WriteString(strconv.Itoa(c.levelMask.GetLevel())) + builder.WriteByte('}') + } if c.special { - str += "*" + builder.WriteByte('*') } if len(c.refs) > 0 { - str += " -> {" + + builder.WriteString(" -> {") + for i, ref := range c.refs { - str += "\n" + ref.dump(deep+1, bin, limitLength) + + builder.WriteByte('\n') + builder.WriteString(ref.dump(deep+1, bin, limitLength)) + if i == len(c.refs)-1 { - str += "\n" + builder.WriteByte('\n') } else { - str += "," + builder.WriteByte(',') } - if uint64(len(str)) > limitLength { + if uint64(builder.Len()) > limitLength { break } } - str += strings.Repeat(" ", deep) + "}" + builder.WriteString(strings.Repeat(" ", deep)) + builder.WriteByte('}') } - if uint64(len(str)) > limitLength { - str = str[:limitLength] + if uint64(builder.Len()) > limitLength { + tmp := builder.String() + builder.Reset() + builder.WriteString(tmp[:limitLength]) } - return str + return builder.String() } const _DataCellMaxLevel = 3 From 31f57cd16803d33490bf1e4ab6894440b3414afa Mon Sep 17 00:00:00 2001 From: AugustineAurelius Date: Sat, 22 Jun 2024 18:41:08 +0300 Subject: [PATCH 03/26] micro tvm optimization --- tvm/cell/cell.go | 5 ++++- tvm/cell/cell_test.go | 3 ++- tvm/cell/dict.go | 20 ++++++++++---------- tvm/cell/proof.go | 21 +++++++++++++-------- 4 files changed, 29 insertions(+), 20 deletions(-) diff --git a/tvm/cell/cell.go b/tvm/cell/cell.go index 392d4763..61c30676 100644 --- a/tvm/cell/cell.go +++ b/tvm/cell/cell.go @@ -286,10 +286,13 @@ func (c *Cell) UnmarshalJSON(bytes []byte) error { } bytes = bytes[1 : len(bytes)-1] - data, err := base64.StdEncoding.DecodeString(string(bytes)) + data := make([]byte, base64.StdEncoding.DecodedLen(len(bytes))) + + n, err := base64.StdEncoding.Decode(data, bytes) if err != nil { return err } + data = data[:n] cl, err := FromBOC(data) if err != nil { diff --git a/tvm/cell/cell_test.go b/tvm/cell/cell_test.go index 37110c27..880964e5 100644 --- a/tvm/cell/cell_test.go +++ b/tvm/cell/cell_test.go @@ -6,11 +6,12 @@ import ( "encoding/base64" "encoding/hex" "encoding/json" - "github.com/xssnick/tonutils-go/address" "log" "math/big" "strings" "testing" + + "github.com/xssnick/tonutils-go/address" ) func TestCell_HashSign(t *testing.T) { diff --git a/tvm/cell/dict.go b/tvm/cell/dict.go index 3d018a9c..c48ddebf 100644 --- a/tvm/cell/dict.go +++ b/tvm/cell/dict.go @@ -232,17 +232,15 @@ func (d *Dictionary) Set(key, value *Cell) error { } var err error - var newRoot *Cell if d.root == nil { - newRoot, err = d.storeLeaf(key.BeginParse(), val, d.keySz) + d.root, err = d.storeLeaf(key.BeginParse(), val, d.keySz) } else { - newRoot, err = dive(d.root, key.BeginParse(), d.keySz) + d.root, err = dive(d.root, key.BeginParse(), d.keySz) } if err != nil { return fmt.Errorf("failed to set value in dict, err: %w", err) } - d.root = newRoot return nil } @@ -314,13 +312,15 @@ func (d *Dictionary) IsEmpty() bool { // Deprecated: use LoadAll, dict was reimplemented, so it will be parsed during this call, and it can return error now. func (d *Dictionary) All() []*HashmapKV { list, _ := d.LoadAll() - var old []*HashmapKV - for _, kv := range list { - old = append(old, &HashmapKV{ - Key: kv.Key.MustToCell(), - Value: kv.Value.MustToCell(), - }) + + old := make([]*HashmapKV, len(list)) + for i := 0; i < len(list); i++ { + old[i] = &HashmapKV{ + Key: list[i].Key.MustToCell(), + Value: list[i].Value.MustToCell(), + } } + return old } diff --git a/tvm/cell/proof.go b/tvm/cell/proof.go index bd027957..c9032857 100644 --- a/tvm/cell/proof.go +++ b/tvm/cell/proof.go @@ -237,8 +237,9 @@ func (c *Cell) calculateHashes() { dsc := make([]byte, 2) dsc[0], dsc[1] = c.descriptors(c.levelMask.Apply(levelIndex)) - hash := sha256.New() - hash.Write(dsc) + hashData := make([]byte, 0, 2+c.bitsSz+32+2+32) + + hashData = append(hashData, dsc...) if hashIndex == hashIndexOffset { if levelIndex != 0 && typ != PrunedCellType { @@ -252,14 +253,15 @@ func (c *Cell) calculateHashes() { // we need to set bit at the end if not whole byte was used data[len(data)-1] += 1 << (unusedBits - 1) } - hash.Write(data) + + hashData = append(hashData, data...) } else { if levelIndex == 0 || typ == PrunedCellType { // should never happen panic("pruned or 0") } off := hashIndex - hashIndexOffset - 1 - hash.Write(c.hashes[off*32 : (off+1)*32]) + hashData = append(hashData, c.hashes[off*32:(off+1)*32]...) } var depth uint16 @@ -273,7 +275,7 @@ func (c *Cell) calculateHashes() { depthBytes := make([]byte, 2) binary.BigEndian.PutUint16(depthBytes, childDepth) - hash.Write(depthBytes) + hashData = append(hashData, depthBytes...) if childDepth > depth { depth = childDepth @@ -288,14 +290,17 @@ func (c *Cell) calculateHashes() { for i := 0; i < len(c.refs); i++ { if typ == MerkleProofCellType || typ == MerkleUpdateCellType { - hash.Write(c.refs[i].getHash(levelIndex + 1)) + hashData = append(hashData, c.refs[i].getHash(levelIndex+1)...) } else { - hash.Write(c.refs[i].getHash(levelIndex)) + hashData = append(hashData, c.refs[i].getHash(levelIndex)...) } } off := hashIndex - hashIndexOffset c.depthLevels[off] = depth - copy(c.hashes[off*32:(off+1)*32], hash.Sum(nil)) + + hash := sha256.Sum256(hashData) + + copy(c.hashes[off*32:(off+1)*32], hash[:]) }() } } From 9cfdd5b9b24dbdd5baebee4248306b048fd92259 Mon Sep 17 00:00:00 2001 From: AugustineAurelius Date: Sat, 22 Jun 2024 18:46:07 +0300 Subject: [PATCH 04/26] roll back dict --- tvm/cell/dict.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tvm/cell/dict.go b/tvm/cell/dict.go index c48ddebf..22ec8fb9 100644 --- a/tvm/cell/dict.go +++ b/tvm/cell/dict.go @@ -232,16 +232,18 @@ func (d *Dictionary) Set(key, value *Cell) error { } var err error + var newRoot *Cell if d.root == nil { - d.root, err = d.storeLeaf(key.BeginParse(), val, d.keySz) + newRoot, err = d.storeLeaf(key.BeginParse(), val, d.keySz) } else { - d.root, err = dive(d.root, key.BeginParse(), d.keySz) + newRoot, err = dive(d.root, key.BeginParse(), d.keySz) } if err != nil { return fmt.Errorf("failed to set value in dict, err: %w", err) } + d.root = newRoot return nil } From 3a8aead69987bb60d7c4113c1017628eaae2ae4d Mon Sep 17 00:00:00 2001 From: Totemancer Date: Thu, 27 Jun 2024 19:18:23 +0200 Subject: [PATCH 05/26] Update NFT Mint Example to V4R2 Current wallet standard to be default --- example/nft-mint/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/nft-mint/main.go b/example/nft-mint/main.go index 00f39800..7f79f736 100644 --- a/example/nft-mint/main.go +++ b/example/nft-mint/main.go @@ -70,7 +70,7 @@ func main() { 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) + w, err := wallet.FromSeed(api, words, wallet.V4R2) if err != nil { panic(err) } From 8c27d789a490e6c001d1a0b917026ed4c041624e Mon Sep 17 00:00:00 2001 From: Totemancer Date: Thu, 27 Jun 2024 19:20:38 +0200 Subject: [PATCH 06/26] Update NFT Collection deploy to verified contracts Using latest standard from tonweb.js --- example/deploy-nft-collection/main.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/example/deploy-nft-collection/main.go b/example/deploy-nft-collection/main.go index d6a3692f..02299b69 100644 --- a/example/deploy-nft-collection/main.go +++ b/example/deploy-nft-collection/main.go @@ -45,7 +45,7 @@ func main() { func getWallet(api ton.APIClientWrapped) *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) + w, err := wallet.FromSeed(api, words, wallet.V4R2) if err != nil { panic(err) } @@ -53,7 +53,7 @@ func getWallet(api ton.APIClientWrapped) *wallet.Wallet { } func getNFTCollectionCode() *cell.Cell { - var hexBOC = "b5ee9c72410213010001fe000114ff00f4a413f4bcf2c80b0102016204020201200e030025bc82df6a2687d20699fea6a6a182de86a182c40202cd0a050201200706003d45af0047021f005778018c8cb0558cf165004fa0213cb6b12ccccc971fb0080201200908001b3e401d3232c084b281f2fff27420002d007232cffe0a33c5b25c083232c044fd003d0032c0326003ebd10638048adf000e8698180b8d848adf07d201800e98fe99ff6a2687d20699fea6a6a184108349e9ca829405d47141baf8280e8410854658056b84008646582a802e78b127d010a65b509e58fe59f80e78b64c0207d80701b28b9e382f970c892e000f18112e001718119026001f1812f82c207f97840d0c0b002801fa40304144c85005cf1613cb3fccccccc9ed5400a6357003d4308e378040f4966fa5208e2906a4208100fabe93f2c18fde81019321a05325bbf2f402fa00d43022544b30f00623ba9302a402de04926c21e2b3e6303250444313c85005cf1613cb3fccccccc9ed5400603502d33f5313bbf2e1925313ba01fa00d43028103459f0068e1201a44343c85005cf1613cb3fccccccc9ed54925f05e2020120120f0201201110002db4f47da89a1f481a67fa9a9a86028be09e008e003e00b0002fb5dafda89a1f481a67fa9a9a860d883a1a61fa61ff4806100043b8b5d31ed44d0fa40d33fd4d4d43010245f04d0d431d430d071c8cb0701cf16ccc98f34ea10e" + var hexBOC = "b5ee9c724102140100021f000114ff00f4a413f4bcf2c80b0102016202030202cd04050201200e0f04e7d10638048adf000e8698180b8d848adf07d201800e98fe99ff6a2687d20699fea6a6a184108349e9ca829405d47141baf8280e8410854658056b84008646582a802e78b127d010a65b509e58fe59f80e78b64c0207d80701b28b9e382f970c892e000f18112e001718112e001f181181981e0024060708090201200a0b00603502d33f5313bbf2e1925313ba01fa00d43028103459f0068e1201a44343c85005cf1613cb3fccccccc9ed54925f05e200a6357003d4308e378040f4966fa5208e2906a4208100fabe93f2c18fde81019321a05325bbf2f402fa00d43022544b30f00623ba9302a402de04926c21e2b3e6303250444313c85005cf1613cb3fccccccc9ed54002c323401fa40304144c85005cf1613cb3fccccccc9ed54003c8e15d4d43010344130c85005cf1613cb3fccccccc9ed54e05f04840ff2f00201200c0d003d45af0047021f005778018c8cb0558cf165004fa0213cb6b12ccccc971fb008002d007232cffe0a33c5b25c083232c044fd003d0032c03260001b3e401d3232c084b281f2fff2742002012010110025bc82df6a2687d20699fea6a6a182de86a182c40043b8b5d31ed44d0fa40d33fd4d4d43010245f04d0d431d430d071c8cb0701cf16ccc980201201213002fb5dafda89a1f481a67fa9a9a860d883a1a61fa61ff480610002db4f47da89a1f481a67fa9a9a86028be09e008e003e00b01a500c6e" codeCellBytes, _ := hex.DecodeString(hexBOC) codeCell, err := cell.FromBOC(codeCellBytes) @@ -65,7 +65,7 @@ func getNFTCollectionCode() *cell.Cell { } func getNFTItemCode() *cell.Cell { - var hexBOC = "b5ee9c7241020d010001d0000114ff00f4a413f4bcf2c80b0102016203020009a11f9fe0050202ce050402012008060201200907001d00f232cfd633c58073c5b3327b552000113e910c1c2ebcb85360003b3b513434cffe900835d27080269fc07e90350c04090408f80c1c165b5b6002d70c8871c02497c0f83434c0c05c6c2497c0f83e903e900c7e800c5c75c87e800c7e800c3c00812ce3850c1b088d148cb1c17cb865407e90350c0408fc00f801b4c7f4cfe08417f30f45148c2ea3a1cc840dd78c9004f80c0d0d0d4d60840bf2c9a884aeb8c097c12103fcbc200b0a00727082108b77173505c8cbff5004cf1610248040708010c8cb055007cf165005fa0215cb6a12cb1fcb3f226eb39458cf17019132e201c901fb0001f65135c705f2e191fa4021f001fa40d20031fa00820afaf0801ba121945315a0a1de22d70b01c300209206a19136e220c2fff2e192218e3e821005138d91c85009cf16500bcf16712449145446a0708010c8cb055007cf165005fa0215cb6a12cb1fcb3f226eb39458cf17019132e201c901fb00104794102a375be20c0082028e3526f0018210d53276db103744006d71708010c8cb055007cf165005fa0215cb6a12cb1fcb3f226eb39458cf17019132e201c901fb0093303234e25502f003cc82807e" + var hexBOC = "b5ee9c7201020d010001d0000114ff00f4a413f4bcf2c80b0102016202030202ce04050009a11f9fe00502012006070201200b0c02d70c8871c02497c0f83434c0c05c6c2497c0f83e903e900c7e800c5c75c87e800c7e800c3c00812ce3850c1b088d148cb1c17cb865407e90350c0408fc00f801b4c7f4cfe08417f30f45148c2ea3a1cc840dd78c9004f80c0d0d0d4d60840bf2c9a884aeb8c097c12103fcbc20080900113e910c1c2ebcb8536001f65135c705f2e191fa4021f001fa40d20031fa00820afaf0801ba121945315a0a1de22d70b01c300209206a19136e220c2fff2e192218e3e821005138d91c85009cf16500bcf16712449145446a0708010c8cb055007cf165005fa0215cb6a12cb1fcb3f226eb39458cf17019132e201c901fb00104794102a375be20a00727082108b77173505c8cbff5004cf1610248040708010c8cb055007cf165005fa0215cb6a12cb1fcb3f226eb39458cf17019132e201c901fb000082028e3526f0018210d53276db103744006d71708010c8cb055007cf165005fa0215cb6a12cb1fcb3f226eb39458cf17019132e201c901fb0093303234e25502f003003b3b513434cffe900835d27080269fc07e90350c04090408f80c1c165b5b60001d00f232cfd633c58073c5b3327b5520" codeCellBytes, _ := hex.DecodeString(hexBOC) codeCell, err := cell.FromBOC(codeCellBytes) From 5adbc49fc3a105c1b65a96c28a37ff00c949047e Mon Sep 17 00:00:00 2001 From: AugustineAurelius Date: Mon, 1 Jul 2024 12:29:32 +0300 Subject: [PATCH 07/26] remove silly opt with hash --- tvm/cell/proof.go | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/tvm/cell/proof.go b/tvm/cell/proof.go index c9032857..7cd5bd45 100644 --- a/tvm/cell/proof.go +++ b/tvm/cell/proof.go @@ -237,9 +237,9 @@ func (c *Cell) calculateHashes() { dsc := make([]byte, 2) dsc[0], dsc[1] = c.descriptors(c.levelMask.Apply(levelIndex)) - hashData := make([]byte, 0, 2+c.bitsSz+32+2+32) + hash := sha256.New() - hashData = append(hashData, dsc...) + hash.Write(dsc) if hashIndex == hashIndexOffset { if levelIndex != 0 && typ != PrunedCellType { @@ -254,14 +254,14 @@ func (c *Cell) calculateHashes() { data[len(data)-1] += 1 << (unusedBits - 1) } - hashData = append(hashData, data...) + hash.Write(data) } else { if levelIndex == 0 || typ == PrunedCellType { // should never happen panic("pruned or 0") } off := hashIndex - hashIndexOffset - 1 - hashData = append(hashData, c.hashes[off*32:(off+1)*32]...) + hash.Write(c.hashes[off*32 : (off+1)*32]) } var depth uint16 @@ -275,7 +275,7 @@ func (c *Cell) calculateHashes() { depthBytes := make([]byte, 2) binary.BigEndian.PutUint16(depthBytes, childDepth) - hashData = append(hashData, depthBytes...) + hash.Write(depthBytes) if childDepth > depth { depth = childDepth @@ -290,17 +290,15 @@ func (c *Cell) calculateHashes() { for i := 0; i < len(c.refs); i++ { if typ == MerkleProofCellType || typ == MerkleUpdateCellType { - hashData = append(hashData, c.refs[i].getHash(levelIndex+1)...) + hash.Write(c.refs[i].getHash(levelIndex + 1)) } else { - hashData = append(hashData, c.refs[i].getHash(levelIndex)...) + hash.Write(c.refs[i].getHash(levelIndex)) } } off := hashIndex - hashIndexOffset c.depthLevels[off] = depth - hash := sha256.Sum256(hashData) - - copy(c.hashes[off*32:(off+1)*32], hash[:]) + copy(c.hashes[off*32:(off+1)*32], hash.Sum(nil)) }() } } From f3c0f82740e813f16263a5336fc1692cc16bd14c Mon Sep 17 00:00:00 2001 From: AugustineAurelius Date: Mon, 1 Jul 2024 12:30:48 +0300 Subject: [PATCH 08/26] remove gofmt for cell_test --- tvm/cell/cell_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tvm/cell/cell_test.go b/tvm/cell/cell_test.go index 880964e5..37110c27 100644 --- a/tvm/cell/cell_test.go +++ b/tvm/cell/cell_test.go @@ -6,12 +6,11 @@ import ( "encoding/base64" "encoding/hex" "encoding/json" + "github.com/xssnick/tonutils-go/address" "log" "math/big" "strings" "testing" - - "github.com/xssnick/tonutils-go/address" ) func TestCell_HashSign(t *testing.T) { From 3e3439bceee779899ac27e48d2d68ce0ea4f58f3 Mon Sep 17 00:00:00 2001 From: AugustineAurelius Date: Mon, 1 Jul 2024 12:32:08 +0300 Subject: [PATCH 09/26] remove empty space --- tvm/cell/proof.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/tvm/cell/proof.go b/tvm/cell/proof.go index 7cd5bd45..bd027957 100644 --- a/tvm/cell/proof.go +++ b/tvm/cell/proof.go @@ -238,7 +238,6 @@ func (c *Cell) calculateHashes() { dsc[0], dsc[1] = c.descriptors(c.levelMask.Apply(levelIndex)) hash := sha256.New() - hash.Write(dsc) if hashIndex == hashIndexOffset { @@ -253,7 +252,6 @@ func (c *Cell) calculateHashes() { // we need to set bit at the end if not whole byte was used data[len(data)-1] += 1 << (unusedBits - 1) } - hash.Write(data) } else { if levelIndex == 0 || typ == PrunedCellType { @@ -297,7 +295,6 @@ func (c *Cell) calculateHashes() { } off := hashIndex - hashIndexOffset c.depthLevels[off] = depth - copy(c.hashes[off*32:(off+1)*32], hash.Sum(nil)) }() } From fb48bfa19945638dc25c6df941a6ed2ebec5489b Mon Sep 17 00:00:00 2001 From: Totemancer Date: Tue, 2 Jul 2024 12:43:04 +0200 Subject: [PATCH 10/26] Refactor V5R1 to V5Beta and Prepare Support for V5 Final (WIP) --- ton/wallet/address.go | 34 +++++++-- ton/wallet/{v5r1.go => v5_beta.go} | 20 +++--- ton/wallet/v5_final.go | 112 +++++++++++++++++++++++++++++ ton/wallet/wallet.go | 46 ++++++++---- 4 files changed, 181 insertions(+), 31 deletions(-) rename ton/wallet/{v5r1.go => v5_beta.go} (81%) create mode 100644 ton/wallet/v5_final.go diff --git a/ton/wallet/address.go b/ton/wallet/address.go index 01a1df13..d62ef6b8 100644 --- a/ton/wallet/address.go +++ b/ton/wallet/address.go @@ -55,13 +55,17 @@ func GetStateInit(pubKey ed25519.PublicKey, version VersionConfig, subWallet uin switch ver { case HighloadV3: return nil, fmt.Errorf("use ConfigHighloadV3 for highload v3 spec") - case V5R1: - return nil, fmt.Errorf("use ConfigV5R1 for v5 spec") + case V5Beta: + return nil, fmt.Errorf("use ConfigV5Beta for v5b spec") + case V5Final: + return nil, fmt.Errorf("use ConfigV5Final for v5f spec") } case ConfigHighloadV3: ver = HighloadV3 - case ConfigV5R1: - ver = V5R1 + case ConfigV5Beta: + ver = V5Beta + case ConfigV5Final: + ver = V5Final } code, ok := walletCode[ver] @@ -84,8 +88,8 @@ func GetStateInit(pubKey ed25519.PublicKey, version VersionConfig, subWallet uin MustStoreSlice(pubKey, 256). MustStoreDict(nil). // empty dict of plugins EndCell() - case V5R1: - config := version.(ConfigV5R1) + case V5Beta: + config := version.(ConfigV5Beta) data = cell.BeginCell(). MustStoreUInt(0, 33). // seqno @@ -96,6 +100,24 @@ func GetStateInit(pubKey ed25519.PublicKey, version VersionConfig, subWallet uin MustStoreSlice(pubKey, 256). MustStoreDict(nil). // empty dict of plugins EndCell() + case V5Final: + config := version.(ConfigV5Final) + + // Create WalletId instance + walletId := WalletId{ + NetworkGlobalID: config.NetworkGlobalID, + WorkChain: config.Workchain, + SubwalletNumber: subWallet, + walletVersion: 0, + } + + data = cell.BeginCell(). + MustStoreBoolBit(true). + MustStoreUInt(0, 32). // seqno + MustStoreUInt(uint64(walletId.Serialized()), 32). // serialize + MustStoreSlice(pubKey, 256). + MustStoreDict(nil). // empty dict of plugins + EndCell() case HighloadV2R2, HighloadV2Verified: data = cell.BeginCell(). MustStoreUInt(uint64(subWallet), 32). diff --git a/ton/wallet/v5r1.go b/ton/wallet/v5_beta.go similarity index 81% rename from ton/wallet/v5r1.go rename to ton/wallet/v5_beta.go index 8c560ca8..99c07c44 100644 --- a/ton/wallet/v5r1.go +++ b/ton/wallet/v5_beta.go @@ -4,32 +4,30 @@ import ( "context" "errors" "fmt" + "time" + "github.com/xssnick/tonutils-go/tlb" "github.com/xssnick/tonutils-go/ton" - "time" "github.com/xssnick/tonutils-go/tvm/cell" ) // https://github.com/tonkeeper/tonkeeper-ton/commit/e8a7f3415e241daf4ac723f273fbc12776663c49#diff-c20d462b2e1ec616bbba2db39acc7a6c61edc3d5e768f5c2034a80169b1a56caR29 -const _V5R1CodeHex = "b5ee9c7241010101002300084202e4cf3b2f4c6d6a61ea0f2b5447d266785b26af3637db2deee6bcd1aa826f34120dcd8e11" +const _V5BetaCodeHex = "b5ee9c7241010101002300084202e4cf3b2f4c6d6a61ea0f2b5447d266785b26af3637db2deee6bcd1aa826f34120dcd8e11" -type ConfigV5R1 struct { +type ConfigV5Beta struct { NetworkGlobalID int32 Workchain int8 } -type SpecV5R1 struct { +type SpecV5Beta struct { SpecRegular SpecSeqno - config ConfigV5R1 + config ConfigV5Beta } -const MainnetGlobalID = -239 -const TestnetGlobalID = -3 - -func (s *SpecV5R1) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt, messages []*Message) (_ *cell.Cell, err error) { +func (s *SpecV5Beta) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt, messages []*Message) (_ *cell.Cell, err error) { // TODO: remove block, now it is here for backwards compatibility if len(messages) > 255 { @@ -41,7 +39,7 @@ func (s *SpecV5R1) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt, return nil, fmt.Errorf("failed to fetch seqno: %w", err) } - actions, err := packV5Actions(messages) + actions, err := packV5BetaActions(messages) if err != nil { return nil, fmt.Errorf("failed to build actions: %w", err) } @@ -62,7 +60,7 @@ func (s *SpecV5R1) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt, return msg, nil } -func packV5Actions(messages []*Message) (*cell.Builder, error) { +func packV5BetaActions(messages []*Message) (*cell.Builder, error) { if len(messages) > 255 { return nil, fmt.Errorf("max 255 messages allowed for v5") } diff --git a/ton/wallet/v5_final.go b/ton/wallet/v5_final.go new file mode 100644 index 00000000..dc270632 --- /dev/null +++ b/ton/wallet/v5_final.go @@ -0,0 +1,112 @@ +package wallet + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/xssnick/tonutils-go/tlb" + "github.com/xssnick/tonutils-go/ton" + + "github.com/xssnick/tonutils-go/tvm/cell" +) + +// https://github.com/tolya-yanot/w5/commit/84eaf15caf79b354507116964c283da08efbb564 +const _V5FinalCodeHex = "b5ee9c724102140100028c000114ff00f4a413f4bcf2c80b0102012004020102f203012020d70b1f82107369676ebaf2e08a7f700f0201480e0502012007060019be5f0f6a2684080a0eb90fa02c0201200b080201480a090011b262fb513435c280200017b325fb51341c75c875c2c7e002016e0d0c0019af1df6a2684010eb90eb858fc00019adce76a2684020eb90eb85ffc002f2d020d749c120915b8f6e20d70b1f2082106578746ebd21821073696e74bdb0925f03e002d0d60301c713c200925f03e00282106578746eba8eb08020d72101fa4030fa44f828fa443058bd915be0ed44d0810141d721f4058307f40e6fa1319130e18040d721707fdb3ce03120d749810282b99130e07070e2100f01e48eefeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109a29945f0bdb31e1f2c087df02b35007b0f2d0845125baf2e0855037baf2e086f823bbf2d0882392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd81003f6eda2edfb02f404216e926c218e4c0221d73930709421c700b38e2d01d72820761e436c20d749c008f2e09320d74ac002f2e09320d71d06c712c2005230b0f2d089d74cd7393001a4e86c128407bbf2e093d74ac000f2e093ed55e2d20001c000915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74a1312110010935bdb31e1d74cd0007230d72c08248e2d21f2e092d200ed44d0d2005113baf2d08f54503091319c01810140d721d70a00f2e08ee2c8ca0058cf16c9ed5493f2c08de2009601fa4001fa44f828fa443058baf2e091ed44d0810141d718f405049d7fc8ca0040048307f453f2e08b8e14038307f45bf2e08c22d70a00216e01b3b0f2d090e2c85003cf1612f400c9ed545bfe7180" + +type ConfigV5Final struct { + NetworkGlobalID int32 + Workchain int8 +} + +type SpecV5Final struct { + SpecRegular + SpecSeqno + + config ConfigV5Final +} + +type WalletId struct { + NetworkGlobalID int32 + WorkChain int8 + SubwalletNumber uint32 + walletVersion uint8 +} + +func (w WalletId) Serialized() uint32 { + // Serialize WalletId into a 32-bit integer + return uint32(w.NetworkGlobalID)<<24 | uint32(w.WorkChain)<<16 | w.SubwalletNumber +} + +func (s *SpecV5Final) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt, messages []*Message) (_ *cell.Cell, err error) { + if len(messages) > 255 { + return nil, errors.New("for this type of wallet max 255 messages can be sent at the same time") + } + + seq, err := s.seqnoFetcher(ctx, s.wallet.subwallet) + if err != nil { + return nil, fmt.Errorf("failed to fetch seqno: %w", err) + } + + actions, err := packV5FinalActions(messages) + if err != nil { + return nil, fmt.Errorf("failed to build actions: %w", err) + } + + walletId := WalletId{ + NetworkGlobalID: s.config.NetworkGlobalID, + WorkChain: s.config.Workchain, + SubwalletNumber: s.wallet.subwallet, + walletVersion: 0, + } + + payload := cell.BeginCell(). + MustStoreUInt(0x7369676e, 32). // external sign op code + MustStoreUInt(uint64(walletId.Serialized()), 32). // serialized WalletId + MustStoreUInt(uint64(time.Now().Add(time.Duration(s.messagesTTL)*time.Second).UTC().Unix()), 32). // validUntil + MustStoreUInt(uint64(seq), 32). // seq (block) + MustStoreBuilder(actions) // Action list + + sign := payload.EndCell().Sign(s.wallet.key) + msg := cell.BeginCell().MustStoreBuilder(payload).MustStoreSlice(sign, 512).EndCell() + + return msg, nil +} + +// Validate messages +func validateMessageFields(messages []*Message) error { + if len(messages) > 255 { + return fmt.Errorf("max 255 messages allowed for v5") + } + for _, message := range messages { + if message.InternalMessage == nil { + return fmt.Errorf("internal message cannot be nil") + } + } + return nil +} + +// Pack Actions +func packV5FinalActions(messages []*Message) (*cell.Builder, error) { + if err := validateMessageFields(messages); err != nil { + return nil, err + } + + var list = cell.BeginCell().EndCell() + for _, message := range messages { + outMsg, err := tlb.ToCell(message.InternalMessage) + if err != nil { + return nil, err + } + + msg := cell.BeginCell().MustStoreUInt(0x0ec3c86d, 32). // action_send_msg prefix + MustStoreUInt(uint64(message.Mode), 8). // mode + MustStoreRef(outMsg) // message reference + + list = cell.BeginCell().MustStoreRef(list).MustStoreBuilder(msg).EndCell() + } + + // Ensure the action list ends with 0, 1 as per the new specification + return cell.BeginCell().MustStoreUInt(1, 1).MustStoreRef(list).MustStoreUInt(0, 1), nil +} diff --git a/ton/wallet/wallet.go b/ton/wallet/wallet.go index e571935d..7f76d879 100644 --- a/ton/wallet/wallet.go +++ b/ton/wallet/wallet.go @@ -13,9 +13,10 @@ import ( "encoding/hex" "errors" "fmt" - "github.com/xssnick/tonutils-go/adnl" "time" + "github.com/xssnick/tonutils-go/adnl" + "github.com/xssnick/tonutils-go/ton" "github.com/xssnick/tonutils-go/address" @@ -25,6 +26,10 @@ import ( type Version int +// Network IDs +const MainnetGlobalID = -239 +const TestnetGlobalID = -3 + const ( V1R1 Version = 11 V1R2 Version = 12 @@ -36,7 +41,8 @@ const ( V3 = V3R2 V4R1 Version = 41 V4R2 Version = 42 - V5R1 Version = 51 + V5Beta Version = 51 + V5Final Version = 52 HighloadV2R2 Version = 122 HighloadV2Verified Version = 123 HighloadV3 Version = 300 @@ -79,7 +85,8 @@ var ( V2R1: _V2R1CodeHex, V2R2: _V2R2CodeHex, V3R1: _V3R1CodeHex, V3R2: _V3R2CodeHex, V4R1: _V4R1CodeHex, V4R2: _V4R2CodeHex, - V5R1: _V5R1CodeHex, + V5Beta: _V5BetaCodeHex, + V5Final: _V5FinalCodeHex, HighloadV2R2: _HighloadV2R2CodeHex, HighloadV2Verified: _HighloadV2VerifiedCodeHex, HighloadV3: _HighloadV3CodeHex, Lockup: _LockupCodeHex, @@ -155,7 +162,8 @@ func FromPrivateKey(api TonAPI, key ed25519.PrivateKey, version VersionConfig) ( // default subwallet depends on wallet type switch version.(type) { - case ConfigV5R1: + case ConfigV5Beta: + case ConfigV5Final: subwallet = 0 } @@ -182,7 +190,7 @@ func FromPrivateKey(api TonAPI, key ed25519.PrivateKey, version VersionConfig) ( func getSpec(w *Wallet) (any, error) { switch v := w.ver.(type) { - case Version, ConfigV5R1: + case Version, ConfigV5Beta, ConfigV5Final: regular := SpecRegular{ wallet: w, messagesTTL: 60 * 3, // default ttl 3 min @@ -210,11 +218,16 @@ func getSpec(w *Wallet) (any, error) { } switch x := w.ver.(type) { - case ConfigV5R1: + case ConfigV5Beta: if x.NetworkGlobalID == 0 { - return nil, fmt.Errorf("NetworkGlobalID should be set in v5 config") + return nil, fmt.Errorf("NetworkGlobalID should be set in V5B config") } - return &SpecV5R1{SpecRegular: regular, SpecSeqno: SpecSeqno{seqnoFetcher: seqnoFetcher}, config: x}, nil + return &SpecV5Beta{SpecRegular: regular, SpecSeqno: SpecSeqno{seqnoFetcher: seqnoFetcher}, config: x}, nil + case ConfigV5Final: + if x.NetworkGlobalID == 0 { + return nil, fmt.Errorf("NetworkGlobalID should be set in V5F config") + } + return &SpecV5Final{SpecRegular: regular, SpecSeqno: SpecSeqno{seqnoFetcher: seqnoFetcher}, config: x}, nil } switch v { @@ -226,8 +239,10 @@ func getSpec(w *Wallet) (any, error) { return &SpecHighloadV2R2{regular, SpecQuery{}}, nil case HighloadV3: return nil, fmt.Errorf("use ConfigHighloadV3 for highload v3 spec") - case V5R1: - return nil, fmt.Errorf("use ConfigV5R1 for v5 spec") + case V5Beta: + return nil, fmt.Errorf("use ConfigV5Beta for v5b spec") + case V5Final: + return nil, fmt.Errorf("use ConfigV5Final for v5f spec") } case ConfigHighloadV3: return &SpecHighloadV3{wallet: w, config: v}, nil @@ -328,13 +343,16 @@ func (w *Wallet) PrepareExternalMessageForMany(ctx context.Context, withStateIni var msg *cell.Cell switch v := w.ver.(type) { - case Version, ConfigV5R1: - if _, ok := v.(ConfigV5R1); ok { - v = V5R1 + case Version, ConfigV5Beta, ConfigV5Final: + if _, ok := v.(ConfigV5Beta); ok { + v = V5Beta + } + if _, ok := v.(ConfigV5Final); ok { + v = V5Final } switch v { - case V3R2, V3R1, V4R2, V4R1, V5R1: + case V3R2, V3R1, V4R2, V4R1, V5Beta, V5Final: msg, err = w.spec.(RegularBuilder).BuildMessage(ctx, !withStateInit, nil, messages) if err != nil { return nil, fmt.Errorf("build message err: %w", err) From e0f781df0b517603f622c04d8e7e2629aea22c6c Mon Sep 17 00:00:00 2001 From: AugustineAurelius Date: Wed, 3 Jul 2024 10:42:19 +0300 Subject: [PATCH 11/26] fix double calc --- tvm/cell/dict.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tvm/cell/dict.go b/tvm/cell/dict.go index 22ec8fb9..71cef2f5 100644 --- a/tvm/cell/dict.go +++ b/tvm/cell/dict.go @@ -476,10 +476,10 @@ func loadLabel(sz uint, loader *Slice, key *Builder) (uint, *Builder, error) { return 0, nil, err } + bitsLen := uint(math.Ceil(math.Log2(float64(sz + 1)))) + // hml_long$10 if second == 0 { - bitsLen := uint(math.Ceil(math.Log2(float64(sz + 1)))) - ln, err := loader.LoadUInt(bitsLen) if err != nil { return 0, nil, err @@ -505,8 +505,6 @@ func loadLabel(sz uint, loader *Slice, key *Builder) (uint, *Builder, error) { return 0, nil, err } - bitsLen := uint(math.Ceil(math.Log2(float64(sz + 1)))) - ln, err := loader.LoadUInt(bitsLen) if err != nil { return 0, nil, err From cb32aa62092b19aeade609c1019e9cf98d14c9a4 Mon Sep 17 00:00:00 2001 From: AugustineAurelius Date: Wed, 3 Jul 2024 10:45:20 +0300 Subject: [PATCH 12/26] dict.go 513 prealloc and init slice --- tvm/cell/dict.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tvm/cell/dict.go b/tvm/cell/dict.go index 71cef2f5..fab7985e 100644 --- a/tvm/cell/dict.go +++ b/tvm/cell/dict.go @@ -510,13 +510,13 @@ func loadLabel(sz uint, loader *Slice, key *Builder) (uint, *Builder, error) { return 0, nil, err } - var toStore []byte + toStore := make([]byte, 1+int(ln)/8) if bitType == 1 { // N of ones - toStore = bytes.Repeat([]byte{0xFF}, 1+(int(ln)/8)) + copy(toStore, bytes.Repeat([]byte{0xFF}, 1+(int(ln)/8))) } else { // N of zeroes - toStore = bytes.Repeat([]byte{0x00}, 1+(int(ln)/8)) + copy(toStore, bytes.Repeat([]byte{0xFF}, 1+(int(ln)/8))) } err = key.StoreSlice(toStore, uint(ln)) From 0fedda1d1c18d9c057bb43c247f25d2d1ae85ede Mon Sep 17 00:00:00 2001 From: Totemancer Date: Wed, 3 Jul 2024 16:15:21 +0200 Subject: [PATCH 13/26] Update to Finalized V5 Contract (WIP) from upstream tonkeeper/w5 compiled base64 IINLe3KxEhR+Gy+0V7hOdNGjDwT3N9T2KmaOlVLSty8= --- ton/wallet/v5_final.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ton/wallet/v5_final.go b/ton/wallet/v5_final.go index dc270632..994fbfb5 100644 --- a/ton/wallet/v5_final.go +++ b/ton/wallet/v5_final.go @@ -12,8 +12,8 @@ import ( "github.com/xssnick/tonutils-go/tvm/cell" ) -// https://github.com/tolya-yanot/w5/commit/84eaf15caf79b354507116964c283da08efbb564 -const _V5FinalCodeHex = "b5ee9c724102140100028c000114ff00f4a413f4bcf2c80b0102012004020102f203012020d70b1f82107369676ebaf2e08a7f700f0201480e0502012007060019be5f0f6a2684080a0eb90fa02c0201200b080201480a090011b262fb513435c280200017b325fb51341c75c875c2c7e002016e0d0c0019af1df6a2684010eb90eb858fc00019adce76a2684020eb90eb85ffc002f2d020d749c120915b8f6e20d70b1f2082106578746ebd21821073696e74bdb0925f03e002d0d60301c713c200925f03e00282106578746eba8eb08020d72101fa4030fa44f828fa443058bd915be0ed44d0810141d721f4058307f40e6fa1319130e18040d721707fdb3ce03120d749810282b99130e07070e2100f01e48eefeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109a29945f0bdb31e1f2c087df02b35007b0f2d0845125baf2e0855037baf2e086f823bbf2d0882392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd81003f6eda2edfb02f404216e926c218e4c0221d73930709421c700b38e2d01d72820761e436c20d749c008f2e09320d74ac002f2e09320d71d06c712c2005230b0f2d089d74cd7393001a4e86c128407bbf2e093d74ac000f2e093ed55e2d20001c000915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74a1312110010935bdb31e1d74cd0007230d72c08248e2d21f2e092d200ed44d0d2005113baf2d08f54503091319c01810140d721d70a00f2e08ee2c8ca0058cf16c9ed5493f2c08de2009601fa4001fa44f828fa443058baf2e091ed44d0810141d718f405049d7fc8ca0040048307f453f2e08b8e14038307f45bf2e08c22d70a00216e01b3b0f2d090e2c85003cf1612f400c9ed545bfe7180" +// https://github.com/tonkeeper/w5/blob/main/build/wallet_v5.compiled.json +const _V5FinalCodeHex = "b5ee9c7241021401000281000114ff00f4a413f4bcf2c80b01020120020d020148030402dcd020d749c120915b8f6320d70b1f2082106578746ebd21821073696e74bdb0925f03e082106578746eba8eb48020d72101d074d721fa4030fa44f828fa443058bd915be0ed44d0810141d721f4058307f40e6fa1319130e18040d721707fdb3ce03120d749810280b99130e070e2100f020120050c020120060902016e07080019adce76a2684020eb90eb85ffc00019af1df6a2684010eb90eb858fc00201480a0b0017b325fb51341c75c875c2c7e00011b262fb513435c280200019be5f0f6a2684080a0eb90fa02c0102f20e011e20d70b1f82107369676ebaf2e08a7f0f01e68ef0eda2edfb218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ccf9109a28945f0adb31e1f2c087df02b35007b0f2d0845125baf2e0855036baf2e086f823bbf2d0882292f800de01a47fc8ca00cb1f01cf16c9ed542092f80fde70db3cd81003f6eda2edfb02f404216e926c218e4c0221d73930709421c700b38e2d01d72820761e436c20d749c008f2e09320d74ac002f2e09320d71d06c712c2005230b0f2d089d74cd7393001a4e86c128407bbf2e093d74ac000f2e093ed55e2d20001c000915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74a111213009601fa4001fa44f828fa443058baf2e091ed44d0810141d718f405049d7fc8ca0040048307f453f2e08b8e14038307f45bf2e08c22d70a00216e01b3b0f2d090e2c85003cf1612f400c9ed54007230d72c08248e2d21f2e092d200ed44d0d2005113baf2d08f54503091319c01810140d721d70a00f2e08ee2c8ca0058cf16c9ed5493f2c08de20010935bdb31e1d74cd0b4d6c35e" type ConfigV5Final struct { NetworkGlobalID int32 From 8ba6af6f8bec49e7560e010f88bf51525c9cc27e Mon Sep 17 00:00:00 2001 From: Totemancer Date: Thu, 4 Jul 2024 14:40:43 +0200 Subject: [PATCH 14/26] Update name to V5R1, V5R2 --- ton/wallet/address.go | 24 +++++++-------- ton/wallet/{v5_beta.go => v5r1.go} | 14 ++++----- ton/wallet/{v5_final.go => v5r2.go} | 16 +++++----- ton/wallet/wallet.go | 46 ++++++++++++++--------------- 4 files changed, 50 insertions(+), 50 deletions(-) rename ton/wallet/{v5_beta.go => v5r1.go} (83%) rename ton/wallet/{v5_final.go => v5r2.go} (62%) diff --git a/ton/wallet/address.go b/ton/wallet/address.go index d62ef6b8..adb304f2 100644 --- a/ton/wallet/address.go +++ b/ton/wallet/address.go @@ -55,17 +55,17 @@ func GetStateInit(pubKey ed25519.PublicKey, version VersionConfig, subWallet uin switch ver { case HighloadV3: return nil, fmt.Errorf("use ConfigHighloadV3 for highload v3 spec") - case V5Beta: - return nil, fmt.Errorf("use ConfigV5Beta for v5b spec") - case V5Final: - return nil, fmt.Errorf("use ConfigV5Final for v5f spec") + case V5R1: + return nil, fmt.Errorf("use ConfigV5R1 for V5 spec") + case V5R2: + return nil, fmt.Errorf("use ConfigV5R2 for V5 spec") } case ConfigHighloadV3: ver = HighloadV3 - case ConfigV5Beta: - ver = V5Beta - case ConfigV5Final: - ver = V5Final + case ConfigV5R1: + ver = V5R1 + case ConfigV5R2: + ver = V5R2 } code, ok := walletCode[ver] @@ -88,8 +88,8 @@ func GetStateInit(pubKey ed25519.PublicKey, version VersionConfig, subWallet uin MustStoreSlice(pubKey, 256). MustStoreDict(nil). // empty dict of plugins EndCell() - case V5Beta: - config := version.(ConfigV5Beta) + case V5R1: + config := version.(ConfigV5R1) data = cell.BeginCell(). MustStoreUInt(0, 33). // seqno @@ -100,8 +100,8 @@ func GetStateInit(pubKey ed25519.PublicKey, version VersionConfig, subWallet uin MustStoreSlice(pubKey, 256). MustStoreDict(nil). // empty dict of plugins EndCell() - case V5Final: - config := version.(ConfigV5Final) + case V5R2: + config := version.(ConfigV5R2) // Create WalletId instance walletId := WalletId{ diff --git a/ton/wallet/v5_beta.go b/ton/wallet/v5r1.go similarity index 83% rename from ton/wallet/v5_beta.go rename to ton/wallet/v5r1.go index 99c07c44..17f59833 100644 --- a/ton/wallet/v5_beta.go +++ b/ton/wallet/v5r1.go @@ -13,21 +13,21 @@ import ( ) // https://github.com/tonkeeper/tonkeeper-ton/commit/e8a7f3415e241daf4ac723f273fbc12776663c49#diff-c20d462b2e1ec616bbba2db39acc7a6c61edc3d5e768f5c2034a80169b1a56caR29 -const _V5BetaCodeHex = "b5ee9c7241010101002300084202e4cf3b2f4c6d6a61ea0f2b5447d266785b26af3637db2deee6bcd1aa826f34120dcd8e11" +const _V5R1CodeHex = "b5ee9c7241010101002300084202e4cf3b2f4c6d6a61ea0f2b5447d266785b26af3637db2deee6bcd1aa826f34120dcd8e11" -type ConfigV5Beta struct { +type ConfigV5R1 struct { NetworkGlobalID int32 Workchain int8 } -type SpecV5Beta struct { +type SpecV5R1 struct { SpecRegular SpecSeqno - config ConfigV5Beta + config ConfigV5R1 } -func (s *SpecV5Beta) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt, messages []*Message) (_ *cell.Cell, err error) { +func (s *SpecV5R1) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt, messages []*Message) (_ *cell.Cell, err error) { // TODO: remove block, now it is here for backwards compatibility if len(messages) > 255 { @@ -39,7 +39,7 @@ func (s *SpecV5Beta) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt return nil, fmt.Errorf("failed to fetch seqno: %w", err) } - actions, err := packV5BetaActions(messages) + actions, err := packV5R1Actions(messages) if err != nil { return nil, fmt.Errorf("failed to build actions: %w", err) } @@ -60,7 +60,7 @@ func (s *SpecV5Beta) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt return msg, nil } -func packV5BetaActions(messages []*Message) (*cell.Builder, error) { +func packV5R1Actions(messages []*Message) (*cell.Builder, error) { if len(messages) > 255 { return nil, fmt.Errorf("max 255 messages allowed for v5") } diff --git a/ton/wallet/v5_final.go b/ton/wallet/v5r2.go similarity index 62% rename from ton/wallet/v5_final.go rename to ton/wallet/v5r2.go index 994fbfb5..1fe33e03 100644 --- a/ton/wallet/v5_final.go +++ b/ton/wallet/v5r2.go @@ -12,19 +12,19 @@ import ( "github.com/xssnick/tonutils-go/tvm/cell" ) -// https://github.com/tonkeeper/w5/blob/main/build/wallet_v5.compiled.json -const _V5FinalCodeHex = "b5ee9c7241021401000281000114ff00f4a413f4bcf2c80b01020120020d020148030402dcd020d749c120915b8f6320d70b1f2082106578746ebd21821073696e74bdb0925f03e082106578746eba8eb48020d72101d074d721fa4030fa44f828fa443058bd915be0ed44d0810141d721f4058307f40e6fa1319130e18040d721707fdb3ce03120d749810280b99130e070e2100f020120050c020120060902016e07080019adce76a2684020eb90eb85ffc00019af1df6a2684010eb90eb858fc00201480a0b0017b325fb51341c75c875c2c7e00011b262fb513435c280200019be5f0f6a2684080a0eb90fa02c0102f20e011e20d70b1f82107369676ebaf2e08a7f0f01e68ef0eda2edfb218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ccf9109a28945f0adb31e1f2c087df02b35007b0f2d0845125baf2e0855036baf2e086f823bbf2d0882292f800de01a47fc8ca00cb1f01cf16c9ed542092f80fde70db3cd81003f6eda2edfb02f404216e926c218e4c0221d73930709421c700b38e2d01d72820761e436c20d749c008f2e09320d74ac002f2e09320d71d06c712c2005230b0f2d089d74cd7393001a4e86c128407bbf2e093d74ac000f2e093ed55e2d20001c000915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74a111213009601fa4001fa44f828fa443058baf2e091ed44d0810141d718f405049d7fc8ca0040048307f453f2e08b8e14038307f45bf2e08c22d70a00216e01b3b0f2d090e2c85003cf1612f400c9ed54007230d72c08248e2d21f2e092d200ed44d0d2005113baf2d08f54503091319c01810140d721d70a00f2e08ee2c8ca0058cf16c9ed5493f2c08de20010935bdb31e1d74cd0b4d6c35e" +// https://github.com/ton-blockchain/wallet-contract-v5/blob/main/build/wallet_v5.compiled.json +const _V5R2CodeHex = "b5ee9c7241021401000281000114ff00f4a413f4bcf2c80b01020120020d020148030402dcd020d749c120915b8f6320d70b1f2082106578746ebd21821073696e74bdb0925f03e082106578746eba8eb48020d72101d074d721fa4030fa44f828fa443058bd915be0ed44d0810141d721f4058307f40e6fa1319130e18040d721707fdb3ce03120d749810280b99130e070e2100f020120050c020120060902016e07080019adce76a2684020eb90eb85ffc00019af1df6a2684010eb90eb858fc00201480a0b0017b325fb51341c75c875c2c7e00011b262fb513435c280200019be5f0f6a2684080a0eb90fa02c0102f20e011e20d70b1f82107369676ebaf2e08a7f0f01e68ef0eda2edfb218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ccf9109a28945f0adb31e1f2c087df02b35007b0f2d0845125baf2e0855036baf2e086f823bbf2d0882292f800de01a47fc8ca00cb1f01cf16c9ed542092f80fde70db3cd81003f6eda2edfb02f404216e926c218e4c0221d73930709421c700b38e2d01d72820761e436c20d749c008f2e09320d74ac002f2e09320d71d06c712c2005230b0f2d089d74cd7393001a4e86c128407bbf2e093d74ac000f2e093ed55e2d20001c000915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74a111213009601fa4001fa44f828fa443058baf2e091ed44d0810141d718f405049d7fc8ca0040048307f453f2e08b8e14038307f45bf2e08c22d70a00216e01b3b0f2d090e2c85003cf1612f400c9ed54007230d72c08248e2d21f2e092d200ed44d0d2005113baf2d08f54503091319c01810140d721d70a00f2e08ee2c8ca0058cf16c9ed5493f2c08de20010935bdb31e1d74cd0b4d6c35e" -type ConfigV5Final struct { +type ConfigV5R2 struct { NetworkGlobalID int32 Workchain int8 } -type SpecV5Final struct { +type SpecV5R2 struct { SpecRegular SpecSeqno - config ConfigV5Final + config ConfigV5R2 } type WalletId struct { @@ -39,7 +39,7 @@ func (w WalletId) Serialized() uint32 { return uint32(w.NetworkGlobalID)<<24 | uint32(w.WorkChain)<<16 | w.SubwalletNumber } -func (s *SpecV5Final) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt, messages []*Message) (_ *cell.Cell, err error) { +func (s *SpecV5R2) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt, messages []*Message) (_ *cell.Cell, err error) { if len(messages) > 255 { return nil, errors.New("for this type of wallet max 255 messages can be sent at the same time") } @@ -49,7 +49,7 @@ func (s *SpecV5Final) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDEx return nil, fmt.Errorf("failed to fetch seqno: %w", err) } - actions, err := packV5FinalActions(messages) + actions, err := packV5R2Actions(messages) if err != nil { return nil, fmt.Errorf("failed to build actions: %w", err) } @@ -88,7 +88,7 @@ func validateMessageFields(messages []*Message) error { } // Pack Actions -func packV5FinalActions(messages []*Message) (*cell.Builder, error) { +func packV5R2Actions(messages []*Message) (*cell.Builder, error) { if err := validateMessageFields(messages); err != nil { return nil, err } diff --git a/ton/wallet/wallet.go b/ton/wallet/wallet.go index 7f76d879..cdec4183 100644 --- a/ton/wallet/wallet.go +++ b/ton/wallet/wallet.go @@ -41,8 +41,8 @@ const ( V3 = V3R2 V4R1 Version = 41 V4R2 Version = 42 - V5Beta Version = 51 - V5Final Version = 52 + V5R1 Version = 51 + V5R2 Version = 52 HighloadV2R2 Version = 122 HighloadV2Verified Version = 123 HighloadV3 Version = 300 @@ -85,8 +85,8 @@ var ( V2R1: _V2R1CodeHex, V2R2: _V2R2CodeHex, V3R1: _V3R1CodeHex, V3R2: _V3R2CodeHex, V4R1: _V4R1CodeHex, V4R2: _V4R2CodeHex, - V5Beta: _V5BetaCodeHex, - V5Final: _V5FinalCodeHex, + V5R1: _V5R1CodeHex, + V5R2: _V5R2CodeHex, HighloadV2R2: _HighloadV2R2CodeHex, HighloadV2Verified: _HighloadV2VerifiedCodeHex, HighloadV3: _HighloadV3CodeHex, Lockup: _LockupCodeHex, @@ -162,8 +162,8 @@ func FromPrivateKey(api TonAPI, key ed25519.PrivateKey, version VersionConfig) ( // default subwallet depends on wallet type switch version.(type) { - case ConfigV5Beta: - case ConfigV5Final: + case ConfigV5R1: + case ConfigV5R2: subwallet = 0 } @@ -190,7 +190,7 @@ func FromPrivateKey(api TonAPI, key ed25519.PrivateKey, version VersionConfig) ( func getSpec(w *Wallet) (any, error) { switch v := w.ver.(type) { - case Version, ConfigV5Beta, ConfigV5Final: + case Version, ConfigV5R1, ConfigV5R2: regular := SpecRegular{ wallet: w, messagesTTL: 60 * 3, // default ttl 3 min @@ -218,16 +218,16 @@ func getSpec(w *Wallet) (any, error) { } switch x := w.ver.(type) { - case ConfigV5Beta: + case ConfigV5R1: if x.NetworkGlobalID == 0 { - return nil, fmt.Errorf("NetworkGlobalID should be set in V5B config") + return nil, fmt.Errorf("NetworkGlobalID should be set in V5 config") } - return &SpecV5Beta{SpecRegular: regular, SpecSeqno: SpecSeqno{seqnoFetcher: seqnoFetcher}, config: x}, nil - case ConfigV5Final: + return &SpecV5R1{SpecRegular: regular, SpecSeqno: SpecSeqno{seqnoFetcher: seqnoFetcher}, config: x}, nil + case ConfigV5R2: if x.NetworkGlobalID == 0 { - return nil, fmt.Errorf("NetworkGlobalID should be set in V5F config") + return nil, fmt.Errorf("NetworkGlobalID should be set in V5 config") } - return &SpecV5Final{SpecRegular: regular, SpecSeqno: SpecSeqno{seqnoFetcher: seqnoFetcher}, config: x}, nil + return &SpecV5R2{SpecRegular: regular, SpecSeqno: SpecSeqno{seqnoFetcher: seqnoFetcher}, config: x}, nil } switch v { @@ -239,10 +239,10 @@ func getSpec(w *Wallet) (any, error) { return &SpecHighloadV2R2{regular, SpecQuery{}}, nil case HighloadV3: return nil, fmt.Errorf("use ConfigHighloadV3 for highload v3 spec") - case V5Beta: - return nil, fmt.Errorf("use ConfigV5Beta for v5b spec") - case V5Final: - return nil, fmt.Errorf("use ConfigV5Final for v5f spec") + case V5R1: + return nil, fmt.Errorf("use ConfigV5R1 for V5 spec") + case V5R2: + return nil, fmt.Errorf("use ConfigV5R2 for V5 spec") } case ConfigHighloadV3: return &SpecHighloadV3{wallet: w, config: v}, nil @@ -343,16 +343,16 @@ func (w *Wallet) PrepareExternalMessageForMany(ctx context.Context, withStateIni var msg *cell.Cell switch v := w.ver.(type) { - case Version, ConfigV5Beta, ConfigV5Final: - if _, ok := v.(ConfigV5Beta); ok { - v = V5Beta + case Version, ConfigV5R1, ConfigV5R2: + if _, ok := v.(ConfigV5R1); ok { + v = V5R1 } - if _, ok := v.(ConfigV5Final); ok { - v = V5Final + if _, ok := v.(ConfigV5R2); ok { + v = V5R2 } switch v { - case V3R2, V3R1, V4R2, V4R1, V5Beta, V5Final: + case V3R2, V3R1, V4R2, V4R1, V5R1, V5R2: msg, err = w.spec.(RegularBuilder).BuildMessage(ctx, !withStateInit, nil, messages) if err != nil { return nil, fmt.Errorf("build message err: %w", err) From 1c01041e636704682b16da5293161e6757e51fe8 Mon Sep 17 00:00:00 2001 From: Totemancer Date: Thu, 4 Jul 2024 15:26:46 +0200 Subject: [PATCH 15/26] Update NFT Item code https://github.com/toncenter/tonweb/blob/master/src/contract/token/nft/NftItem.js#L8C28-L8C988 --- example/deploy-nft-collection/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/deploy-nft-collection/main.go b/example/deploy-nft-collection/main.go index 02299b69..b2cc706b 100644 --- a/example/deploy-nft-collection/main.go +++ b/example/deploy-nft-collection/main.go @@ -65,7 +65,7 @@ func getNFTCollectionCode() *cell.Cell { } func getNFTItemCode() *cell.Cell { - var hexBOC = "b5ee9c7201020d010001d0000114ff00f4a413f4bcf2c80b0102016202030202ce04050009a11f9fe00502012006070201200b0c02d70c8871c02497c0f83434c0c05c6c2497c0f83e903e900c7e800c5c75c87e800c7e800c3c00812ce3850c1b088d148cb1c17cb865407e90350c0408fc00f801b4c7f4cfe08417f30f45148c2ea3a1cc840dd78c9004f80c0d0d0d4d60840bf2c9a884aeb8c097c12103fcbc20080900113e910c1c2ebcb8536001f65135c705f2e191fa4021f001fa40d20031fa00820afaf0801ba121945315a0a1de22d70b01c300209206a19136e220c2fff2e192218e3e821005138d91c85009cf16500bcf16712449145446a0708010c8cb055007cf165005fa0215cb6a12cb1fcb3f226eb39458cf17019132e201c901fb00104794102a375be20a00727082108b77173505c8cbff5004cf1610248040708010c8cb055007cf165005fa0215cb6a12cb1fcb3f226eb39458cf17019132e201c901fb000082028e3526f0018210d53276db103744006d71708010c8cb055007cf165005fa0215cb6a12cb1fcb3f226eb39458cf17019132e201c901fb0093303234e25502f003003b3b513434cffe900835d27080269fc07e90350c04090408f80c1c165b5b60001d00f232cfd633c58073c5b3327b5520" + var hexBOC = "b5ee9c7241020d010001d0000114ff00f4a413f4bcf2c80b0102016202030202ce04050009a11f9fe00502012006070201200b0c02d70c8871c02497c0f83434c0c05c6c2497c0f83e903e900c7e800c5c75c87e800c7e800c3c00812ce3850c1b088d148cb1c17cb865407e90350c0408fc00f801b4c7f4cfe08417f30f45148c2ea3a1cc840dd78c9004f80c0d0d0d4d60840bf2c9a884aeb8c097c12103fcbc20080900113e910c1c2ebcb8536001f65135c705f2e191fa4021f001fa40d20031fa00820afaf0801ba121945315a0a1de22d70b01c300209206a19136e220c2fff2e192218e3e821005138d91c85009cf16500bcf16712449145446a0708010c8cb055007cf165005fa0215cb6a12cb1fcb3f226eb39458cf17019132e201c901fb00104794102a375be20a00727082108b77173505c8cbff5004cf1610248040708010c8cb055007cf165005fa0215cb6a12cb1fcb3f226eb39458cf17019132e201c901fb000082028e3526f0018210d53276db103744006d71708010c8cb055007cf165005fa0215cb6a12cb1fcb3f226eb39458cf17019132e201c901fb0093303234e25502f003003b3b513434cffe900835d27080269fc07e90350c04090408f80c1c165b5b60001d00f232cfd633c58073c5b3327b5520bf75041b" codeCellBytes, _ := hex.DecodeString(hexBOC) codeCell, err := cell.FromBOC(codeCellBytes) From d1d859039ccab5d21388865a16cd1be596ba68ab Mon Sep 17 00:00:00 2001 From: Totemancer Date: Thu, 4 Jul 2024 16:39:49 +0200 Subject: [PATCH 16/26] Add comments near the wallet versions --- ton/wallet/wallet.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ton/wallet/wallet.go b/ton/wallet/wallet.go index cdec4183..cec1c75b 100644 --- a/ton/wallet/wallet.go +++ b/ton/wallet/wallet.go @@ -41,8 +41,8 @@ const ( V3 = V3R2 V4R1 Version = 41 V4R2 Version = 42 - V5R1 Version = 51 - V5R2 Version = 52 + V5R1 Version = 51 // Beta W5 + V5R2 Version = 52 // Final W5 HighloadV2R2 Version = 122 HighloadV2Verified Version = 123 HighloadV3 Version = 300 From 59bf2647c3a5dcd7c6121409f5260cac44314d36 Mon Sep 17 00:00:00 2001 From: AugustineAurelius Date: Fri, 5 Jul 2024 10:15:52 +0300 Subject: [PATCH 17/26] revert dict toStore --- tvm/cell/dict.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tvm/cell/dict.go b/tvm/cell/dict.go index fab7985e..7ac4a5cf 100644 --- a/tvm/cell/dict.go +++ b/tvm/cell/dict.go @@ -510,13 +510,13 @@ func loadLabel(sz uint, loader *Slice, key *Builder) (uint, *Builder, error) { return 0, nil, err } - toStore := make([]byte, 1+int(ln)/8) + var toStore []byte if bitType == 1 { // N of ones - copy(toStore, bytes.Repeat([]byte{0xFF}, 1+(int(ln)/8))) + toStore = bytes.Repeat([]byte{0xFF}, 1+(int(ln)/8)) } else { // N of zeroes - copy(toStore, bytes.Repeat([]byte{0xFF}, 1+(int(ln)/8))) + toStore = bytes.Repeat([]byte{0xFF}, 1+(int(ln)/8)) } err = key.StoreSlice(toStore, uint(ln)) From 6bbba3baf4fafc060d0f056c2b39378d2dd8f150 Mon Sep 17 00:00:00 2001 From: AugustineAurelius Date: Fri, 5 Jul 2024 12:41:36 +0300 Subject: [PATCH 18/26] fix: bytes repeat --- tvm/cell/dict.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvm/cell/dict.go b/tvm/cell/dict.go index 7ac4a5cf..71cef2f5 100644 --- a/tvm/cell/dict.go +++ b/tvm/cell/dict.go @@ -516,7 +516,7 @@ func loadLabel(sz uint, loader *Slice, key *Builder) (uint, *Builder, error) { toStore = bytes.Repeat([]byte{0xFF}, 1+(int(ln)/8)) } else { // N of zeroes - toStore = bytes.Repeat([]byte{0xFF}, 1+(int(ln)/8)) + toStore = bytes.Repeat([]byte{0x00}, 1+(int(ln)/8)) } err = key.StoreSlice(toStore, uint(ln)) From 1cbd66d36eebe0c7b25028282b7086f36ce67eef Mon Sep 17 00:00:00 2001 From: Totemancer Date: Fri, 5 Jul 2024 18:17:39 +0200 Subject: [PATCH 19/26] Finalized V5 Contract Integration --- ton/wallet/address.go | 42 ++++++++-------- ton/wallet/v5beta.go | 90 +++++++++++++++++++++++++++++++++ ton/wallet/v5r1.go | 111 +++++++++++++++++++++++++++++++---------- ton/wallet/v5r2.go | 112 ------------------------------------------ ton/wallet/wallet.go | 32 ++++++------ 5 files changed, 212 insertions(+), 175 deletions(-) create mode 100644 ton/wallet/v5beta.go delete mode 100644 ton/wallet/v5r2.go diff --git a/ton/wallet/address.go b/ton/wallet/address.go index adb304f2..7d73d8ad 100644 --- a/ton/wallet/address.go +++ b/ton/wallet/address.go @@ -55,17 +55,17 @@ func GetStateInit(pubKey ed25519.PublicKey, version VersionConfig, subWallet uin switch ver { case HighloadV3: return nil, fmt.Errorf("use ConfigHighloadV3 for highload v3 spec") + case V5Beta: + return nil, fmt.Errorf("use ConfigV5Beta for V5 spec") case V5R1: return nil, fmt.Errorf("use ConfigV5R1 for V5 spec") - case V5R2: - return nil, fmt.Errorf("use ConfigV5R2 for V5 spec") } case ConfigHighloadV3: ver = HighloadV3 + case ConfigV5Beta: + ver = V5Beta case ConfigV5R1: ver = V5R1 - case ConfigV5R2: - ver = V5R2 } code, ok := walletCode[ver] @@ -88,35 +88,35 @@ func GetStateInit(pubKey ed25519.PublicKey, version VersionConfig, subWallet uin MustStoreSlice(pubKey, 256). MustStoreDict(nil). // empty dict of plugins EndCell() - case V5R1: - config := version.(ConfigV5R1) + case V5Beta: + config := version.(ConfigV5Beta) data = cell.BeginCell(). - MustStoreUInt(0, 33). // seqno - MustStoreInt(int64(config.NetworkGlobalID), 32). - MustStoreInt(int64(config.Workchain), 8). - MustStoreUInt(0, 8). // version of v5 - MustStoreUInt(uint64(subWallet), 32). + MustStoreUInt(0, 33). // seqno + MustStoreInt(int64(config.NetworkGlobalID), 32). // network id + MustStoreInt(int64(config.Workchain), 8). // workchain + MustStoreUInt(0, 8). // version of v5 + MustStoreUInt(uint64(subWallet), 32). // default 0 MustStoreSlice(pubKey, 256). MustStoreDict(nil). // empty dict of plugins EndCell() - case V5R2: - config := version.(ConfigV5R2) + case V5R1: + config := version.(ConfigV5R1) // Create WalletId instance walletId := WalletId{ - NetworkGlobalID: config.NetworkGlobalID, + NetworkGlobalID: config.NetworkGlobalID, // -3 Testnet, -239 Mainnet WorkChain: config.Workchain, - SubwalletNumber: subWallet, - walletVersion: 0, + SubwalletNumber: uint16(subWallet), + WalletVersion: 0, // Wallet Version } data = cell.BeginCell(). - MustStoreBoolBit(true). - MustStoreUInt(0, 32). // seqno - MustStoreUInt(uint64(walletId.Serialized()), 32). // serialize - MustStoreSlice(pubKey, 256). - MustStoreDict(nil). // empty dict of plugins + MustStoreBoolBit(true). // storeUint(1, 1) - boolean flag for context type + MustStoreUInt(0, 32). // Sequence number, hardcoded as 0 + MustStoreUInt(uint64(walletId.Serialized()), 32). // Serializing WalletId into 32-bit integer + MustStoreSlice(pubKey, 256). // Storing the public key + MustStoreDict(nil). // Storing an empty plugins dictionary EndCell() case HighloadV2R2, HighloadV2Verified: data = cell.BeginCell(). diff --git a/ton/wallet/v5beta.go b/ton/wallet/v5beta.go new file mode 100644 index 00000000..99c07c44 --- /dev/null +++ b/ton/wallet/v5beta.go @@ -0,0 +1,90 @@ +package wallet + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/xssnick/tonutils-go/tlb" + "github.com/xssnick/tonutils-go/ton" + + "github.com/xssnick/tonutils-go/tvm/cell" +) + +// https://github.com/tonkeeper/tonkeeper-ton/commit/e8a7f3415e241daf4ac723f273fbc12776663c49#diff-c20d462b2e1ec616bbba2db39acc7a6c61edc3d5e768f5c2034a80169b1a56caR29 +const _V5BetaCodeHex = "b5ee9c7241010101002300084202e4cf3b2f4c6d6a61ea0f2b5447d266785b26af3637db2deee6bcd1aa826f34120dcd8e11" + +type ConfigV5Beta struct { + NetworkGlobalID int32 + Workchain int8 +} + +type SpecV5Beta struct { + SpecRegular + SpecSeqno + + config ConfigV5Beta +} + +func (s *SpecV5Beta) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt, messages []*Message) (_ *cell.Cell, err error) { + // TODO: remove block, now it is here for backwards compatibility + + if len(messages) > 255 { + return nil, errors.New("for this type of wallet max 4 messages can be sent in the same time") + } + + seq, err := s.seqnoFetcher(ctx, s.wallet.subwallet) + if err != nil { + return nil, fmt.Errorf("failed to fetch seqno: %w", err) + } + + actions, err := packV5BetaActions(messages) + if err != nil { + return nil, fmt.Errorf("failed to build actions: %w", err) + } + + payload := cell.BeginCell(). + MustStoreUInt(0x7369676e, 32). // external sign op code + MustStoreInt(int64(s.config.NetworkGlobalID), 32). + MustStoreInt(int64(s.config.Workchain), 8). + MustStoreUInt(0, 8). // version of v5 + MustStoreUInt(uint64(s.wallet.subwallet), 32). + MustStoreUInt(uint64(timeNow().Add(time.Duration(s.messagesTTL)*time.Second).UTC().Unix()), 32). + MustStoreUInt(uint64(seq), 32). + MustStoreBuilder(actions) + + sign := payload.EndCell().Sign(s.wallet.key) + msg := cell.BeginCell().MustStoreBuilder(payload).MustStoreSlice(sign, 512).EndCell() + + return msg, nil +} + +func packV5BetaActions(messages []*Message) (*cell.Builder, error) { + if len(messages) > 255 { + return nil, fmt.Errorf("max 255 messages allowed for v5") + } + + var list = cell.BeginCell().EndCell() + for _, message := range messages { + outMsg, err := tlb.ToCell(message.InternalMessage) + if err != nil { + return nil, err + } + + /* + out_list_empty$_ = OutList 0; + out_list$_ {n:#} prev:^(OutList n) action:OutAction + = OutList (n + 1); + action_send_msg#0ec3c86d mode:(## 8) + out_msg:^(MessageRelaxed Any) = OutAction; + */ + msg := cell.BeginCell().MustStoreUInt(0x0ec3c86d, 32). + MustStoreUInt(uint64(message.Mode), 8). + MustStoreRef(outMsg) + + list = cell.BeginCell().MustStoreRef(list).MustStoreBuilder(msg).EndCell() + } + + return cell.BeginCell().MustStoreUInt(0, 1).MustStoreRef(list), nil +} diff --git a/ton/wallet/v5r1.go b/ton/wallet/v5r1.go index 17f59833..9e57c1cb 100644 --- a/ton/wallet/v5r1.go +++ b/ton/wallet/v5r1.go @@ -12,8 +12,9 @@ import ( "github.com/xssnick/tonutils-go/tvm/cell" ) -// https://github.com/tonkeeper/tonkeeper-ton/commit/e8a7f3415e241daf4ac723f273fbc12776663c49#diff-c20d462b2e1ec616bbba2db39acc7a6c61edc3d5e768f5c2034a80169b1a56caR29 -const _V5R1CodeHex = "b5ee9c7241010101002300084202e4cf3b2f4c6d6a61ea0f2b5447d266785b26af3637db2deee6bcd1aa826f34120dcd8e11" +// Contract source: +// https://github.com/ton-blockchain/wallet-contract-v5/blob/main/build/wallet_v5.compiled.json +const _V5R1CodeHex = "b5ee9c7241021401000281000114ff00f4a413f4bcf2c80b01020120020d020148030402dcd020d749c120915b8f6320d70b1f2082106578746ebd21821073696e74bdb0925f03e082106578746eba8eb48020d72101d074d721fa4030fa44f828fa443058bd915be0ed44d0810141d721f4058307f40e6fa1319130e18040d721707fdb3ce03120d749810280b99130e070e2100f020120050c020120060902016e07080019adce76a2684020eb90eb85ffc00019af1df6a2684010eb90eb858fc00201480a0b0017b325fb51341c75c875c2c7e00011b262fb513435c280200019be5f0f6a2684080a0eb90fa02c0102f20e011e20d70b1f82107369676ebaf2e08a7f0f01e68ef0eda2edfb218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ccf9109a28945f0adb31e1f2c087df02b35007b0f2d0845125baf2e0855036baf2e086f823bbf2d0882292f800de01a47fc8ca00cb1f01cf16c9ed542092f80fde70db3cd81003f6eda2edfb02f404216e926c218e4c0221d73930709421c700b38e2d01d72820761e436c20d749c008f2e09320d74ac002f2e09320d71d06c712c2005230b0f2d089d74cd7393001a4e86c128407bbf2e093d74ac000f2e093ed55e2d20001c000915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74a111213009601fa4001fa44f828fa443058baf2e091ed44d0810141d718f405049d7fc8ca0040048307f453f2e08b8e14038307f45bf2e08c22d70a00216e01b3b0f2d090e2c85003cf1612f400c9ed54007230d72c08248e2d21f2e092d200ed44d0d2005113baf2d08f54503091319c01810140d721d70a00f2e08ee2c8ca0058cf16c9ed5493f2c08de20010935bdb31e1d74cd0b4d6c35e" type ConfigV5R1 struct { NetworkGlobalID int32 @@ -27,11 +28,57 @@ type SpecV5R1 struct { config ConfigV5R1 } -func (s *SpecV5R1) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt, messages []*Message) (_ *cell.Cell, err error) { - // TODO: remove block, now it is here for backwards compatibility +// Source: https://github.com/tonkeeper/tonkeeper-ton/commit/d9aec6adfdb853eb37e0bba7453d83ae52e2a170#diff-c8ee60dec2f4e3ee55ad5e40f56fd9a104f21df78086a114d33d62e4fa0ffee6R139 +/* + * schema: + * wallet_id -- int32 + * wallet_id = global_id ^ context_id + * context_id_client$1 = wc:int8 wallet_version:uint8 counter:uint15 + * context_id_backoffice$0 = counter:uint31 + * + * + * calculated default values serialisation: + * + * global_id = -239, workchain = 0, wallet_version = 0', subwallet_number = 0 (client context) + * gives wallet_id = 2147483409 + * + * global_id = -239, workchain = -1, wallet_version = 0', subwallet_number = 0 (client context) + * gives wallet_id = 8388369 + * + * global_id = -3, workchain = 0, wallet_version = 0', subwallet_number = 0 (client context) + * gives wallet_id = 2147483645 + * + * global_id = -3, workchain = -1, wallet_version = 0', subwallet_number = 0 (client context) + * gives wallet_id = 8388605 + */ +// Function to generate the context ID based on the given workchain +func genContextID(workchain int8) uint32 { + var context uint32 + + // Convert workchain to uint32 after ensuring it's correctly handled as an 8-bit value + context |= 1 << 31 // Write 1 bit as 1 at the leftmost bit + context |= (uint32(workchain) & 0xFF) << 23 // Write 8 bits of workchain, shifted to position + context |= uint32(0) << 15 // Write 8 bits of 0 (wallet version) + context |= uint32(0) // Write 15 bits of 0 (subwallet number) + + return context +} + +type WalletId struct { + NetworkGlobalID int32 + WorkChain int8 + SubwalletNumber uint16 + WalletVersion uint8 +} +func (w WalletId) Serialized() uint32 { + context := genContextID(w.WorkChain) + return uint32(int32(context) ^ w.NetworkGlobalID) +} + +func (s *SpecV5R1) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt, messages []*Message) (_ *cell.Cell, err error) { if len(messages) > 255 { - return nil, errors.New("for this type of wallet max 4 messages can be sent in the same time") + return nil, errors.New("for this type of wallet max 255 messages can be sent at the same time") } seq, err := s.seqnoFetcher(ctx, s.wallet.subwallet) @@ -44,15 +91,19 @@ func (s *SpecV5R1) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt, return nil, fmt.Errorf("failed to build actions: %w", err) } + walletId := WalletId{ + NetworkGlobalID: s.config.NetworkGlobalID, + WorkChain: s.config.Workchain, + SubwalletNumber: uint16(s.wallet.subwallet), + WalletVersion: 0, + } + payload := cell.BeginCell(). - MustStoreUInt(0x7369676e, 32). // external sign op code - MustStoreInt(int64(s.config.NetworkGlobalID), 32). - MustStoreInt(int64(s.config.Workchain), 8). - MustStoreUInt(0, 8). // version of v5 - MustStoreUInt(uint64(s.wallet.subwallet), 32). - MustStoreUInt(uint64(timeNow().Add(time.Duration(s.messagesTTL)*time.Second).UTC().Unix()), 32). - MustStoreUInt(uint64(seq), 32). - MustStoreBuilder(actions) + MustStoreUInt(0x7369676e, 32). // external sign op code + MustStoreUInt(uint64(walletId.Serialized()), 32). // serialized WalletId + MustStoreUInt(uint64(time.Now().Add(time.Duration(s.messagesTTL)*time.Second).UTC().Unix()), 32). // validUntil + MustStoreUInt(uint64(seq), 32). // seq (block) + MustStoreBuilder(actions) // Action list sign := payload.EndCell().Sign(s.wallet.key) msg := cell.BeginCell().MustStoreBuilder(payload).MustStoreSlice(sign, 512).EndCell() @@ -60,9 +111,23 @@ func (s *SpecV5R1) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt, return msg, nil } -func packV5R1Actions(messages []*Message) (*cell.Builder, error) { +// Validate messages +func validateMessageFields(messages []*Message) error { if len(messages) > 255 { - return nil, fmt.Errorf("max 255 messages allowed for v5") + return fmt.Errorf("max 255 messages allowed for v5") + } + for _, message := range messages { + if message.InternalMessage == nil { + return fmt.Errorf("internal message cannot be nil") + } + } + return nil +} + +// Pack Actions +func packV5R1Actions(messages []*Message) (*cell.Builder, error) { + if err := validateMessageFields(messages); err != nil { + return nil, err } var list = cell.BeginCell().EndCell() @@ -72,19 +137,13 @@ func packV5R1Actions(messages []*Message) (*cell.Builder, error) { return nil, err } - /* - out_list_empty$_ = OutList 0; - out_list$_ {n:#} prev:^(OutList n) action:OutAction - = OutList (n + 1); - action_send_msg#0ec3c86d mode:(## 8) - out_msg:^(MessageRelaxed Any) = OutAction; - */ - msg := cell.BeginCell().MustStoreUInt(0x0ec3c86d, 32). - MustStoreUInt(uint64(message.Mode), 8). - MustStoreRef(outMsg) + msg := cell.BeginCell().MustStoreUInt(0x0ec3c86d, 32). // action_send_msg prefix + MustStoreUInt(uint64(message.Mode), 8). // mode + MustStoreRef(outMsg) // message reference list = cell.BeginCell().MustStoreRef(list).MustStoreBuilder(msg).EndCell() } - return cell.BeginCell().MustStoreUInt(0, 1).MustStoreRef(list), nil + // Ensure the action list ends with 0, 1 as per the new specification + return cell.BeginCell().MustStoreUInt(1, 1).MustStoreRef(list).MustStoreUInt(0, 1), nil } diff --git a/ton/wallet/v5r2.go b/ton/wallet/v5r2.go deleted file mode 100644 index 1fe33e03..00000000 --- a/ton/wallet/v5r2.go +++ /dev/null @@ -1,112 +0,0 @@ -package wallet - -import ( - "context" - "errors" - "fmt" - "time" - - "github.com/xssnick/tonutils-go/tlb" - "github.com/xssnick/tonutils-go/ton" - - "github.com/xssnick/tonutils-go/tvm/cell" -) - -// https://github.com/ton-blockchain/wallet-contract-v5/blob/main/build/wallet_v5.compiled.json -const _V5R2CodeHex = "b5ee9c7241021401000281000114ff00f4a413f4bcf2c80b01020120020d020148030402dcd020d749c120915b8f6320d70b1f2082106578746ebd21821073696e74bdb0925f03e082106578746eba8eb48020d72101d074d721fa4030fa44f828fa443058bd915be0ed44d0810141d721f4058307f40e6fa1319130e18040d721707fdb3ce03120d749810280b99130e070e2100f020120050c020120060902016e07080019adce76a2684020eb90eb85ffc00019af1df6a2684010eb90eb858fc00201480a0b0017b325fb51341c75c875c2c7e00011b262fb513435c280200019be5f0f6a2684080a0eb90fa02c0102f20e011e20d70b1f82107369676ebaf2e08a7f0f01e68ef0eda2edfb218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ccf9109a28945f0adb31e1f2c087df02b35007b0f2d0845125baf2e0855036baf2e086f823bbf2d0882292f800de01a47fc8ca00cb1f01cf16c9ed542092f80fde70db3cd81003f6eda2edfb02f404216e926c218e4c0221d73930709421c700b38e2d01d72820761e436c20d749c008f2e09320d74ac002f2e09320d71d06c712c2005230b0f2d089d74cd7393001a4e86c128407bbf2e093d74ac000f2e093ed55e2d20001c000915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74a111213009601fa4001fa44f828fa443058baf2e091ed44d0810141d718f405049d7fc8ca0040048307f453f2e08b8e14038307f45bf2e08c22d70a00216e01b3b0f2d090e2c85003cf1612f400c9ed54007230d72c08248e2d21f2e092d200ed44d0d2005113baf2d08f54503091319c01810140d721d70a00f2e08ee2c8ca0058cf16c9ed5493f2c08de20010935bdb31e1d74cd0b4d6c35e" - -type ConfigV5R2 struct { - NetworkGlobalID int32 - Workchain int8 -} - -type SpecV5R2 struct { - SpecRegular - SpecSeqno - - config ConfigV5R2 -} - -type WalletId struct { - NetworkGlobalID int32 - WorkChain int8 - SubwalletNumber uint32 - walletVersion uint8 -} - -func (w WalletId) Serialized() uint32 { - // Serialize WalletId into a 32-bit integer - return uint32(w.NetworkGlobalID)<<24 | uint32(w.WorkChain)<<16 | w.SubwalletNumber -} - -func (s *SpecV5R2) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt, messages []*Message) (_ *cell.Cell, err error) { - if len(messages) > 255 { - return nil, errors.New("for this type of wallet max 255 messages can be sent at the same time") - } - - seq, err := s.seqnoFetcher(ctx, s.wallet.subwallet) - if err != nil { - return nil, fmt.Errorf("failed to fetch seqno: %w", err) - } - - actions, err := packV5R2Actions(messages) - if err != nil { - return nil, fmt.Errorf("failed to build actions: %w", err) - } - - walletId := WalletId{ - NetworkGlobalID: s.config.NetworkGlobalID, - WorkChain: s.config.Workchain, - SubwalletNumber: s.wallet.subwallet, - walletVersion: 0, - } - - payload := cell.BeginCell(). - MustStoreUInt(0x7369676e, 32). // external sign op code - MustStoreUInt(uint64(walletId.Serialized()), 32). // serialized WalletId - MustStoreUInt(uint64(time.Now().Add(time.Duration(s.messagesTTL)*time.Second).UTC().Unix()), 32). // validUntil - MustStoreUInt(uint64(seq), 32). // seq (block) - MustStoreBuilder(actions) // Action list - - sign := payload.EndCell().Sign(s.wallet.key) - msg := cell.BeginCell().MustStoreBuilder(payload).MustStoreSlice(sign, 512).EndCell() - - return msg, nil -} - -// Validate messages -func validateMessageFields(messages []*Message) error { - if len(messages) > 255 { - return fmt.Errorf("max 255 messages allowed for v5") - } - for _, message := range messages { - if message.InternalMessage == nil { - return fmt.Errorf("internal message cannot be nil") - } - } - return nil -} - -// Pack Actions -func packV5R2Actions(messages []*Message) (*cell.Builder, error) { - if err := validateMessageFields(messages); err != nil { - return nil, err - } - - var list = cell.BeginCell().EndCell() - for _, message := range messages { - outMsg, err := tlb.ToCell(message.InternalMessage) - if err != nil { - return nil, err - } - - msg := cell.BeginCell().MustStoreUInt(0x0ec3c86d, 32). // action_send_msg prefix - MustStoreUInt(uint64(message.Mode), 8). // mode - MustStoreRef(outMsg) // message reference - - list = cell.BeginCell().MustStoreRef(list).MustStoreBuilder(msg).EndCell() - } - - // Ensure the action list ends with 0, 1 as per the new specification - return cell.BeginCell().MustStoreUInt(1, 1).MustStoreRef(list).MustStoreUInt(0, 1), nil -} diff --git a/ton/wallet/wallet.go b/ton/wallet/wallet.go index cec1c75b..6e4e2cd3 100644 --- a/ton/wallet/wallet.go +++ b/ton/wallet/wallet.go @@ -41,8 +41,8 @@ const ( V3 = V3R2 V4R1 Version = 41 V4R2 Version = 42 - V5R1 Version = 51 // Beta W5 - V5R2 Version = 52 // Final W5 + V5Beta Version = 51 // W5 Beta + V5R1 Version = 52 // W5 Final HighloadV2R2 Version = 122 HighloadV2Verified Version = 123 HighloadV3 Version = 300 @@ -85,8 +85,8 @@ var ( V2R1: _V2R1CodeHex, V2R2: _V2R2CodeHex, V3R1: _V3R1CodeHex, V3R2: _V3R2CodeHex, V4R1: _V4R1CodeHex, V4R2: _V4R2CodeHex, + V5Beta: _V5BetaCodeHex, V5R1: _V5R1CodeHex, - V5R2: _V5R2CodeHex, HighloadV2R2: _HighloadV2R2CodeHex, HighloadV2Verified: _HighloadV2VerifiedCodeHex, HighloadV3: _HighloadV3CodeHex, Lockup: _LockupCodeHex, @@ -162,8 +162,8 @@ func FromPrivateKey(api TonAPI, key ed25519.PrivateKey, version VersionConfig) ( // default subwallet depends on wallet type switch version.(type) { + case ConfigV5Beta: case ConfigV5R1: - case ConfigV5R2: subwallet = 0 } @@ -190,7 +190,7 @@ func FromPrivateKey(api TonAPI, key ed25519.PrivateKey, version VersionConfig) ( func getSpec(w *Wallet) (any, error) { switch v := w.ver.(type) { - case Version, ConfigV5R1, ConfigV5R2: + case Version, ConfigV5Beta, ConfigV5R1: regular := SpecRegular{ wallet: w, messagesTTL: 60 * 3, // default ttl 3 min @@ -218,16 +218,16 @@ func getSpec(w *Wallet) (any, error) { } switch x := w.ver.(type) { - case ConfigV5R1: + case ConfigV5Beta: if x.NetworkGlobalID == 0 { return nil, fmt.Errorf("NetworkGlobalID should be set in V5 config") } - return &SpecV5R1{SpecRegular: regular, SpecSeqno: SpecSeqno{seqnoFetcher: seqnoFetcher}, config: x}, nil - case ConfigV5R2: + return &SpecV5Beta{SpecRegular: regular, SpecSeqno: SpecSeqno{seqnoFetcher: seqnoFetcher}, config: x}, nil + case ConfigV5R1: if x.NetworkGlobalID == 0 { return nil, fmt.Errorf("NetworkGlobalID should be set in V5 config") } - return &SpecV5R2{SpecRegular: regular, SpecSeqno: SpecSeqno{seqnoFetcher: seqnoFetcher}, config: x}, nil + return &SpecV5R1{SpecRegular: regular, SpecSeqno: SpecSeqno{seqnoFetcher: seqnoFetcher}, config: x}, nil } switch v { @@ -239,10 +239,10 @@ func getSpec(w *Wallet) (any, error) { return &SpecHighloadV2R2{regular, SpecQuery{}}, nil case HighloadV3: return nil, fmt.Errorf("use ConfigHighloadV3 for highload v3 spec") + case V5Beta: + return nil, fmt.Errorf("use ConfigV5Beta for V5 spec") case V5R1: return nil, fmt.Errorf("use ConfigV5R1 for V5 spec") - case V5R2: - return nil, fmt.Errorf("use ConfigV5R2 for V5 spec") } case ConfigHighloadV3: return &SpecHighloadV3{wallet: w, config: v}, nil @@ -343,16 +343,16 @@ func (w *Wallet) PrepareExternalMessageForMany(ctx context.Context, withStateIni var msg *cell.Cell switch v := w.ver.(type) { - case Version, ConfigV5R1, ConfigV5R2: + case Version, ConfigV5Beta, ConfigV5R1: + if _, ok := v.(ConfigV5Beta); ok { + v = V5Beta + } if _, ok := v.(ConfigV5R1); ok { v = V5R1 } - if _, ok := v.(ConfigV5R2); ok { - v = V5R2 - } switch v { - case V3R2, V3R1, V4R2, V4R1, V5R1, V5R2: + case V3R2, V3R1, V4R2, V4R1, V5Beta, V5R1: msg, err = w.spec.(RegularBuilder).BuildMessage(ctx, !withStateInit, nil, messages) if err != nil { return nil, fmt.Errorf("build message err: %w", err) From 98d6edeb0844fad7383270d8d8e80f31eb2e7286 Mon Sep 17 00:00:00 2001 From: Oleg Baranov Date: Fri, 12 Jul 2024 12:31:35 +0400 Subject: [PATCH 20/26] Jetton accept example & address Equals func --- address/addr.go | 5 +++++ example/accept-payments/main.go | 39 ++++++++++++++++++++++++++++----- tlb/transaction.go | 10 ++++++--- ton/jetton/jetton.go | 11 ++++++++++ 4 files changed, 56 insertions(+), 9 deletions(-) diff --git a/address/addr.go b/address/addr.go index 5822628a..fd661377 100644 --- a/address/addr.go +++ b/address/addr.go @@ -1,6 +1,7 @@ package address import ( + "bytes" "encoding/base64" "encoding/binary" "encoding/hex" @@ -337,3 +338,7 @@ func (a *Address) Workchain() int32 { func (a *Address) Data() []byte { return a.data } + +func (a *Address) Equals(b *Address) bool { + return a.workchain == b.workchain && bytes.Equal(a.data, b.data) +} diff --git a/example/accept-payments/main.go b/example/accept-payments/main.go index 97765f25..c9620276 100644 --- a/example/accept-payments/main.go +++ b/example/accept-payments/main.go @@ -6,6 +6,7 @@ import ( "github.com/xssnick/tonutils-go/liteclient" "github.com/xssnick/tonutils-go/tlb" "github.com/xssnick/tonutils-go/ton" + "github.com/xssnick/tonutils-go/ton/jetton" "log" ) @@ -26,19 +27,17 @@ func main() { } // initialize ton api lite connection wrapper with full proof checks - api := ton.NewAPIClient(client, ton.ProofCheckPolicySecure).WithRetry() + api := ton.NewAPIClient(client, ton.ProofCheckPolicyFast).WithRetry() api.SetTrustedBlockFromConfig(cfg) - log.Println("fetching and checking proofs since config init block, it may take near a minute...") master, err := api.CurrentMasterchainInfo(context.Background()) // we fetch block just to trigger chain proof check if err != nil { log.Fatalln("get masterchain info err: ", err.Error()) return } - log.Println("master proof checks are completed successfully, now communication is 100% safe!") // address on which we are accepting payments - treasuryAddress := address.MustParseAddr("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N") + treasuryAddress := address.MustParseAddr("EQAYqo4u7VF0fa4DPAebk4g9lBytj2VFny7pzXR0trjtXQaO") acc, err := api.GetAccount(context.Background(), master, treasuryAddress) if err != nil { @@ -58,10 +57,38 @@ func main() { log.Println("waiting for transfers...") + // USDT master contract addr, but can be any jetton + usdt := jetton.NewJettonMasterClient(api, address.MustParseAddr("EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs")) + // get our jetton wallet address + treasuryJettonWallet, err := usdt.GetJettonWalletAtBlock(context.Background(), treasuryAddress, master) + if err != nil { + log.Fatalln("get jetton wallet address err: ", err.Error()) + return + } + // listen for new transactions from channel for tx := range transactions { - // process transaction here - log.Println(tx.String()) + // only internal messages can increase the balance + if tx.IO.In != nil && tx.IO.In.MsgType == tlb.MsgTypeInternal { + ti := tx.IO.In.AsInternal() + src := ti.SrcAddr + + // verify that sender is our jetton wallet + if ti.SrcAddr.Equals(treasuryJettonWallet.Address()) { + var transfer jetton.TransferNotification + if err = tlb.LoadFromCell(&transfer, ti.Body.BeginParse()); err == nil { + // convert decimals to 6 for USDT (it can be fetched from jetton details too), default is 9 + amt := tlb.MustFromNano(transfer.Amount.Nano(), 6) + + // reassign sender to real jetton sender instead of its jetton wallet contract + src = transfer.Sender + log.Println("received", amt.String(), "USDT from", src.String()) + } + } + + // show received ton amount + log.Println("received", ti.Amount.String(), "TON from", src.String()) + } // update last processed lt and save it in db lastProcessedLT = tx.LT diff --git a/tlb/transaction.go b/tlb/transaction.go index 974aa9a1..76b9da44 100644 --- a/tlb/transaction.go +++ b/tlb/transaction.go @@ -1,6 +1,7 @@ package tlb import ( + "encoding/hex" "fmt" "math/big" "reflect" @@ -290,17 +291,20 @@ func (t *Transaction) String() string { case TransactionDescriptionOrdinary: } if t.IO.In != nil { + build += fmt.Sprintf("LT: %d", t.LT) + if t.IO.In.MsgType == MsgTypeInternal { in = t.IO.In.AsInternal().Amount.Nano() - } - if in.Cmp(big.NewInt(0)) != 0 { intTx := t.IO.In.AsInternal() - build += fmt.Sprintf("LT: %d, In: %s TON, From %s", t.LT, FromNanoTON(in).String(), intTx.SrcAddr) + build += fmt.Sprintf(", In: %s TON, From %s", FromNanoTON(in).String(), intTx.SrcAddr) comment := intTx.Comment() if comment != "" { build += ", Comment: " + comment } + } else if t.IO.In.MsgType == MsgTypeExternalIn { + exTx := t.IO.In.AsExternalIn() + build += ", ExternalIn, hash: " + hex.EncodeToString(exTx.Body.Hash()) } } diff --git a/ton/jetton/jetton.go b/ton/jetton/jetton.go index fa51471a..554d5f93 100644 --- a/ton/jetton/jetton.go +++ b/ton/jetton/jetton.go @@ -2,6 +2,7 @@ package jetton import ( "context" + "errors" "fmt" "math/big" @@ -19,6 +20,8 @@ type TonApi interface { SubscribeOnTransactions(workerCtx context.Context, addr *address.Address, lastProcessedLT uint64, channel chan<- *tlb.Transaction) } +var ErrInvalidTransfer = errors.New("transfer is not verified") + type MintPayloadMasterMsg struct { Opcode uint32 `tlb:"## 32"` QueryID uint64 `tlb:"## 64"` @@ -34,6 +37,14 @@ type MintPayload struct { MasterMsg MintPayloadMasterMsg `tlb:"^"` } +type TransferNotification struct { + _ tlb.Magic `tlb:"#7362d09c"` + QueryID uint64 `tlb:"## 64"` + Amount tlb.Coins `tlb:"."` + Sender *address.Address `tlb:"addr"` + ForwardPayload *cell.Cell `tlb:"either . ^"` +} + type Data struct { TotalSupply *big.Int Mintable bool From 4cb1aff883c03162585adc91a27c397a62d5e5d0 Mon Sep 17 00:00:00 2001 From: Totemancer Date: Sat, 13 Jul 2024 01:19:39 +0200 Subject: [PATCH 21/26] Updated the denominator for the NFT collection deployment example Change the denominator to 100 for a more human-readable format in NFT collections. --- example/deploy-nft-collection/main.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/example/deploy-nft-collection/main.go b/example/deploy-nft-collection/main.go index b2cc706b..c3029e10 100644 --- a/example/deploy-nft-collection/main.go +++ b/example/deploy-nft-collection/main.go @@ -86,9 +86,9 @@ func getContractData(collectionOwnerAddr, royaltyAddr *address.Address) *cell.Ce // = Storage; royalty := cell.BeginCell(). - MustStoreUInt(50, 16). // 5% royalty - MustStoreUInt(1000, 16). - MustStoreAddr(royaltyAddr). + MustStoreUInt(5, 16). // 5% royalty + MustStoreUInt(100, 16). // denominator + MustStoreAddr(royaltyAddr). // fee addr destination EndCell() // collection data From 33351bd87c2f595ac2d45223f856da0948b6e942 Mon Sep 17 00:00:00 2001 From: Oleg Baranov Date: Thu, 18 Jul 2024 17:57:08 +0400 Subject: [PATCH 22/26] Accept jetton payments example --- example/accept-payments/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/accept-payments/main.go b/example/accept-payments/main.go index c9620276..a2fd672a 100644 --- a/example/accept-payments/main.go +++ b/example/accept-payments/main.go @@ -73,7 +73,7 @@ func main() { ti := tx.IO.In.AsInternal() src := ti.SrcAddr - // verify that sender is our jetton wallet + // verify that event sender is our jetton wallet if ti.SrcAddr.Equals(treasuryJettonWallet.Address()) { var transfer jetton.TransferNotification if err = tlb.LoadFromCell(&transfer, ti.Body.BeginParse()); err == nil { From c45f9d4c2ae69265c2992507cd45b2e4f406fdbb Mon Sep 17 00:00:00 2001 From: Oleg Baranov Date: Thu, 18 Jul 2024 17:57:32 +0400 Subject: [PATCH 23/26] Wallet V5 naming refactoring --- ton/wallet/address.go | 26 ++++++------- ton/wallet/integration_test.go | 19 +++++++-- ton/wallet/v5beta.go | 26 +++++++------ ton/wallet/v5r1.go | 71 ++++++++++++++-------------------- ton/wallet/wallet.go | 44 ++++++++++----------- 5 files changed, 95 insertions(+), 91 deletions(-) diff --git a/ton/wallet/address.go b/ton/wallet/address.go index 7d73d8ad..a3700618 100644 --- a/ton/wallet/address.go +++ b/ton/wallet/address.go @@ -55,17 +55,17 @@ func GetStateInit(pubKey ed25519.PublicKey, version VersionConfig, subWallet uin switch ver { case HighloadV3: return nil, fmt.Errorf("use ConfigHighloadV3 for highload v3 spec") - case V5Beta: - return nil, fmt.Errorf("use ConfigV5Beta for V5 spec") - case V5R1: - return nil, fmt.Errorf("use ConfigV5R1 for V5 spec") + case V5R1Beta: + return nil, fmt.Errorf("use ConfigV5R1Beta for V5 spec") + case V5R1Final: + return nil, fmt.Errorf("use ConfigV5R1Final for V5 spec") } case ConfigHighloadV3: ver = HighloadV3 - case ConfigV5Beta: - ver = V5Beta - case ConfigV5R1: - ver = V5R1 + case ConfigV5R1Beta: + ver = V5R1Beta + case ConfigV5R1Final: + ver = V5R1Final } code, ok := walletCode[ver] @@ -88,8 +88,8 @@ func GetStateInit(pubKey ed25519.PublicKey, version VersionConfig, subWallet uin MustStoreSlice(pubKey, 256). MustStoreDict(nil). // empty dict of plugins EndCell() - case V5Beta: - config := version.(ConfigV5Beta) + case V5R1Beta: + config := version.(ConfigV5R1Beta) data = cell.BeginCell(). MustStoreUInt(0, 33). // seqno @@ -100,11 +100,11 @@ func GetStateInit(pubKey ed25519.PublicKey, version VersionConfig, subWallet uin MustStoreSlice(pubKey, 256). MustStoreDict(nil). // empty dict of plugins EndCell() - case V5R1: - config := version.(ConfigV5R1) + case V5R1Final: + config := version.(ConfigV5R1Final) // Create WalletId instance - walletId := WalletId{ + walletId := V5R1ID{ NetworkGlobalID: config.NetworkGlobalID, // -3 Testnet, -239 Mainnet WorkChain: config.Workchain, SubwalletNumber: uint16(subWallet), diff --git a/ton/wallet/integration_test.go b/ton/wallet/integration_test.go index f785bb05..80ce3fab 100644 --- a/ton/wallet/integration_test.go +++ b/ton/wallet/integration_test.go @@ -86,7 +86,7 @@ func Test_HighloadHeavyTransfer(t *testing.T) { func Test_V5HeavyTransfer(t *testing.T) { seed := strings.Split(_seed, " ") - w, err := FromSeed(api, seed, ConfigV5R1{ + w, err := FromSeed(api, seed, ConfigV5R1Final{ NetworkGlobalID: MainnetGlobalID, }) if err != nil { @@ -114,7 +114,9 @@ func Test_V5HeavyTransfer(t *testing.T) { func Test_WalletTransfer(t *testing.T) { seed := strings.Split(_seed, " ") - for _, v := range []VersionConfig{ConfigV5R1{ + for _, v := range []VersionConfig{ConfigV5R1Final{ + NetworkGlobalID: TestnetGlobalID, + }, ConfigV5R1Beta{ NetworkGlobalID: TestnetGlobalID, }, V3R2, V4R2, HighloadV2R2, V3R1, V4R1, HighloadV2Verified, ConfigHighloadV3{ MessageTTL: 120, @@ -164,11 +166,22 @@ func Test_WalletTransfer(t *testing.T) { comment := randString(150) addr := address.MustParseAddr("EQA8aJTl0jfFnUZBJjTeUxu9OcbsoPBp9UcHE9upyY_X35kE") if balance.Nano().Uint64() >= 3000000 { - err = w.Transfer(ctx, addr, tlb.MustFromTON("0.003"), comment, true) + tr, err := w.BuildTransfer(addr, tlb.MustFromTON("0.003"), false, comment) + if err != nil { + t.Fatal("Build transfer err:", err.Error()) + return + } + + tx, _, err := w.SendManyWaitTransaction(ctx, []*Message{tr}) if err != nil { t.Fatal("Transfer err:", err.Error()) return } + + if tx.OutMsgCount == 0 { + t.Fatal("Out msg is 0:", ver) + return + } } else { t.Fatal("not enough balance") return diff --git a/ton/wallet/v5beta.go b/ton/wallet/v5beta.go index 99c07c44..f31db570 100644 --- a/ton/wallet/v5beta.go +++ b/ton/wallet/v5beta.go @@ -4,30 +4,34 @@ import ( "context" "errors" "fmt" + "github.com/xssnick/tonutils-go/tlb" "time" - "github.com/xssnick/tonutils-go/tlb" "github.com/xssnick/tonutils-go/ton" "github.com/xssnick/tonutils-go/tvm/cell" ) // https://github.com/tonkeeper/tonkeeper-ton/commit/e8a7f3415e241daf4ac723f273fbc12776663c49#diff-c20d462b2e1ec616bbba2db39acc7a6c61edc3d5e768f5c2034a80169b1a56caR29 -const _V5BetaCodeHex = "b5ee9c7241010101002300084202e4cf3b2f4c6d6a61ea0f2b5447d266785b26af3637db2deee6bcd1aa826f34120dcd8e11" +const _V5R1BetaCodeHex = "b5ee9c7241010101002300084202e4cf3b2f4c6d6a61ea0f2b5447d266785b26af3637db2deee6bcd1aa826f34120dcd8e11" -type ConfigV5Beta struct { +type ConfigV5R1Beta struct { NetworkGlobalID int32 Workchain int8 } -type SpecV5Beta struct { +type SpecV5R1Beta struct { SpecRegular SpecSeqno - config ConfigV5Beta + config ConfigV5R1Beta } -func (s *SpecV5Beta) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt, messages []*Message) (_ *cell.Cell, err error) { +func (c ConfigV5R1Beta) String() string { + return "V5R1Beta" +} + +func (s *SpecV5R1Beta) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt, messages []*Message) (_ *cell.Cell, err error) { // TODO: remove block, now it is here for backwards compatibility if len(messages) > 255 { @@ -61,8 +65,8 @@ func (s *SpecV5Beta) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt } func packV5BetaActions(messages []*Message) (*cell.Builder, error) { - if len(messages) > 255 { - return nil, fmt.Errorf("max 255 messages allowed for v5") + if err := validateMessageFields(messages); err != nil { + return nil, err } var list = cell.BeginCell().EndCell() @@ -79,9 +83,9 @@ func packV5BetaActions(messages []*Message) (*cell.Builder, error) { action_send_msg#0ec3c86d mode:(## 8) out_msg:^(MessageRelaxed Any) = OutAction; */ - msg := cell.BeginCell().MustStoreUInt(0x0ec3c86d, 32). - MustStoreUInt(uint64(message.Mode), 8). - MustStoreRef(outMsg) + msg := cell.BeginCell().MustStoreUInt(0x0ec3c86d, 32). // action_send_msg prefix + MustStoreUInt(uint64(message.Mode), 8). // mode + MustStoreRef(outMsg) // message reference list = cell.BeginCell().MustStoreRef(list).MustStoreBuilder(msg).EndCell() } diff --git a/ton/wallet/v5r1.go b/ton/wallet/v5r1.go index 9e57c1cb..1e97fc16 100644 --- a/ton/wallet/v5r1.go +++ b/ton/wallet/v5r1.go @@ -14,69 +14,49 @@ import ( // Contract source: // https://github.com/ton-blockchain/wallet-contract-v5/blob/main/build/wallet_v5.compiled.json -const _V5R1CodeHex = "b5ee9c7241021401000281000114ff00f4a413f4bcf2c80b01020120020d020148030402dcd020d749c120915b8f6320d70b1f2082106578746ebd21821073696e74bdb0925f03e082106578746eba8eb48020d72101d074d721fa4030fa44f828fa443058bd915be0ed44d0810141d721f4058307f40e6fa1319130e18040d721707fdb3ce03120d749810280b99130e070e2100f020120050c020120060902016e07080019adce76a2684020eb90eb85ffc00019af1df6a2684010eb90eb858fc00201480a0b0017b325fb51341c75c875c2c7e00011b262fb513435c280200019be5f0f6a2684080a0eb90fa02c0102f20e011e20d70b1f82107369676ebaf2e08a7f0f01e68ef0eda2edfb218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ccf9109a28945f0adb31e1f2c087df02b35007b0f2d0845125baf2e0855036baf2e086f823bbf2d0882292f800de01a47fc8ca00cb1f01cf16c9ed542092f80fde70db3cd81003f6eda2edfb02f404216e926c218e4c0221d73930709421c700b38e2d01d72820761e436c20d749c008f2e09320d74ac002f2e09320d71d06c712c2005230b0f2d089d74cd7393001a4e86c128407bbf2e093d74ac000f2e093ed55e2d20001c000915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74a111213009601fa4001fa44f828fa443058baf2e091ed44d0810141d718f405049d7fc8ca0040048307f453f2e08b8e14038307f45bf2e08c22d70a00216e01b3b0f2d090e2c85003cf1612f400c9ed54007230d72c08248e2d21f2e092d200ed44d0d2005113baf2d08f54503091319c01810140d721d70a00f2e08ee2c8ca0058cf16c9ed5493f2c08de20010935bdb31e1d74cd0b4d6c35e" +const _V5R1FinalCodeHex = "b5ee9c7241021401000281000114ff00f4a413f4bcf2c80b01020120020d020148030402dcd020d749c120915b8f6320d70b1f2082106578746ebd21821073696e74bdb0925f03e082106578746eba8eb48020d72101d074d721fa4030fa44f828fa443058bd915be0ed44d0810141d721f4058307f40e6fa1319130e18040d721707fdb3ce03120d749810280b99130e070e2100f020120050c020120060902016e07080019adce76a2684020eb90eb85ffc00019af1df6a2684010eb90eb858fc00201480a0b0017b325fb51341c75c875c2c7e00011b262fb513435c280200019be5f0f6a2684080a0eb90fa02c0102f20e011e20d70b1f82107369676ebaf2e08a7f0f01e68ef0eda2edfb218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ccf9109a28945f0adb31e1f2c087df02b35007b0f2d0845125baf2e0855036baf2e086f823bbf2d0882292f800de01a47fc8ca00cb1f01cf16c9ed542092f80fde70db3cd81003f6eda2edfb02f404216e926c218e4c0221d73930709421c700b38e2d01d72820761e436c20d749c008f2e09320d74ac002f2e09320d71d06c712c2005230b0f2d089d74cd7393001a4e86c128407bbf2e093d74ac000f2e093ed55e2d20001c000915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74a111213009601fa4001fa44f828fa443058baf2e091ed44d0810141d718f405049d7fc8ca0040048307f453f2e08b8e14038307f45bf2e08c22d70a00216e01b3b0f2d090e2c85003cf1612f400c9ed54007230d72c08248e2d21f2e092d200ed44d0d2005113baf2d08f54503091319c01810140d721d70a00f2e08ee2c8ca0058cf16c9ed5493f2c08de20010935bdb31e1d74cd0b4d6c35e" -type ConfigV5R1 struct { +type ConfigV5R1Final struct { NetworkGlobalID int32 Workchain int8 } -type SpecV5R1 struct { +type SpecV5R1Final struct { SpecRegular SpecSeqno - config ConfigV5R1 + config ConfigV5R1Final +} + +func (c ConfigV5R1Final) String() string { + return "V5R1Final" } -// Source: https://github.com/tonkeeper/tonkeeper-ton/commit/d9aec6adfdb853eb37e0bba7453d83ae52e2a170#diff-c8ee60dec2f4e3ee55ad5e40f56fd9a104f21df78086a114d33d62e4fa0ffee6R139 -/* - * schema: - * wallet_id -- int32 - * wallet_id = global_id ^ context_id - * context_id_client$1 = wc:int8 wallet_version:uint8 counter:uint15 - * context_id_backoffice$0 = counter:uint31 - * - * - * calculated default values serialisation: - * - * global_id = -239, workchain = 0, wallet_version = 0', subwallet_number = 0 (client context) - * gives wallet_id = 2147483409 - * - * global_id = -239, workchain = -1, wallet_version = 0', subwallet_number = 0 (client context) - * gives wallet_id = 8388369 - * - * global_id = -3, workchain = 0, wallet_version = 0', subwallet_number = 0 (client context) - * gives wallet_id = 2147483645 - * - * global_id = -3, workchain = -1, wallet_version = 0', subwallet_number = 0 (client context) - * gives wallet_id = 8388605 - */ // Function to generate the context ID based on the given workchain -func genContextID(workchain int8) uint32 { - var context uint32 +func genContextID(workchain uint8, version uint8, subwallet uint16) uint32 { + var ctx uint32 // Convert workchain to uint32 after ensuring it's correctly handled as an 8-bit value - context |= 1 << 31 // Write 1 bit as 1 at the leftmost bit - context |= (uint32(workchain) & 0xFF) << 23 // Write 8 bits of workchain, shifted to position - context |= uint32(0) << 15 // Write 8 bits of 0 (wallet version) - context |= uint32(0) // Write 15 bits of 0 (subwallet number) + ctx |= 1 << 31 // Write 1 bit as 1 at the leftmost bit + ctx |= uint32(workchain) << 23 // Write 8 bits of workchain, shifted to position + ctx |= uint32(version) << 15 // Write 8 bits of 0 (wallet version) + ctx |= uint32(subwallet) // Write 15 bits of 0 (subwallet number) - return context + return ctx } -type WalletId struct { +type V5R1ID struct { NetworkGlobalID int32 WorkChain int8 SubwalletNumber uint16 WalletVersion uint8 } -func (w WalletId) Serialized() uint32 { - context := genContextID(w.WorkChain) - return uint32(int32(context) ^ w.NetworkGlobalID) +func (w V5R1ID) Serialized() uint32 { + return genContextID(uint8(w.WorkChain), w.WalletVersion, w.SubwalletNumber) ^ uint32(w.NetworkGlobalID) } -func (s *SpecV5R1) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt, messages []*Message) (_ *cell.Cell, err error) { +func (s *SpecV5R1Final) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt, messages []*Message) (_ *cell.Cell, err error) { if len(messages) > 255 { return nil, errors.New("for this type of wallet max 255 messages can be sent at the same time") } @@ -86,12 +66,12 @@ func (s *SpecV5R1) BuildMessage(ctx context.Context, _ bool, _ *ton.BlockIDExt, return nil, fmt.Errorf("failed to fetch seqno: %w", err) } - actions, err := packV5R1Actions(messages) + actions, err := packV5Actions(messages) if err != nil { return nil, fmt.Errorf("failed to build actions: %w", err) } - walletId := WalletId{ + walletId := V5R1ID{ NetworkGlobalID: s.config.NetworkGlobalID, WorkChain: s.config.Workchain, SubwalletNumber: uint16(s.wallet.subwallet), @@ -125,7 +105,7 @@ func validateMessageFields(messages []*Message) error { } // Pack Actions -func packV5R1Actions(messages []*Message) (*cell.Builder, error) { +func packV5Actions(messages []*Message) (*cell.Builder, error) { if err := validateMessageFields(messages); err != nil { return nil, err } @@ -137,6 +117,13 @@ func packV5R1Actions(messages []*Message) (*cell.Builder, error) { return nil, err } + /* + out_list_empty$_ = OutList 0; + out_list$_ {n:#} prev:^(OutList n) action:OutAction + = OutList (n + 1); + action_send_msg#0ec3c86d mode:(## 8) + out_msg:^(MessageRelaxed Any) = OutAction; + */ msg := cell.BeginCell().MustStoreUInt(0x0ec3c86d, 32). // action_send_msg prefix MustStoreUInt(uint64(message.Mode), 8). // mode MustStoreRef(outMsg) // message reference diff --git a/ton/wallet/wallet.go b/ton/wallet/wallet.go index 6e4e2cd3..ba30e287 100644 --- a/ton/wallet/wallet.go +++ b/ton/wallet/wallet.go @@ -41,8 +41,8 @@ const ( V3 = V3R2 V4R1 Version = 41 V4R2 Version = 42 - V5Beta Version = 51 // W5 Beta - V5R1 Version = 52 // W5 Final + V5R1Beta Version = 51 // W5 Beta + V5R1Final Version = 52 // W5 Final HighloadV2R2 Version = 122 HighloadV2Verified Version = 123 HighloadV3 Version = 300 @@ -85,8 +85,8 @@ var ( V2R1: _V2R1CodeHex, V2R2: _V2R2CodeHex, V3R1: _V3R1CodeHex, V3R2: _V3R2CodeHex, V4R1: _V4R1CodeHex, V4R2: _V4R2CodeHex, - V5Beta: _V5BetaCodeHex, - V5R1: _V5R1CodeHex, + V5R1Beta: _V5R1BetaCodeHex, + V5R1Final: _V5R1FinalCodeHex, HighloadV2R2: _HighloadV2R2CodeHex, HighloadV2Verified: _HighloadV2VerifiedCodeHex, HighloadV3: _HighloadV3CodeHex, Lockup: _LockupCodeHex, @@ -162,8 +162,8 @@ func FromPrivateKey(api TonAPI, key ed25519.PrivateKey, version VersionConfig) ( // default subwallet depends on wallet type switch version.(type) { - case ConfigV5Beta: - case ConfigV5R1: + case ConfigV5R1Beta: + case ConfigV5R1Final: subwallet = 0 } @@ -190,7 +190,7 @@ func FromPrivateKey(api TonAPI, key ed25519.PrivateKey, version VersionConfig) ( func getSpec(w *Wallet) (any, error) { switch v := w.ver.(type) { - case Version, ConfigV5Beta, ConfigV5R1: + case Version, ConfigV5R1Beta, ConfigV5R1Final: regular := SpecRegular{ wallet: w, messagesTTL: 60 * 3, // default ttl 3 min @@ -218,16 +218,16 @@ func getSpec(w *Wallet) (any, error) { } switch x := w.ver.(type) { - case ConfigV5Beta: + case ConfigV5R1Beta: if x.NetworkGlobalID == 0 { return nil, fmt.Errorf("NetworkGlobalID should be set in V5 config") } - return &SpecV5Beta{SpecRegular: regular, SpecSeqno: SpecSeqno{seqnoFetcher: seqnoFetcher}, config: x}, nil - case ConfigV5R1: + return &SpecV5R1Beta{SpecRegular: regular, SpecSeqno: SpecSeqno{seqnoFetcher: seqnoFetcher}, config: x}, nil + case ConfigV5R1Final: if x.NetworkGlobalID == 0 { return nil, fmt.Errorf("NetworkGlobalID should be set in V5 config") } - return &SpecV5R1{SpecRegular: regular, SpecSeqno: SpecSeqno{seqnoFetcher: seqnoFetcher}, config: x}, nil + return &SpecV5R1Final{SpecRegular: regular, SpecSeqno: SpecSeqno{seqnoFetcher: seqnoFetcher}, config: x}, nil } switch v { @@ -239,10 +239,10 @@ func getSpec(w *Wallet) (any, error) { return &SpecHighloadV2R2{regular, SpecQuery{}}, nil case HighloadV3: return nil, fmt.Errorf("use ConfigHighloadV3 for highload v3 spec") - case V5Beta: - return nil, fmt.Errorf("use ConfigV5Beta for V5 spec") - case V5R1: - return nil, fmt.Errorf("use ConfigV5R1 for V5 spec") + case V5R1Beta: + return nil, fmt.Errorf("use ConfigV5R1Beta for V5 Beta spec") + case V5R1Final: + return nil, fmt.Errorf("use ConfigV5R1Final for V5 spec") } case ConfigHighloadV3: return &SpecHighloadV3{wallet: w, config: v}, nil @@ -343,16 +343,16 @@ func (w *Wallet) PrepareExternalMessageForMany(ctx context.Context, withStateIni var msg *cell.Cell switch v := w.ver.(type) { - case Version, ConfigV5Beta, ConfigV5R1: - if _, ok := v.(ConfigV5Beta); ok { - v = V5Beta - } - if _, ok := v.(ConfigV5R1); ok { - v = V5R1 + case Version, ConfigV5R1Beta, ConfigV5R1Final: + switch v.(type) { + case ConfigV5R1Beta: + v = V5R1Beta + case ConfigV5R1Final: + v = V5R1Final } switch v { - case V3R2, V3R1, V4R2, V4R1, V5Beta, V5R1: + case V3R2, V3R1, V4R2, V4R1, V5R1Beta, V5R1Final: msg, err = w.spec.(RegularBuilder).BuildMessage(ctx, !withStateInit, nil, messages) if err != nil { return nil, fmt.Errorf("build message err: %w", err) From 6bf06e40bacd79bc30e6c7132f332fc2d7e6a986 Mon Sep 17 00:00:00 2001 From: Oleg Baranov Date: Thu, 18 Jul 2024 20:37:38 +0400 Subject: [PATCH 24/26] Added nil check for out messages in find last transaction --- ton/transactions.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ton/transactions.go b/ton/transactions.go index 6deecd8f..acc79d4a 100644 --- a/ton/transactions.go +++ b/ton/transactions.go @@ -324,6 +324,10 @@ func (c *APIClient) findLastTransactionByHash(ctx context.Context, addr *address } if isOut { + if transaction.IO.Out == nil { + continue + } + list, err := transaction.IO.Out.ToSlice() if err != nil { return nil, fmt.Errorf("cannot list out messages: %w", err) From 5bacf22c3461fc1b266285c4116ea3f5cd50d1ff Mon Sep 17 00:00:00 2001 From: Oleg Baranov Date: Sat, 20 Jul 2024 21:41:27 +0530 Subject: [PATCH 25/26] Fixed findLastTransactionByHash out messages list scan --- ton/transactions.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ton/transactions.go b/ton/transactions.go index acc79d4a..a3f2888c 100644 --- a/ton/transactions.go +++ b/ton/transactions.go @@ -338,6 +338,8 @@ func (c *APIClient) findLastTransactionByHash(ctx context.Context, addr *address return transaction, nil } } + + continue } else { if transaction.IO.In == nil { continue From 2ec0b5d2d5230df4fc3efda36f1b9dfebaa65b66 Mon Sep 17 00:00:00 2001 From: Oleg Baranov Date: Thu, 25 Jul 2024 19:00:26 +0400 Subject: [PATCH 26/26] Fixed ShardDescription field type in ShardInfo --- ton/block.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ton/block.go b/ton/block.go index 09c139d7..5fe1e155 100644 --- a/ton/block.go +++ b/ton/block.go @@ -182,7 +182,7 @@ type ShardInfo struct { ID *BlockIDExt `tl:"struct"` ShardBlock *BlockIDExt `tl:"struct"` ShardProof []*cell.Cell `tl:"cell optional 2"` - ShardDescription *cell.Cell `tl:"bytes"` + ShardDescription *cell.Cell `tl:"cell optional"` } type BlockTransactions struct {