diff --git a/abi/messages_test.go b/abi/messages_test.go index a10b4591..b08fd50f 100644 --- a/abi/messages_test.go +++ b/abi/messages_test.go @@ -62,3 +62,24 @@ func TestDecodeAndEncodeUnknownInMsgBody(t *testing.T) { t.Fatalf("got different result") } } + +func TestDecodeAndEncodeHashMap(t *testing.T) { + data := "b5ee9c7241010b0100f4000202c8010a0201620209020120030802012004070201580506004327fd889d4ca5a81250b38cfb489c99475bacacb61c512fac81458a37f66e1b10eff400432002aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac0044d4ff53355555555555555555555555555555555555555555555555555555555555550045bd002c44ea652d4092859c67da44e4ca3add6565b0e2897d640a2c51bfb370d8877fa00045a3cff555555555555555555555555555555555555555555555555555555555555555580045bd5800aa6aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab8a1b898e" + boc1, _ := boc.DeserializeBocHex(data) + + var x tlb.Hashmap[tlb.Uint16, tlb.MsgAddress] + if err := tlb.Unmarshal(boc1[0], &x); err != nil { + t.Fatalf("Unable to unmarshal: %v", err) + } + + boc2 := boc.NewCell() + if err := tlb.Marshal(boc2, x); err != nil { + t.Fatalf("Unable to marshal: %v", err) + } + + hs1, _ := boc1[0].HashString() + hs2, _ := boc2.HashString() + if hs1 != hs2 { + t.Fatalf("got different result") + } +} diff --git a/boc/bitString.go b/boc/bitString.go index fb914333..56064013 100644 --- a/boc/bitString.go +++ b/boc/bitString.go @@ -213,10 +213,11 @@ func (s *BitString) ReadBigUint(bitLen int) (*big.Int, error) { } b = []byte{byte(firstByte)} } - b, err = s.ReadBytes(bitLen / 8) + normalizeB, err := s.ReadBytes(bitLen / 8) if err != nil { return nil, err } + b = append(b, normalizeB...) num.SetBytes(b) return num, nil } diff --git a/boc/cell.go b/boc/cell.go index 0b8cc8f8..d69ebbbe 100644 --- a/boc/cell.go +++ b/boc/cell.go @@ -106,6 +106,7 @@ func (c *Cell) BitSize() int { func (c *Cell) Hash() ([]byte, error) { return c.hash(map[*Cell]*immutableCell{}) } + func (c *Cell) Hash256() ([32]byte, error) { b, err := c.hash(map[*Cell]*immutableCell{}) if err != nil { @@ -340,6 +341,21 @@ func (c *Cell) CopyRemaining() *Cell { return c2 } +func (c *Cell) CopyCell() *Cell { + if c == nil { + return nil + } + c2 := NewCellWithBits(c.RawBitString()) + for _, ref := range c.Refs() { + refCopy := ref.CopyCell() + if err := c2.AddRef(refCopy); err != nil { + // this should never happen but anyway + panic(err) + } + } + return c2 +} + func (c *Cell) WriteBytes(b []byte) error { return c.bits.WriteBytes(b) } diff --git a/examples/tolk/payload-unmarshal/abi/jetton_wallet.json b/examples/tolk/payload-unmarshal/abi/jetton_wallet.json new file mode 100644 index 00000000..56311fca --- /dev/null +++ b/examples/tolk/payload-unmarshal/abi/jetton_wallet.json @@ -0,0 +1,75 @@ +{ + "contractName": "Jetton", + "declarations": [ + { + "kind": "Struct", + "name": "Transfer", + "prefix": { + "prefixStr": "0x0f8a7ea5", + "prefixLen": 32 + }, + "fields": [ + { + "name": "queryId", + "ty": { + "kind": "uintN", + "n": 64 + } + }, + { + "name": "amount", + "ty": { + "kind": "coins" + } + }, + { + "name": "destination", + "ty": { + "kind": "address" + } + }, + { + "name": "responseDestination", + "ty": { + "kind": "address" + } + }, + { + "name": "customPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "forwardAmount", + "ty": { + "kind": "coins" + } + }, + { + "name": "forwardPayload", + "isPayload": true, + "ty": { + "kind": "remaining" + } + } + ] + } + ], + "incomingMessages": [], + "outgoingMessages": [], + "emittedEvents": [], + "storage": { + "storageTy": { + "kind": "nullLiteral" + } + }, + "getMethods": [], + "thrownErrors": [], + "compilerName": "tolk", + "compilerVersion": "1.2.0", + "codeBoc64": "te6ccgEBAgEAFQABFP8A9KQT9LzyyAsBAAzTMPiR8kA=" +} \ No newline at end of file diff --git a/examples/tolk/payload-unmarshal/abi/swap_coffee.json b/examples/tolk/payload-unmarshal/abi/swap_coffee.json new file mode 100644 index 00000000..337cc270 --- /dev/null +++ b/examples/tolk/payload-unmarshal/abi/swap_coffee.json @@ -0,0 +1,156 @@ +{ + "contractName": "Test", + "declarations": [ + { + "kind": "Struct", + "name": "CoffeeCrossDexResend", + "prefix": { + "prefixStr": "0x4ee9b106", + "prefixLen": 32 + }, + "fields": [ + { + "name": "next", + "isPayload": true, + "ty": { + "kind": "cell" + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeSwap", + "prefix": { + "prefixStr": "0xc0ffee10", + "prefixLen": 32 + }, + "fields": [ + { + "name": "step", + "ty": { + "kind": "StructRef", + "structName": "CoffeeSwapStepParams" + } + }, + { + "name": "params", + "ty": { + "kind": "cellOf", + "inner": { + "kind": "StructRef", + "structName": "CoffeeSwapParams" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeSwapStepParams", + "fields": [ + { + "name": "poolAddressHash", + "ty": { + "kind": "uintN", + "n": 256 + } + }, + { + "name": "minOutputAmount", + "ty": { + "kind": "coins" + } + }, + { + "name": "next", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cellOf", + "inner": { + "kind": "StructRef", + "structName": "CoffeeSwapStepParams" + } + } + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeSwapParams", + "fields": [ + { + "name": "deadline", + "ty": { + "kind": "uintN", + "n": 32 + } + }, + { + "name": "recipient", + "ty": { + "kind": "address" + } + }, + { + "name": "referral", + "ty": { + "kind": "addressOpt" + } + }, + { + "name": "notificationData", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cellOf", + "inner": { + "kind": "StructRef", + "structName": "CoffeeNotificationData" + } + } + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeNotificationData", + "fields": [ + { + "name": "reciever", + "ty": { + "kind": "address" + } + }, + { + "name": "fwdGas", + "ty": { + "kind": "coins" + } + }, + { + "name": "payload", + "ty": { + "kind": "cell" + } + } + ] + } + ], + "incomingMessages": [], + "outgoingMessages": [], + "emittedEvents": [], + "storage": { + "storageTy": { + "kind": "nullLiteral" + } + }, + "getMethods": [], + "thrownErrors": [], + "compilerName": "tolk", + "compilerVersion": "1.2.0", + "codeBoc64": "te6ccgEBAgEAFQABFP8A9KQT9LzyyAsBAAzTMPiR8kA=" +} \ No newline at end of file diff --git a/examples/tolk/payload-unmarshal/main.go b/examples/tolk/payload-unmarshal/main.go new file mode 100644 index 00000000..73e221d7 --- /dev/null +++ b/examples/tolk/payload-unmarshal/main.go @@ -0,0 +1,127 @@ +package main + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "os" + + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tolk" + "github.com/tonkeeper/tongo/tolk/parser" +) +import _ "embed" + +//go:embed abi/jetton_wallet.json +var jettonWalletAbiData []byte + +//go:embed abi/swap_coffee.json +var swapCoffeeAbiData []byte + +func main() { + var jettonWalletABI parser.ABI + err := json.Unmarshal(jettonWalletAbiData, &jettonWalletABI) + if err != nil { + panic(err) + } + + var swapCoffeeABI parser.ABI + err = json.Unmarshal(swapCoffeeAbiData, &swapCoffeeABI) + if err != nil { + panic(err) + } + + ty := parser.NewStructType("Transfer") + b, err := hex.DecodeString("b5ee9c72010205010001310001ae0f8a7ea5003c0fe80d6334813ef1895801d9d00dc43cb19c9bfa417f874e7be3670825205a6e50d3490a0aec1194fe2e8f003ffffbc78bb7c4bb37210cfa7f64397ff32df53ce36c46934e21a1e315c93137482ed968810101084ee9b1060201ae0f8a7ea5003c0fe80d6334813ef189d801fe7933ccc21619bc6f1800071d12d4e7aae8d68f637fcdb8facdca753b08c859003ffffbc78bb7c4bb37210cfa7f64397ff32df53ce36c46934e21a1e315c93137481823cf41030153c0ffee10c44cf0b64eb9dedfb010c7b24a508140d86f6e07789f9383bb9b2141ea9c08ea502d0bbc060404008d69a178e5801ffffde3c5dbe25d9b90867d3fb21cbff996fa9e71b62349a710d0f18ae4989bb00021706f0b21587534d776380d79ce6fcbef538e7aacc3f949f91149c80db932bd") + if err != nil { + panic(err) + } + cell, err := boc.DeserializeBoc(b) + if err != nil { + panic(err) + } + + decoder := tolk.NewDecoder() + err = decoder.WithABIs(jettonWalletABI, swapCoffeeABI) + if err != nil { + panic(err) + } + res, err := decoder.Unmarshal(cell[0], ty) + if err != nil { + panic(err) + } + tolkStruct, ok := res.GetStruct() + if !ok { + panic("Struct Transfer not found") + } + prefix, exists := tolkStruct.GetPrefix() + if !exists { + panic("Transfer prefix not found") + } + + queryId := tolkStruct.MustGetField("queryId") + queryIdValue := queryId.MustGetSmallUInt() + + newOwner, ok := tolkStruct.GetField("destination") + if !ok { + panic("transfer.destination not found") + } + newOwnerValue, ok := newOwner.GetAddress() + if !ok { + panic("cannot get transfer.destination value") + } + + responseDestination, ok := tolkStruct.GetField("responseDestination") + if !ok { + panic("transfer.responseDestination not found") + } + responseDestinationValue, ok := responseDestination.GetAddress() + if !ok { + panic("cannot get transfer.responseDestination value") + } + + customPayload, ok := tolkStruct.GetField("customPayload") + if !ok { + panic("transfer.customPayload not found") + } + customPayloadValue, ok := customPayload.GetOptionalValue() + if !ok { + panic("cannot get transfer.customPayload value") + } + + forwardAmount, ok := tolkStruct.GetField("forwardAmount") + if !ok { + panic("transfer.forwardAmount not found") + } + forwardAmountValue, ok := forwardAmount.GetCoins() + if !ok { + panic("cannot get transfer.forwardAmount value") + } + + _, ok = tolkStruct.GetField("forwardPayload") + if !ok { + panic("transfer.forwardPayload not found") + } + + val, err := json.MarshalIndent(res, "", " ") + if err != nil { + panic(err) + } + inputFilename := "examples/tolk/payload-unmarshal/output.json" + err = os.WriteFile(inputFilename, val, os.ModePerm) + if err != nil { + panic(err) + } + + tolkValue := tolk.Value{} + if err := json.Unmarshal(val, &tolkValue); err != nil { + panic(err) + } + + fmt.Printf("Transfer prefix: 0x%x\n", prefix.Prefix) + fmt.Printf("Transfer query id: %v\n", queryIdValue) + fmt.Printf("Transfer new owner: %v\n", newOwnerValue.ToRaw()) + fmt.Printf("Transfer response destination: %v\n", responseDestinationValue.ToRaw()) + fmt.Printf("Transfer is custom payload exists: %v\n", customPayloadValue.IsExists) + fmt.Printf("Transfer forward amount: %x\n", forwardAmountValue.String()) +} diff --git a/examples/tolk/payload-unmarshal/output.json b/examples/tolk/payload-unmarshal/output.json new file mode 100755 index 00000000..9eeab089 --- /dev/null +++ b/examples/tolk/payload-unmarshal/output.json @@ -0,0 +1,148 @@ +{ + "sumType": "struct", + "struct": { + "prefix": { + "len": 32, + "prefix": 260734629 + }, + "fields": { + "queryId": { + "sumType": "smallUint", + "smallUint": 16905987934073985 + }, + "amount": { + "sumType": "coins", + "coins": "-1107819" + }, + "destination": { + "sumType": "internalAddress", + "internalAddress": "0:ece806e21e58ce4dfd20bfc3a73df1b38412902d372869a485057608ca7f1747" + }, + "responseDestination": { + "sumType": "internalAddress", + "internalAddress": "0:ffffef1e2edf12ecdc8433e9fd90e5ffccb7d4f38db11a4d3886878c5724c4dd" + }, + "customPayload": { + "sumType": "optionalValue", + "optionalValue": { + "isExists": false + } + }, + "forwardAmount": { + "sumType": "coins", + "coins": "393000000" + }, + "forwardPayload": { + "sumType": "struct", + "struct": { + "prefix": { + "len": 32, + "prefix": 1323938054 + }, + "fields": { + "next": { + "sumType": "struct", + "struct": { + "prefix": { + "len": 32, + "prefix": 260734629 + }, + "fields": { + "queryId": { + "sumType": "smallUint", + "smallUint": 16905987934073985 + }, + "amount": { + "sumType": "coins", + "coins": "-1107811" + }, + "destination": { + "sumType": "internalAddress", + "internalAddress": "0:ff3c99e6610b0cde378c00038e896a73d5746b47b1bfe6dc7d66e53a9d84642c" + }, + "responseDestination": { + "sumType": "internalAddress", + "internalAddress": "0:ffffef1e2edf12ecdc8433e9fd90e5ffccb7d4f38db11a4d3886878c5724c4dd" + }, + "customPayload": { + "sumType": "optionalValue", + "optionalValue": { + "isExists": false + } + }, + "forwardAmount": { + "sumType": "coins", + "coins": "202500000" + }, + "forwardPayload": { + "sumType": "struct", + "struct": { + "prefix": { + "len": 32, + "prefix": 3237998096 + }, + "fields": { + "step": { + "sumType": "struct", + "struct": { + "fields": { + "poolAddressHash": { + "sumType": "bigUint", + "bigUint": "88789260033367713967225486461025260896651147011656566896934426357962362325226" + }, + "minOutputAmount": { + "sumType": "coins", + "coins": "12091900000" + }, + "next": { + "sumType": "optionalValue", + "optionalValue": { + "isExists": false + } + } + } + } + }, + "params": { + "sumType": "refValue", + "refValue": { + "sumType": "struct", + "struct": { + "fields": { + "deadline": { + "sumType": "smallUint", + "smallUint": 1772189925 + }, + "recipient": { + "sumType": "internalAddress", + "internalAddress": "0:ffffef1e2edf12ecdc8433e9fd90e5ffccb7d4f38db11a4d3886878c5724c4dd" + }, + "referral": { + "sumType": "optionalAddress", + "optionalAddress": { + "sumType": "internalAddress", + "internalAddress": "0:085c1bc2c8561d4d35dd8e035e739bf2fbd4e39eab30fe527e445272036e4caf" + } + }, + "notificationData": { + "sumType": "optionalValue", + "optionalValue": { + "isExists": false + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/examples/tolk/simple-unmarshal/abi/jetton_wallet.json b/examples/tolk/simple-unmarshal/abi/jetton_wallet.json new file mode 100644 index 00000000..56311fca --- /dev/null +++ b/examples/tolk/simple-unmarshal/abi/jetton_wallet.json @@ -0,0 +1,75 @@ +{ + "contractName": "Jetton", + "declarations": [ + { + "kind": "Struct", + "name": "Transfer", + "prefix": { + "prefixStr": "0x0f8a7ea5", + "prefixLen": 32 + }, + "fields": [ + { + "name": "queryId", + "ty": { + "kind": "uintN", + "n": 64 + } + }, + { + "name": "amount", + "ty": { + "kind": "coins" + } + }, + { + "name": "destination", + "ty": { + "kind": "address" + } + }, + { + "name": "responseDestination", + "ty": { + "kind": "address" + } + }, + { + "name": "customPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "forwardAmount", + "ty": { + "kind": "coins" + } + }, + { + "name": "forwardPayload", + "isPayload": true, + "ty": { + "kind": "remaining" + } + } + ] + } + ], + "incomingMessages": [], + "outgoingMessages": [], + "emittedEvents": [], + "storage": { + "storageTy": { + "kind": "nullLiteral" + } + }, + "getMethods": [], + "thrownErrors": [], + "compilerName": "tolk", + "compilerVersion": "1.2.0", + "codeBoc64": "te6ccgEBAgEAFQABFP8A9KQT9LzyyAsBAAzTMPiR8kA=" +} \ No newline at end of file diff --git a/examples/tolk/simple-unmarshal/main.go b/examples/tolk/simple-unmarshal/main.go new file mode 100644 index 00000000..5149808f --- /dev/null +++ b/examples/tolk/simple-unmarshal/main.go @@ -0,0 +1,118 @@ +package main + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "os" + + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tolk" + "github.com/tonkeeper/tongo/tolk/parser" +) +import _ "embed" + +//go:embed abi/jetton_wallet.json +var jettonWalletAbiData []byte + +func main() { + var jettonWalletABI parser.ABI + err := json.Unmarshal(jettonWalletAbiData, &jettonWalletABI) + if err != nil { + panic(err) + } + + ty := parser.NewStructType("Transfer") + b, err := hex.DecodeString("b5ee9c720102030100011b0001ae0f8a7ea5bf4e20320df05dd0318537180125c28235ca8d125e676591513d520721b1fe99f7722f4c87723ce7ee0dfb73a3001f8cc4cadf3be6b14e892d7aa31f5be355c1eab776f0e1d61a46644ddc17e68b881908b1010101e16664de2a801244183034d9fd59a236f71ec4271be377399056dda4cc3a5ebf5dc40967df641001f8cc4cadf3be6b14e892d7aa31f5be355c1eab776f0e1d61a46644ddc17e68ba003f198995be77cd629d125af5463eb7c6ab83d56eede1c3ac348cc89bb82fcd17000000007fffffffc0020095446cf7101800fc662656f9df358a74496bd518fadf1aae0f55bbb7870eb0d233226ee0bf345c00000540095e99c8dc6a438526df4961936ff51209f307a28c37c6c78310ce140ab78ab658") + if err != nil { + panic(err) + } + cell, err := boc.DeserializeBoc(b) + if err != nil { + panic(err) + } + + decoder := tolk.NewDecoder() + err = decoder.WithABIs(jettonWalletABI) + if err != nil { + panic(err) + } + res, err := decoder.Unmarshal(cell[0], ty) + if err != nil { + panic(err) + } + tolkStruct, ok := res.GetStruct() + if !ok { + panic("Struct Transfer not found") + } + prefix, exists := tolkStruct.GetPrefix() + if !exists { + panic("Transfer prefix not found") + } + + queryId := tolkStruct.MustGetField("queryId") + queryIdValue := queryId.MustGetSmallUInt() + + newOwner, ok := tolkStruct.GetField("destination") + if !ok { + panic("transfer.destination not found") + } + newOwnerValue, ok := newOwner.GetAddress() + if !ok { + panic("cannot get transfer.destination value") + } + + responseDestination, ok := tolkStruct.GetField("responseDestination") + if !ok { + panic("transfer.responseDestination not found") + } + responseDestinationValue, ok := responseDestination.GetAddress() + if !ok { + panic("cannot get transfer.responseDestination value") + } + + customPayload, ok := tolkStruct.GetField("customPayload") + if !ok { + panic("transfer.customPayload not found") + } + customPayloadValue, ok := customPayload.GetOptionalValue() + if !ok { + panic("cannot get transfer.customPayload value") + } + + forwardAmount, ok := tolkStruct.GetField("forwardAmount") + if !ok { + panic("transfer.forwardAmount not found") + } + forwardAmountValue, ok := forwardAmount.GetCoins() + if !ok { + panic("cannot get transfer.forwardAmount value") + } + + _, ok = tolkStruct.GetField("forwardPayload") + if !ok { + panic("transfer.forwardPayload not found") + } + + val, err := json.MarshalIndent(res, "", " ") + if err != nil { + panic(err) + } + inputFilename := "examples/tolk/simple-unmarshal/output.json" + err = os.WriteFile(inputFilename, val, os.ModePerm) + if err != nil { + panic(err) + } + + tolkValue := tolk.Value{} + if err := json.Unmarshal(val, &tolkValue); err != nil { + panic(err) + } + + fmt.Printf("Transfer prefix: 0x%x\n", prefix.Prefix) + fmt.Printf("Transfer query id: %v\n", queryIdValue) + fmt.Printf("Transfer new owner: %v\n", newOwnerValue.ToRaw()) + fmt.Printf("Transfer response destination: %v\n", responseDestinationValue.ToRaw()) + fmt.Printf("Transfer is custom payload exists: %v\n", customPayloadValue.IsExists) + fmt.Printf("Transfer forward amount: %x\n", forwardAmountValue.String()) +} diff --git a/examples/tolk/simple-unmarshal/output.json b/examples/tolk/simple-unmarshal/output.json new file mode 100755 index 00000000..b0a10c01 --- /dev/null +++ b/examples/tolk/simple-unmarshal/output.json @@ -0,0 +1,41 @@ +{ + "sumType": "struct", + "struct": { + "prefix": { + "len": 32, + "prefix": 260734629 + }, + "fields": { + "queryId": { + "sumType": "smallUint", + "smallUint": 13784990908781977040 + }, + "amount": { + "sumType": "coins", + "coins": "1594225" + }, + "destination": { + "sumType": "internalAddress", + "internalAddress": "0:92e1411ae546892f33b2c8a89ea90390d8ff4cfbb917a643b91e73f706fdb9d1" + }, + "responseDestination": { + "sumType": "internalAddress", + "internalAddress": "0:7e33132b7cef9ac53a24b5ea8c7d6f8d5707aadddbc3875869199137705f9a2e" + }, + "customPayload": { + "sumType": "optionalValue", + "optionalValue": { + "isExists": false + } + }, + "forwardAmount": { + "sumType": "coins", + "coins": "210000000" + }, + "forwardPayload": { + "sumType": "remaining", + "remaining": "b5ee9c720101030100c40001000101e16664de2a801244183034d9fd59a236f71ec4271be377399056dda4cc3a5ebf5dc40967df641001f8cc4cadf3be6b14e892d7aa31f5be355c1eab776f0e1d61a46644ddc17e68ba003f198995be77cd629d125af5463eb7c6ab83d56eede1c3ac348cc89bb82fcd17000000007fffffffc0020095446cf7101800fc662656f9df358a74496bd518fadf1aae0f55bbb7870eb0d233226ee0bf345c00000540095e99c8dc6a438526df4961936ff51209f307a28c37c6c78310ce140ab78ab658" + } + } + } +} \ No newline at end of file diff --git a/tlb/address.go b/tlb/address.go index 76943d3d..7f6bcab0 100644 --- a/tlb/address.go +++ b/tlb/address.go @@ -2,7 +2,11 @@ package tlb import ( "bytes" + "encoding/hex" "fmt" + "math" + "strconv" + "strings" "github.com/tonkeeper/tongo/boc" ) @@ -62,3 +66,95 @@ func (addr AddressWithWorkchain) MarshalJSON() ([]byte, error) { raw := fmt.Sprintf("%v:%x", addr.Workchain, addr.Address) return []byte(`"` + raw + `"`), nil } + +// InternalAddress is a TL-B type that represents only internal message address: +// addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 address:bits256 = MsgAddressInt; +// Anycast is deprecated since TVM 10, so internal message always has `100` prefix +type InternalAddress struct { + Workchain int8 + Address Bits256 +} + +func (addr InternalAddress) Equal(other any) bool { + otherAddr, ok := other.(InternalAddress) + if !ok { + return false + } + return addr.Workchain == otherAddr.Workchain && addr.Address == otherAddr.Address +} + +// Compare returns an integer comparing two addresses lexicographically. +// The result will be 0 if both are equal, -1 if addr < other, and +1 if addr > other. +func (addr InternalAddress) Compare(other any) (int, bool) { + otherAddr, ok := other.(InternalAddress) + if !ok { + return 0, false + } + workchain := uint32(addr.Workchain) + otherWorkchain := uint32(otherAddr.Workchain) + if workchain < otherWorkchain { + return -1, true + } + if workchain > otherWorkchain { + return 1, true + } + return bytes.Compare(addr.Address[:], otherAddr.Address[:]), true +} + +func (addr InternalAddress) FixedSize() int { + return 267 +} + +func (addr *InternalAddress) UnmarshalTLB(c *boc.Cell, decoder *Decoder) error { + err := c.Skip(3) // $100 prefix + if err != nil { + return err + } + workchain, err := c.ReadInt(8) + if err != nil { + return err + } + address, err := c.ReadBytes(32) + if err != nil { + return err + } + addr.Workchain = int8(workchain) + copy(addr.Address[:], address) + return nil +} + +func (addr InternalAddress) MarshalTLB(c *boc.Cell, encoder *Encoder) error { + if err := c.WriteUint(2, 3); err != nil { + return err + } + if err := c.WriteInt(int64(addr.Workchain), 8); err != nil { + return err + } + return c.WriteBytes(addr.Address[:]) +} + +func (addr InternalAddress) MarshalJSON() ([]byte, error) { + var anycastExtra string + x := fmt.Sprintf("%d:%s", addr.Workchain, addr.Address.Hex()) + return []byte(fmt.Sprintf(`"%s%s"`, x, anycastExtra)), nil +} + +func (addr *InternalAddress) UnmarshalJSON(b []byte) error { + parts := strings.Split(string(b), ":") + if len(parts) != 2 { + return fmt.Errorf("invalid address: %s", string(b)) + } + num, err := strconv.ParseInt(parts[0], 10, 32) + isWorkchainInt8 := err == nil && num >= int64(math.MinInt8) && num <= int64(math.MaxInt8) + if !isWorkchainInt8 { + return fmt.Errorf("invalid address: %s", string(b)) + } + var dst [32]byte + _, err = hex.Decode(dst[:], []byte(parts[1])) + if err != nil { + return err + } + addr.Workchain = int8(num) + addr.Address = dst + return nil +} diff --git a/tlb/hashmap.go b/tlb/hashmap.go index 49f5d549..e26b605e 100644 --- a/tlb/hashmap.go +++ b/tlb/hashmap.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "math" "slices" "strconv" "strings" @@ -409,45 +410,107 @@ func encodeLabel(c *boc.Cell, keyFirst, keyLast *boc.BitString, keySize int) (bo } keyFirst.ResetCounter() keyLast.ResetCounter() - if label.BitsAvailableForRead() < 8 { - //hml_short$0 {m:#} {n:#} len:(Unary ~n) {n <= m} s:(n * Bit) = HmLabel ~n m; - err := c.WriteBit(false) - if err != nil { - return boc.BitString{}, err - } - // todo pack label - err = c.WriteUnary(uint(label.BitsAvailableForRead())) - if err != nil { - return boc.BitString{}, err - } - err = c.WriteBitString(label) - if err != nil { - return boc.BitString{}, err - } + labelLen := label.BitsAvailableForRead() + + // We must find the most compact way to serialize key + hmlShortSize := 2*labelLen + 2 + hmlLongSize := 2 + int(math.Ceil(math.Log2(float64(keySize)+1))) + labelLen + var encodeFunc func(*boc.Cell, int, boc.BitString) error + isShort := false + if hmlShortSize <= hmlLongSize { + isShort = true + encodeFunc = encodeShortLabel } else { - // hml_long$10 {m:#} n:(#<= m) s:(n * Bit) = HmLabel ~n m; - err := c.WriteBit(true) - if err != nil { - return boc.BitString{}, err - } - err = c.WriteBit(false) + encodeFunc = encodeLongLabel + } + + // If all bits in label are the same then we can use hml_same + isAllZero := true + isAllOne := true + for label.BitsAvailableForRead() > 0 { + bit, err := label.ReadBit() if err != nil { return boc.BitString{}, err } - // todo pack label - err = c.WriteLimUint(label.BitsAvailableForRead(), keySize) - if err != nil { - return boc.BitString{}, err + if bit { + isAllZero = false + } else { + isAllOne = false } - err = c.WriteBitString(label) - if err != nil { - return boc.BitString{}, err + } + label.ResetCounter() + if isAllZero || isAllOne { + hmlSameSize := 2 + 1 + int(math.Ceil(math.Log2(float64(keySize)+1))) + if isShort && hmlSameSize < hmlShortSize { + encodeFunc = encodeSameLabel + } else if !isShort && hmlSameSize < hmlLongSize { + encodeFunc = encodeSameLabel } } + + err := encodeFunc(c, keySize, label) + if err != nil { + return boc.BitString{}, err + } return label, nil } +func encodeShortLabel(c *boc.Cell, keySize int, label boc.BitString) error { + //hml_short$0 {m:#} {n:#} len:(Unary ~n) {n <= m} s:(n * Bit) = HmLabel ~n m; + err := c.WriteBit(false) + if err != nil { + return err + } + err = c.WriteUnary(uint(label.BitsAvailableForRead())) + if err != nil { + return err + } + err = c.WriteBitString(label) + if err != nil { + return err + } + return nil +} + +func encodeLongLabel(c *boc.Cell, keySize int, label boc.BitString) error { + // hml_long$10 {m:#} n:(#<= m) s:(n * Bit) = HmLabel ~n m; + err := c.WriteBit(true) + if err != nil { + return err + } + err = c.WriteBit(false) + if err != nil { + return err + } + err = c.WriteLimUint(label.BitsAvailableForRead(), keySize) + if err != nil { + return err + } + err = c.WriteBitString(label) + if err != nil { + return err + } + return nil +} + +func encodeSameLabel(c *boc.Cell, keySize int, label boc.BitString) error { + //hml_same$11 {m:#} v:Bit n:(#<= m) = HmLabel ~n m; + err := c.WriteUint(0b11, 2) + if err != nil { + return err + } + err = c.WriteBit(label.BinaryString()[0] == '1') + if err != nil { + return err + } + err = c.WriteLimUint(label.BitsAvailableForRead(), keySize) + if err != nil { + return err + } + return nil +} + type HashmapAug[keyT fixedSize, T1, T2 any] struct { keys []keyT values []T1 diff --git a/tlb/integers.go b/tlb/integers.go index d09ddaea..a3a172c7 100644 --- a/tlb/integers.go +++ b/tlb/integers.go @@ -1,5 +1,6 @@ package tlb -// Code autogenerated. DO NOT EDIT. + +// Code autogenerated. DO NOT EDIT. import ( "bytes" @@ -7712,6 +7713,26 @@ func (u Int128) FixedSize() int { return 128 } +func (u Int128) Equal(other any) bool { + otherInt, ok := other.(Int128) + if !ok { + return false + } + bigU := big.Int(u) + otherBigInt := big.Int(otherInt) + return bigU.Cmp(&otherBigInt) == 0 +} + +func (u Int128) Compare(other any) (int, bool) { + otherInt, ok := other.(Int128) + if !ok { + return 0, false + } + bigU := big.Int(u) + otherBigInt := big.Int(otherInt) + return bigU.Cmp(&otherBigInt), true +} + func (u Int128) MarshalJSON() ([]byte, error) { i := big.Int(u) return []byte(fmt.Sprintf("\"%s\"", i.String())), nil @@ -7747,6 +7768,26 @@ func (u Int256) FixedSize() int { return 256 } +func (u Int256) Equal(other any) bool { + otherInt, ok := other.(Int256) + if !ok { + return false + } + bigU := big.Int(u) + otherBigInt := big.Int(otherInt) + return bigU.Cmp(&otherBigInt) == 0 +} + +func (u Int256) Compare(other any) (int, bool) { + otherInt, ok := other.(Int256) + if !ok { + return 0, false + } + bigU := big.Int(u) + otherBigInt := big.Int(otherInt) + return bigU.Cmp(&otherBigInt), true +} + func (u Int256) MarshalJSON() ([]byte, error) { i := big.Int(u) return []byte(fmt.Sprintf("\"%s\"", i.String())), nil @@ -7782,6 +7823,26 @@ func (u Int257) FixedSize() int { return 257 } +func (u Int257) Equal(other any) bool { + otherInt, ok := other.(Int257) + if !ok { + return false + } + bigU := big.Int(u) + otherBigInt := big.Int(otherInt) + return bigU.Cmp(&otherBigInt) == 0 +} + +func (u Int257) Compare(other any) (int, bool) { + otherInt, ok := other.(Int257) + if !ok { + return 0, false + } + bigU := big.Int(u) + otherBigInt := big.Int(otherInt) + return bigU.Cmp(&otherBigInt), true +} + func (u Int257) MarshalJSON() ([]byte, error) { i := big.Int(u) return []byte(fmt.Sprintf("\"%s\"", i.String())), nil @@ -7817,6 +7878,26 @@ func (u Uint128) FixedSize() int { return 128 } +func (u Uint128) Equal(other any) bool { + otherUint, ok := other.(Uint128) + if !ok { + return false + } + bigU := big.Int(u) + otherBigUint := big.Int(otherUint) + return bigU.Cmp(&otherBigUint) == 0 +} + +func (u Uint128) Compare(other any) (int, bool) { + otherUint, ok := other.(Uint128) + if !ok { + return 0, false + } + bigU := big.Int(u) + otherBigUint := big.Int(otherUint) + return bigU.Cmp(&otherBigUint), true +} + func (u Uint128) MarshalJSON() ([]byte, error) { i := big.Int(u) return []byte(fmt.Sprintf("\"%s\"", i.String())), nil @@ -7852,6 +7933,26 @@ func (u Uint160) FixedSize() int { return 160 } +func (u Uint160) Equal(other any) bool { + otherUint, ok := other.(Uint160) + if !ok { + return false + } + bigU := big.Int(u) + otherBigUint := big.Int(otherUint) + return bigU.Cmp(&otherBigUint) == 0 +} + +func (u Uint160) Compare(other any) (int, bool) { + otherUint, ok := other.(Uint160) + if !ok { + return 0, false + } + bigU := big.Int(u) + otherBigUint := big.Int(otherUint) + return bigU.Cmp(&otherBigUint), true +} + func (u Uint160) MarshalJSON() ([]byte, error) { i := big.Int(u) return []byte(fmt.Sprintf("\"%s\"", i.String())), nil @@ -7887,6 +7988,26 @@ func (u Uint220) FixedSize() int { return 220 } +func (u Uint220) Equal(other any) bool { + otherUint, ok := other.(Uint220) + if !ok { + return false + } + bigU := big.Int(u) + otherBigUint := big.Int(otherUint) + return bigU.Cmp(&otherBigUint) == 0 +} + +func (u Uint220) Compare(other any) (int, bool) { + otherUint, ok := other.(Uint220) + if !ok { + return 0, false + } + bigU := big.Int(u) + otherBigUint := big.Int(otherUint) + return bigU.Cmp(&otherBigUint), true +} + func (u Uint220) MarshalJSON() ([]byte, error) { i := big.Int(u) return []byte(fmt.Sprintf("\"%s\"", i.String())), nil @@ -7922,6 +8043,26 @@ func (u Uint256) FixedSize() int { return 256 } +func (u Uint256) Equal(other any) bool { + otherUint, ok := other.(Uint256) + if !ok { + return false + } + bigU := big.Int(u) + otherBigUint := big.Int(otherUint) + return bigU.Cmp(&otherBigUint) == 0 +} + +func (u Uint256) Compare(other any) (int, bool) { + otherUint, ok := other.(Uint256) + if !ok { + return 0, false + } + bigU := big.Int(u) + otherBigUint := big.Int(otherUint) + return bigU.Cmp(&otherBigUint), true +} + func (u Uint256) MarshalJSON() ([]byte, error) { i := big.Int(u) return []byte(fmt.Sprintf("\"%s\"", i.String())), nil @@ -8240,4 +8381,3 @@ func (u Bits512) Compare(other any) (int, bool) { } return bytes.Compare(u[:], otherBits[:]), true } - \ No newline at end of file diff --git a/tlb/parser/builtin_generator.go b/tlb/parser/builtin_generator.go index 989233cc..43cf75b7 100644 --- a/tlb/parser/builtin_generator.go +++ b/tlb/parser/builtin_generator.go @@ -234,6 +234,26 @@ func (u Int{{.NameIndex}}) FixedSize() int { return {{.NameIndex}} } +func (u Int{{.NameIndex}}) Equal(other any) bool { + otherInt, ok := other.(Int{{.NameIndex}}) + if !ok { + return false + } + bigU := big.Int(u) + otherBigInt := big.Int(otherInt) + return bigU.Cmp(&otherBigInt) == 0 +} + +func (u Int{{.NameIndex}}) Compare(other any) (int, bool) { + otherInt, ok := other.(Int{{.NameIndex}}) + if !ok { + return 0, false + } + bigU := big.Int(u) + otherBigInt := big.Int(otherInt) + return bigU.Cmp(&otherBigInt), true +} + func (u Int{{.NameIndex}}) MarshalJSON() ([]byte, error) { i := big.Int(u) return []byte(fmt.Sprintf("\"%s\"", i.String())), nil @@ -293,6 +313,26 @@ func (u Uint{{.NameIndex}}) FixedSize() int { return {{.NameIndex}} } +func (u Uint{{.NameIndex}}) Equal(other any) bool { + otherUint, ok := other.(Uint{{.NameIndex}}) + if !ok { + return false + } + bigU := big.Int(u) + otherBigUint := big.Int(otherUint) + return bigU.Cmp(&otherBigUint) == 0 +} + +func (u Uint{{.NameIndex}}) Compare(other any) (int, bool) { + otherUint, ok := other.(Uint{{.NameIndex}}) + if !ok { + return 0, false + } + bigU := big.Int(u) + otherBigUint := big.Int(otherUint) + return bigU.Cmp(&otherBigUint), true +} + func (u Uint{{.NameIndex}}) MarshalJSON() ([]byte, error) { i := big.Int(u) return []byte(fmt.Sprintf("\"%s\"", i.String())), nil diff --git a/tolk/addresses.go b/tolk/addresses.go new file mode 100644 index 00000000..1dcd6971 --- /dev/null +++ b/tolk/addresses.go @@ -0,0 +1,490 @@ +package tolk + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "fmt" + "strconv" + "strings" + + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tolk/parser" +) + +type InternalAddress struct { + Workchain int8 + Address [32]byte +} + +func (i *InternalAddress) Unmarshal(cell *boc.Cell, ty parser.Address, decoder *Decoder) error { + err := cell.Skip(3) // skip addr type ($10) and anycast (0) + if err != nil { + return fmt.Errorf("failed to skip internal address type and anycast: %w", err) + } + workchain, err := cell.ReadInt(8) + if err != nil { + return fmt.Errorf("failed to read internal address workchain: %w", err) + } + address, err := cell.ReadBytes(32) + if err != nil { + return fmt.Errorf("failed to read internal address hash: %w", err) + } + *i = InternalAddress{ + Workchain: int8(workchain), + Address: [32]byte(address), + } + return nil +} + +func (i *InternalAddress) Marshal(cell *boc.Cell, ty parser.Address, encoder *Encoder) error { + err := cell.WriteUint(0b100, 3) // internal addr type ($10) and anycast (0) + if err != nil { + return fmt.Errorf("failed to write internal address type and anycast: %w", err) + } + err = cell.WriteInt(int64(i.Workchain), 8) + if err != nil { + return fmt.Errorf("failed to write internal address workchain: %w", err) + } + err = cell.WriteBytes(i.Address[:]) + if err != nil { + return fmt.Errorf("failed to write internal address hash: %w", err) + } + return nil +} + +func (i *InternalAddress) Equal(other any) bool { + otherInternalAddress, ok := other.(InternalAddress) + if !ok { + return false + } + return *i == otherInternalAddress +} + +func (i *InternalAddress) ToRaw() string { + return fmt.Sprintf("%v:%x", i.Workchain, i.Address) +} + +func (i InternalAddress) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf("\"%v:%x\"", i.Workchain, i.Address)), nil +} + +func (i *InternalAddress) UnmarshalJSON(b []byte) error { + if len(b) < 2 { + return fmt.Errorf("invalid internal address format: %s", string(b)) + } + addr := strings.Split(string(b[1:len(b)-1]), ":") + if len(addr) != 2 { + return fmt.Errorf("invalid internal address format: %s", string(b)) + } + workchain, err := strconv.ParseInt(addr[0], 10, 32) + if err != nil { + return fmt.Errorf("failed to parse internal address workchain: %w", err) + } + hexAddr, err := hex.DecodeString(addr[1]) + if err != nil { + return fmt.Errorf("failed to parse internal address hash: %w", err) + } + i.Workchain = int8(workchain) + i.Address = [32]byte(hexAddr) + return nil +} + +type NoneAddress struct { +} + +func (n *NoneAddress) Unmarshal(cell *boc.Cell, ty parser.AddressOpt, decoder *Decoder) error { + _, err := cell.ReadUint(2) + if err != nil { + return fmt.Errorf("failed to read none address type: %w", err) + } + + return nil +} + +func (n *NoneAddress) Marshal(cell *boc.Cell, ty parser.AddressOpt, encoder *Encoder) error { + err := cell.WriteUint(0, 2) // none addr type ($00) + if err != nil { + return fmt.Errorf("failed to write none address type: %w", err) + } + return nil +} + +func (n NoneAddress) MarshalJSON() ([]byte, error) { + return []byte("\"\""), nil +} + +func (n *NoneAddress) UnmarshalJSON(b []byte) error { + return nil +} + +type OptionalAddress struct { + SumType + NoneAddress *NoneAddress + InternalAddress *InternalAddress +} + +func (o *OptionalAddress) Equal(other any) bool { + otherOptionalAddress, ok := other.(OptionalAddress) + if !ok { + return false + } + if o.SumType != otherOptionalAddress.SumType { + return false + } + if o.SumType == "InternalAddress" { + return o.InternalAddress.Equal(*otherOptionalAddress.InternalAddress) + } + return true +} + +func (o *OptionalAddress) Unmarshal(cell *boc.Cell, ty parser.AddressOpt, decoder *Decoder) error { + copyCell := cell.CopyRemaining() + tag, err := copyCell.ReadUint(2) + if err != nil { + return fmt.Errorf("failed to read optional address type: %w", err) + } + if tag == 0 { + o.SumType = "NoneAddress" + o.NoneAddress = &NoneAddress{} + err = o.NoneAddress.Unmarshal(cell, ty, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal optional address: %w", err) + } + return err + } + + o.SumType = "InternalAddress" + o.InternalAddress = &InternalAddress{} + err = o.InternalAddress.Unmarshal(cell, parser.Address{}, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal optional address: %w", err) + } + return nil +} + +func (o *OptionalAddress) Marshal(cell *boc.Cell, ty parser.AddressOpt, encoder *Encoder) error { + if o.SumType == "NoneAddress" { + err := o.NoneAddress.Marshal(cell, ty, encoder) + if err != nil { + return fmt.Errorf("failed to marshal optional address: %w", err) + } + return nil + } else if o.SumType == "InternalAddress" { + err := o.InternalAddress.Marshal(cell, parser.Address{}, encoder) + if err != nil { + return fmt.Errorf("failed to marshal optional address: %w", err) + } + return nil + } + return fmt.Errorf("unknown optional address SumType: %v", o.SumType) +} + +func (o OptionalAddress) MarshalJSON() ([]byte, error) { + if o.SumType == "NoneAddress" { + return json.Marshal(o.NoneAddress) + } else if o.SumType == "InternalAddress" { + return json.Marshal(o.InternalAddress) + } else { + return nil, fmt.Errorf("unknown optional address SumType: %v", o.SumType) + } +} + +func (o *OptionalAddress) UnmarshalJSON(b []byte) error { + var internalAddress *InternalAddress + if err := json.Unmarshal(b, &internalAddress); err != nil { + o.SumType = "NoneAddress" + o.NoneAddress = &NoneAddress{} + return nil + } + + o.SumType = "InternalAddress" + o.InternalAddress = internalAddress + return nil +} + +type ExternalAddress struct { + Len int16 + Address boc.BitString +} + +func (e *ExternalAddress) Unmarshal(cell *boc.Cell, ty parser.AddressExt, decoder *Decoder) error { + err := cell.Skip(2) + if err != nil { + return fmt.Errorf("failed to skip external address type: %w", err) + } + ln, err := cell.ReadUint(9) + if err != nil { + return fmt.Errorf("failed to read external address length: %w", err) + } + bs, err := cell.ReadBits(int(ln)) + if err != nil { + return fmt.Errorf("failed to read external address bytes: %w", err) + } + *e = ExternalAddress{ + Len: int16(ln), + Address: bs, + } + + return nil +} + +func (e *ExternalAddress) Marshal(cell *boc.Cell, ty parser.AddressExt, encoder *Encoder) error { + err := cell.WriteUint(1, 2) // external addr type ($01) + if err != nil { + return fmt.Errorf("failed to write external address type: %w", err) + } + err = cell.WriteUint(uint64(e.Len), 9) + if err != nil { + return fmt.Errorf("failed to write external address length: %w", err) + } + err = cell.WriteBitString(e.Address) + if err != nil { + return fmt.Errorf("failed to write external address bytes: %w", err) + } + return nil +} + +func (e *ExternalAddress) Equal(other any) bool { + otherExternalAddress, ok := other.(ExternalAddress) + if !ok { + return false + } + if e.Len != otherExternalAddress.Len { + return false + } + return bytes.Equal(e.Address.Buffer(), otherExternalAddress.Address.Buffer()) +} + +func (e ExternalAddress) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf("\"%s\"", e.Address.ToFiftHex())), nil +} + +func (e *ExternalAddress) UnmarshalJSON(b []byte) error { + addr, err := boc.BitStringFromFiftHex(string(b[1 : len(b)-1])) + if err != nil { + return fmt.Errorf("invalid external address format: %v", string(b)) + } + e.Len = int16(addr.BitsAvailableForRead()) + e.Address = *addr + + return nil +} + +type AnyAddress struct { + SumType + InternalAddress *InternalAddress + NoneAddress *NoneAddress + ExternalAddress *ExternalAddress + VarAddress *VarAddress +} + +func (a *AnyAddress) Unmarshal(cell *boc.Cell, ty parser.AddressAny, decoder *Decoder) error { + copyCell := cell.CopyRemaining() + tag, err := copyCell.ReadUint(2) + if err != nil { + return fmt.Errorf("failed to read any address type: %w", err) + } + switch tag { + case 0: + a.SumType = "NoneAddress" + a.NoneAddress = &NoneAddress{} + err = a.NoneAddress.Unmarshal(cell, parser.AddressOpt{}, decoder) + case 1: + a.SumType = "ExternalAddress" + a.ExternalAddress = &ExternalAddress{} + err = a.ExternalAddress.Unmarshal(cell, parser.AddressExt{}, decoder) + case 2: + a.SumType = "InternalAddress" + a.InternalAddress = &InternalAddress{} + err = a.InternalAddress.Unmarshal(cell, parser.Address{}, decoder) + case 3: + a.SumType = "VarAddress" + a.VarAddress = &VarAddress{} + err = a.VarAddress.Unmarshal(cell, parser.AddressExt{}, decoder) + } + if err != nil { + return fmt.Errorf("failed to unmarshal any address: %w", err) + } + return nil +} + +func (a *AnyAddress) Marshal(cell *boc.Cell, ty parser.AddressAny, encoder *Encoder) error { + var err error + switch a.SumType { + case "NoneAddress": + err = a.NoneAddress.Marshal(cell, parser.AddressOpt{}, encoder) + case "InternalAddress": + err = a.InternalAddress.Marshal(cell, parser.Address{}, encoder) + case "ExternalAddress": + err = a.ExternalAddress.Marshal(cell, parser.AddressExt{}, encoder) + case "VarAddress": + err = a.VarAddress.Marshal(cell, parser.AddressAny{}, encoder) + default: + return fmt.Errorf("unknown any address SumType: %v", a.SumType) + } + if err != nil { + return fmt.Errorf("failed to marshal any address: %w", err) + } + + return nil +} + +func (a *AnyAddress) Equal(other any) bool { + otherAnyAddress, ok := other.(AnyAddress) + if !ok { + return false + } + if otherAnyAddress.SumType != a.SumType { + return false + } + switch a.SumType { + case "NoneAddress": + return true + case "InternalAddress": + return a.InternalAddress.Equal(*otherAnyAddress.InternalAddress) + case "ExternalAddress": + return a.ExternalAddress.Equal(*otherAnyAddress.ExternalAddress) + case "VarAddress": + return a.VarAddress.Equal(*otherAnyAddress.VarAddress) + } + return false +} + +func (a AnyAddress) MarshalJSON() ([]byte, error) { + var data []byte + var err error + switch a.SumType { + case "NoneAddress": + data, err = json.Marshal(a.NoneAddress) + case "InternalAddress": + data, err = json.Marshal(a.InternalAddress) + case "ExternalAddress": + data, err = json.Marshal(a.ExternalAddress) + case "VarAddress": + data, err = json.Marshal(a.VarAddress) + default: + return nil, fmt.Errorf("unknown any address SumType: %v", a.SumType) + } + if err != nil { + return nil, fmt.Errorf("failed to marshal AnyAddress: %w", err) + } + return data, nil +} + +func (a *AnyAddress) UnmarshalJSON(b []byte) error { + var internalAddress *InternalAddress + if err := json.Unmarshal(b, &internalAddress); err == nil { + a.SumType = "InternalAddress" + a.InternalAddress = internalAddress + return nil + } + + var externalAddress *ExternalAddress + if err := json.Unmarshal(b, &externalAddress); err == nil { + a.SumType = "ExternalAddress" + a.ExternalAddress = externalAddress + } + + var varAddress *VarAddress + if err := json.Unmarshal(b, &varAddress); err == nil { + a.SumType = "VarAddress" + a.VarAddress = varAddress + } + + a.SumType = "NoneAddress" + a.NoneAddress = &NoneAddress{} + return nil +} + +type VarAddress struct { + Len int16 + Workchain int32 + Address boc.BitString +} + +func (va *VarAddress) Equal(other any) bool { + otherVarAddress, ok := other.(VarAddress) + if !ok { + return false + } + if va.Len != otherVarAddress.Len { + return false + } + if va.Workchain != otherVarAddress.Workchain { + return false + } + return bytes.Equal(va.Address.Buffer(), otherVarAddress.Address.Buffer()) +} + +func (va *VarAddress) Unmarshal(cell *boc.Cell, ty parser.AddressExt, decoder *Decoder) error { + err := cell.Skip(3) // skip var type ($11) and anycast (0) + if err != nil { + return fmt.Errorf("failed to skip var address type and anycast: %w", err) + } + ln, err := cell.ReadUint(9) + if err != nil { + return fmt.Errorf("failed to read var address length: %w", err) + } + workchain, err := cell.ReadInt(32) + if err != nil { + return fmt.Errorf("failed to read var address workchain: %w", err) + } + bs, err := cell.ReadBits(int(ln)) + if err != nil { + return fmt.Errorf("failed to read var address bytes: %w", err) + } + *va = VarAddress{ + Len: int16(ln), + Workchain: int32(workchain), + Address: bs, + } + + return nil +} + +func (va *VarAddress) Marshal(cell *boc.Cell, ty parser.AddressAny, encoder *Encoder) error { + err := cell.WriteUint(0b110, 3) // var addr type ($11) and anycast (0) + if err != nil { + return fmt.Errorf("failed to write var address type and anycast: %w", err) + } + err = cell.WriteUint(uint64(va.Len), 9) + if err != nil { + return fmt.Errorf("failed to write var address length: %w", err) + } + err = cell.WriteInt(int64(va.Workchain), 32) + if err != nil { + return fmt.Errorf("failed to write var address workchain: %w", err) + } + err = cell.WriteBitString(va.Address) + if err != nil { + return fmt.Errorf("failed to write var address bytes: %w", err) + } + return nil +} + +func (va VarAddress) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf("\"%d:%s\"", va.Workchain, va.Address.ToFiftHex())), nil +} + +func (va *VarAddress) UnmarshalJSON(b []byte) error { + if len(b) < 2 { + return fmt.Errorf("invalid var address format: %v", string(b)) + } + parts := bytes.Split(b[1:len(b)-1], []byte(":")) + if len(parts) != 2 { + return fmt.Errorf("invalid var address format: %v", string(b)) + } + workchain, err := strconv.ParseInt(string(parts[0]), 10, 32) + if err != nil { + return fmt.Errorf("failed to parse var address workchain: %w", err) + } + bs, err := boc.BitStringFromFiftHex(string(parts[1])) + if err != nil { + return fmt.Errorf("failed to parse var address bytes: %w", err) + } + va.Workchain = int32(workchain) + va.Len = int16(bs.BitsAvailableForRead()) + va.Address = *bs + return nil +} diff --git a/tolk/cells.go b/tolk/cells.go new file mode 100644 index 00000000..51b34d2c --- /dev/null +++ b/tolk/cells.go @@ -0,0 +1,262 @@ +package tolk + +import ( + "encoding/json" + "fmt" + + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tolk/parser" +) + +type Any boc.Cell + +func (a *Any) Unmarshal(cell *boc.Cell, ty parser.Cell, decoder *Decoder) error { + ref, err := cell.NextRef() + if err != nil { + return fmt.Errorf("failed to get next ref: %w", err) + } + *a = Any(*ref) + + return nil +} + +func (a *Any) Marshal(cell *boc.Cell, ty parser.Cell, encoder *Encoder) error { + c := boc.Cell(*a) + ref := c.CopyRemaining() + err := cell.AddRef(ref) + if err != nil { + return fmt.Errorf("failed to add ref: %w", err) + } + + return nil +} + +func (a *Any) Equal(o any) bool { + other, ok := o.(Any) + if !ok { + return false + } + cellV := boc.Cell(*a) + vHash, err := cellV.HashString() + if err != nil { + return false + } + cellO := boc.Cell(other) + oHash, err := cellO.HashString() + if err != nil { + return false + } + return oHash == vHash +} + +func (a Any) MarshalJSON() ([]byte, error) { + data, err := boc.Cell(a).MarshalJSON() + if err != nil { + return nil, fmt.Errorf("failed to marshal any: %w", err) + } + return data, nil +} + +func (a *Any) UnmarshalJSON(b []byte) error { + v := &boc.Cell{} + if err := json.Unmarshal(b, v); err != nil { + return fmt.Errorf("failed to unmarshal ref: %w", err) + } + *a = Any(*v) + return nil +} + +type RemainingValue boc.Cell + +func (r *RemainingValue) Unmarshal(cell *boc.Cell, ty parser.Remaining, decoder *Decoder) error { + rem := cell.CopyRemaining() + cell.ReadRemainingBits() + if rem != nil { + *r = RemainingValue(*rem) + } + return nil +} + +func (r *RemainingValue) Marshal(cell *boc.Cell, ty parser.Remaining, encoder *Encoder) error { + c := boc.Cell(*r) + err := cell.WriteBitString(c.ReadRemainingBits()) + if err != nil { + return fmt.Errorf("failed to write remaining bits: %w", err) + } + for i, ref := range c.Refs() { + err = cell.AddRef(ref) + if err != nil { + return fmt.Errorf("failed to add %v remaining ref: %w", i, err) + } + } + + return nil +} + +func (r *RemainingValue) Equal(o any) bool { + other, ok := o.(RemainingValue) + if !ok { + return false + } + cellV := boc.Cell(*r) + vHash, err := cellV.HashString() + if err != nil { + return false + } + cellO := boc.Cell(other) + oHash, err := cellO.HashString() + if err != nil { + return false + } + return oHash == vHash +} + +func (r RemainingValue) MarshalJSON() ([]byte, error) { + data, err := boc.Cell(r).MarshalJSON() + if err != nil { + return nil, fmt.Errorf("failed to marshal remainings: %w", err) + } + return data, nil +} + +func (r *RemainingValue) UnmarshalJSON(b []byte) error { + v := &boc.Cell{} + if err := json.Unmarshal(b, v); err != nil { + return fmt.Errorf("failed to unmarshal remainigs: %w", err) + } + *r = RemainingValue(*v) + return nil +} + +type OptValue struct { + IsExists bool + Val Value +} + +func (o *OptValue) Unmarshal(cell *boc.Cell, ty parser.Nullable, decoder *Decoder) error { + isExists, err := cell.ReadBit() + if err != nil { + return fmt.Errorf("failed to read optinal value existance bit: %w", err) + } + o.IsExists = isExists + if isExists { + err = o.Val.Unmarshal(cell, ty.Inner, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal optinal value: %w", err) + } + } + return nil +} + +func (o *OptValue) Marshal(cell *boc.Cell, ty parser.Nullable, encoder *Encoder) error { + err := cell.WriteBit(o.IsExists) + if err != nil { + return fmt.Errorf("failed to write optinal value existance bit: %w", err) + } + if o.IsExists { + err = o.Val.Marshal(cell, ty.Inner, encoder) + if err != nil { + return fmt.Errorf("failed to marshal optinal value: %w", err) + } + } + + return nil +} + +func (o *OptValue) Equal(other any) bool { + otherOptValue, ok := other.(OptValue) + if !ok { + return false + } + if o.IsExists != otherOptValue.IsExists { + return false + } + if o.IsExists { + return o.Val.Equal(otherOptValue.Val) + } + return true +} + +func (o OptValue) MarshalJSON() ([]byte, error) { + var jsonOptValue = struct { + IsExists bool `json:"isExists"` + Val *Value `json:"value,omitempty"` + }{ + IsExists: o.IsExists, + } + if o.IsExists { + jsonOptValue.Val = &o.Val + } + + data, err := json.Marshal(jsonOptValue) + if err != nil { + return nil, fmt.Errorf("failed to marshal optinal value: %w", err) + } + return data, nil +} + +func (o *OptValue) UnmarshalJSON(b []byte) error { + var jsonOptValue = struct { + IsExists bool `json:"isExists"` + Val *Value `json:"value,omitempty"` + }{} + if err := json.Unmarshal(b, &jsonOptValue); err != nil { + return fmt.Errorf("failed to unmarshal optinal value: %w", err) + } + o.IsExists = jsonOptValue.IsExists + if o.IsExists { + o.Val = *jsonOptValue.Val + } + + return nil +} + +type RefValue Value + +func (r *RefValue) Unmarshal(cell *boc.Cell, ty parser.CellOf, decoder *Decoder) error { + ref, err := cell.NextRef() + if err != nil { + return fmt.Errorf("failed to get next ref: %w", err) + } + innerV := Value{} + err = innerV.Unmarshal(ref, ty.Inner, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal ref: %w", err) + } + *r = RefValue(innerV) + + return nil +} + +func (r *RefValue) Marshal(cell *boc.Cell, ty parser.CellOf, encoder *Encoder) error { + val := Value(*r) + ref := boc.NewCell() + err := val.Marshal(ref, ty.Inner, encoder) + if err != nil { + return fmt.Errorf("failed to marshal ref: %w", err) + } + err = cell.AddRef(ref) + if err != nil { + return fmt.Errorf("failed to add ref: %w", err) + } + + return nil +} + +func (r *RefValue) Equal(other any) bool { + otherRefValue, ok := other.(RefValue) + if !ok { + return false + } + v := Value(*r) + return v.Equal(Value(otherRefValue)) +} + +func (r RefValue) MarshalJSON() ([]byte, error) { + v := Value(r) + data, err := v.MarshalJSON() + if err != nil { + return nil, fmt.Errorf("failed to marshal ref: %w", err) + } + return data, nil +} diff --git a/tolk/integers.go b/tolk/integers.go new file mode 100644 index 00000000..ed11b901 --- /dev/null +++ b/tolk/integers.go @@ -0,0 +1,395 @@ +package tolk + +import ( + "bytes" + "encoding/json" + "fmt" + "math/big" + + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tolk/parser" +) + +type Int64 int64 + +func (i *Int64) Unmarshal(cell *boc.Cell, ty parser.IntN, decoder *Decoder) error { + num, err := cell.ReadInt(ty.N) + if err != nil { + return fmt.Errorf("failed to read %v-bit integer: %w", ty.N, err) + } + *i = Int64(num) + return nil +} + +func (i *Int64) Marshal(cell *boc.Cell, ty parser.IntN, encoder *Encoder) error { + err := cell.WriteInt(int64(*i), ty.N) + if err != nil { + return fmt.Errorf("failed to write %v-bit integer: %w", ty.N, err) + } + return nil +} + +func (i *Int64) Equal(other any) bool { + otherInt, ok := other.(Int64) + if !ok { + return false + } + return *i == otherInt +} + +type BigInt big.Int + +func (b *BigInt) Unmarshal(cell *boc.Cell, ty parser.IntN, decoder *Decoder) error { + num, err := cell.ReadBigInt(ty.N) + if err != nil { + return fmt.Errorf("failed to read %v-bit big integer: %w", ty.N, err) + } + *b = BigInt(*num) + return nil +} + +func (b *BigInt) Marshal(cell *boc.Cell, ty parser.IntN, encoder *Encoder) error { + bi := big.Int(*b) + err := cell.WriteBigInt(&bi, ty.N) + if err != nil { + return fmt.Errorf("failed to write %v-bit big integer: %w", ty.N, err) + } + return nil +} + +func (b *BigInt) Equal(other any) bool { + otherBigInt, ok := other.(BigInt) + if !ok { + return false + } + bi := big.Int(*b) + otherBi := big.Int(otherBigInt) + return bi.Cmp(&otherBi) == 0 +} + +func (b BigInt) MarshalJSON() ([]byte, error) { + bi := big.Int(b) + return []byte(`"` + bi.String() + `"`), nil +} + +func (b *BigInt) UnmarshalJSON(bytes []byte) error { + if len(bytes) < 2 { + return fmt.Errorf("invalid big int format: %s", string(bytes)) + } + bi, ok := new(big.Int).SetString(string(bytes[1:len(bytes)-1]), 10) + if !ok { + return fmt.Errorf("failed to parse big integer from %v", string(bytes[1:len(bytes)-1])) + } + *b = BigInt(*bi) + return nil +} + +type UInt64 uint64 + +func (i *UInt64) Unmarshal(cell *boc.Cell, ty parser.UintN, decoder *Decoder) error { + num, err := cell.ReadUint(ty.N) + if err != nil { + return fmt.Errorf("failed to read %v-bit unsigned integer: %w", ty.N, err) + } + *i = UInt64(num) + return nil +} + +func (i *UInt64) Marshal(cell *boc.Cell, ty parser.UintN, encoder *Encoder) error { + err := cell.WriteUint(uint64(*i), ty.N) + if err != nil { + return fmt.Errorf("failed to write %v-bit unsigned integer: %w", ty.N, err) + } + return nil +} + +func (i *UInt64) Equal(other any) bool { + otherUint, ok := other.(UInt64) + if !ok { + return false + } + return *i == otherUint +} + +type BigUInt big.Int + +func (b *BigUInt) Unmarshal(cell *boc.Cell, ty parser.UintN, decoder *Decoder) error { + num, err := cell.ReadBigUint(ty.N) + if err != nil { + return fmt.Errorf("failed to read %v-bit usigned big integer: %w", ty.N, err) + } + *b = BigUInt(*num) + return nil +} + +func (b *BigUInt) Marshal(cell *boc.Cell, ty parser.UintN, encoder *Encoder) error { + bi := big.Int(*b) + err := cell.WriteBigUint(&bi, ty.N) + if err != nil { + return fmt.Errorf("failed to write %v-bit usigned big integer: %w", ty.N, err) + } + return nil +} + +func (b *BigUInt) Equal(other any) bool { + otherBigInt, ok := other.(BigUInt) + if !ok { + return false + } + bi := big.Int(*b) + otherBi := big.Int(otherBigInt) + return bi.Cmp(&otherBi) == 0 +} + +func (b BigUInt) MarshalJSON() ([]byte, error) { + bi := big.Int(b) + return []byte(`"` + bi.String() + `"`), nil +} + +func (b *BigUInt) UnmarshalJSON(bytes []byte) error { + if len(bytes) < 2 { + return fmt.Errorf("invalid usigned big int format: %s", string(bytes)) + } + bi, ok := new(big.Int).SetString(string(bytes[1:len(bytes)-1]), 10) + if !ok { + return fmt.Errorf("failed to parse usigned big interger from %v", string(bytes[1:len(bytes)-1])) + } + *b = BigUInt(*bi) + return nil +} + +type VarInt big.Int + +func (vi *VarInt) Unmarshal(cell *boc.Cell, ty parser.VarIntN, decoder *Decoder) error { + ln, err := cell.ReadLimUint(ty.N - 1) + if err != nil { + return fmt.Errorf("failed to read var integer length: %w", err) + } + val, err := cell.ReadBigInt(int(ln) * 8) + if err != nil { + return fmt.Errorf("failed to read var integer value: %w", err) + } + *vi = VarInt(*val) + return nil +} + +func (vi *VarInt) Marshal(cell *boc.Cell, ty parser.VarIntN, encoder *Encoder) error { + bi := big.Int(*vi) + num := bi.Bytes() + err := cell.WriteLimUint(len(num), ty.N-1) + if err != nil { + return fmt.Errorf("failed to write var integer length: %w", err) + } + err = cell.WriteBigInt(&bi, len(num)*8) + if err != nil { + return fmt.Errorf("failed to write var integer value: %w", err) + } + + return nil +} + +func (vi *VarInt) Equal(other any) bool { + otherBigInt, ok := other.(VarInt) + if !ok { + return false + } + bi := big.Int(*vi) + otherBi := big.Int(otherBigInt) + return bi.Cmp(&otherBi) == 0 +} + +func (vi VarInt) MarshalJSON() ([]byte, error) { + bi := big.Int(vi) + return []byte(`"` + bi.String() + `"`), nil +} + +func (vi *VarInt) UnmarshalJSON(bytes []byte) error { + if len(bytes) < 2 { + return fmt.Errorf("invalid var integer format: %s", string(bytes)) + } + bi, ok := new(big.Int).SetString(string(bytes[1:len(bytes)-1]), 10) + if !ok { + return fmt.Errorf("failed to parse var integer from %v", string(bytes[1:len(bytes)-1])) + } + *vi = VarInt(*bi) + return nil +} + +type VarUInt big.Int + +func (vu *VarUInt) Unmarshal(cell *boc.Cell, ty parser.VarUintN, decoder *Decoder) error { + ln, err := cell.ReadLimUint(ty.N - 1) + if err != nil { + return fmt.Errorf("failed to read usigned var integer length: %w", err) + } + val, err := cell.ReadBigInt(int(ln) * 8) + if err != nil { + return fmt.Errorf("failed to read usigned var integer value: %w", err) + } + *vu = VarUInt(*val) + return nil +} + +func (vu *VarUInt) Marshal(cell *boc.Cell, ty parser.VarUintN, encoder *Encoder) error { + bi := big.Int(*vu) + num := bi.Bytes() + err := cell.WriteLimUint(len(num), ty.N-1) + if err != nil { + return fmt.Errorf("failed to write usigned var integer length: %w", err) + } + err = cell.WriteBytes(num) + if err != nil { + return fmt.Errorf("failed to write usigned var integer value: %w", err) + } + + return nil +} + +func (vu *VarUInt) Equal(other any) bool { + otherBigInt, ok := other.(VarUInt) + if !ok { + return false + } + bi := big.Int(*vu) + otherBi := big.Int(otherBigInt) + return bi.Cmp(&otherBi) == 0 +} + +func (vu VarUInt) MarshalJSON() ([]byte, error) { + bi := big.Int(vu) + return []byte(`"` + bi.String() + `"`), nil +} + +func (vu *VarUInt) UnmarshalJSON(bytes []byte) error { + if len(bytes) < 2 { + return fmt.Errorf("invalid var integer format: %s", string(bytes)) + } + bi, ok := new(big.Int).SetString(string(bytes[1:len(bytes)-1]), 10) + if !ok { + return fmt.Errorf("failed to parse usigned var integer from %v", string(bytes[1:len(bytes)-1])) + } + *vu = VarUInt(*bi) + return nil +} + +type Bits boc.BitString + +func (b *Bits) Unmarshal(cell *boc.Cell, ty parser.BitsN, decoder *Decoder) error { + val, err := cell.ReadBits(ty.N) + if err != nil { + return fmt.Errorf("failed to read bits value: %w", err) + } + *b = Bits(val) + return nil +} + +func (b *Bits) Marshal(cell *boc.Cell, ty parser.BitsN, encoder *Encoder) error { + bi := boc.BitString(*b) + err := cell.WriteBitString(bi) + if err != nil { + return fmt.Errorf("failed to write bits value: %w", err) + } + + return nil +} + +func (b *Bits) Equal(other any) bool { + otherBits, ok := other.(Bits) + if !ok { + return false + } + bs := boc.BitString(*b) + otherBs := boc.BitString(otherBits) + return bytes.Equal(bs.Buffer(), otherBs.Buffer()) +} + +func (b Bits) MarshalJSON() ([]byte, error) { + data, err := boc.BitString(b).MarshalJSON() + if err != nil { + return nil, fmt.Errorf("failed to marshal bits value: %w", err) + } + return data, nil +} + +func (b *Bits) UnmarshalJSON(bytes []byte) error { + bs := boc.BitString{} + if err := json.Unmarshal(bytes, &bs); err != nil { + return fmt.Errorf("failed to unmarshal bits value: %w", err) + } + *b = Bits(bs) + return nil +} + +type CoinsValue big.Int + +func (c *CoinsValue) Unmarshal(cell *boc.Cell, ty parser.Coins, decoder *Decoder) error { + varUint := VarUInt{} + if err := varUint.Unmarshal(cell, parser.VarUintN{N: 16}, decoder); err != nil { + return fmt.Errorf("failed to unmarshal coins value: %w", err) + } + *c = CoinsValue(varUint) + return nil +} + +func (c *CoinsValue) Marshal(cell *boc.Cell, ty parser.Coins, encoder *Encoder) error { + varUint := VarUInt(*c) + err := varUint.Marshal(cell, parser.VarUintN{N: 16}, encoder) // coins is actually varuint16 + if err != nil { + return fmt.Errorf("failed to marshal coins value: %w", err) + } + return nil +} + +func (c *CoinsValue) Equal(other any) bool { + otherBigInt, ok := other.(CoinsValue) + if !ok { + return false + } + bi := big.Int(*c) + otherBi := big.Int(otherBigInt) + return bi.Cmp(&otherBi) == 0 +} + +func (c CoinsValue) MarshalJSON() ([]byte, error) { + bi := big.Int(c) + return []byte(`"` + bi.String() + `"`), nil +} + +func (c *CoinsValue) UnmarshalJSON(bytes []byte) error { + if len(bytes) < 2 { + return fmt.Errorf("invalid coins format: %s", string(bytes)) + } + bi, ok := new(big.Int).SetString(string(bytes[1:len(bytes)-1]), 10) + if !ok { + return fmt.Errorf("failed to parse coins from %v", string(bytes[1:len(bytes)-1])) + } + *c = CoinsValue(*bi) + return nil +} + +type BoolValue bool + +func (b *BoolValue) Equal(o any) bool { + otherBool, ok := o.(BoolValue) + if !ok { + return false + } + return *b == otherBool +} + +func (b *BoolValue) Unmarshal(cell *boc.Cell, ty parser.Bool, decoder *Decoder) error { + val, err := cell.ReadBit() + if err != nil { + return fmt.Errorf("failed to read bool value: %w", err) + } + *b = BoolValue(val) + return nil +} + +func (b *BoolValue) Marshal(cell *boc.Cell, ty parser.Bool, encoder *Encoder) error { + err := cell.WriteBit(bool(*b)) + if err != nil { + return fmt.Errorf("failed to write bool value: %w", err) + } + return nil +} diff --git a/tolk/map.go b/tolk/map.go new file mode 100644 index 00000000..fdb66361 --- /dev/null +++ b/tolk/map.go @@ -0,0 +1,661 @@ +package tolk + +import ( + "encoding/json" + "errors" + "fmt" + "math" + "strings" + + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tolk/parser" +) + +type MapValue struct { + keys []Value + values []Value + len int +} + +func (m *MapValue) Unmarshal(cell *boc.Cell, ty parser.Map, decoder *Decoder) error { + keySize, ok := ty.K.GetFixedSize() + if !ok { + return fmt.Errorf("%v type is not comparable", ty.K.SumType) + } + keyPrefix := boc.NewBitString(keySize) + + isNotEmpty, err := cell.ReadBit() + if err != nil { + return err + } + mp := MapValue{ + keys: make([]Value, 0), + values: make([]Value, 0), + } + if isNotEmpty { + mpCell, err := cell.NextRef() + if err != nil { + return fmt.Errorf("failed to get map's next ref: %w", err) + } + err = mapInner(keySize, keySize, mpCell, &keyPrefix, ty.K, ty.V, &mp.keys, &mp.values, decoder) + if err != nil { + return fmt.Errorf("failed to parse map value: %w", err) + } + } + + m.len = len(mp.keys) + m.keys = mp.keys + m.values = mp.values + + return nil +} + +func mapInner( + keySize, leftKeySize int, + c *boc.Cell, + keyPrefix *boc.BitString, + kt, vt parser.Ty, + keys, values *[]Value, + decoder *Decoder, +) error { + var err error + var size int + if c.CellType() == boc.PrunedBranchCell { + return nil + } + size, keyPrefix, err = loadLabel(leftKeySize, c, keyPrefix) + if err != nil { + return fmt.Errorf("failed to load map's label: %w", err) + } + // until key size is not equals we go deeper + if keyPrefix.BitsAvailableForRead() < keySize { + // 0 bit branch + left, err := c.NextRef() + if err != nil { + return fmt.Errorf("failed to get map's left branch: %w", err) + } + lp := keyPrefix.Copy() + err = lp.WriteBit(false) + if err != nil { + return fmt.Errorf("failed to write map's left branch key prefix: %w", err) + } + err = mapInner(keySize, leftKeySize-(1+size), left, &lp, kt, vt, keys, values, decoder) + if err != nil { + return fmt.Errorf("failed to get map's left value: %w", err) + } + // 1 bit branch + right, err := c.NextRef() + if err != nil { + return fmt.Errorf("failed to get map's right branch: %w", err) + } + rp := keyPrefix.Copy() + err = rp.WriteBit(true) + if err != nil { + return fmt.Errorf("failed to write map's right branch key prefix: %w", err) + } + err = mapInner(keySize, leftKeySize-(1+size), right, &rp, kt, vt, keys, values, decoder) + if err != nil { + return fmt.Errorf("failed to get map's right value: %w", err) + } + return nil + } + // add node to map + v := Value{} + err = v.Unmarshal(c, vt, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal map's value: %w", err) + } + *values = append(*values, v) + + key, err := keyPrefix.ReadBits(keySize) + if err != nil { + return fmt.Errorf("failed to get map's key: %w", err) + } + k := Value{} + cell := boc.NewCellWithBits(key) + err = k.Unmarshal(cell, kt, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal map's key: %w", err) + } + *keys = append(*keys, k) + + return nil +} + +func loadLabel(size int, c *boc.Cell, key *boc.BitString) (int, *boc.BitString, error) { + first, err := c.ReadBit() + if err != nil { + return 0, nil, fmt.Errorf("failed to read map's first bit of label's type: %w", err) + } + // hml_short$0 + if !first { + // Unary, while 1, add to ln + ln, err := c.ReadUnary() + if err != nil { + return 0, nil, fmt.Errorf("failed to read map's short label's length: %w", err) + } + // add bits to key + for i := 0; i < int(ln); i++ { + bit, err := c.ReadBit() + if err != nil { + return 0, nil, fmt.Errorf("failed to read map's short label's %v-bit: %w", i, err) + } + err = key.WriteBit(bit) + if err != nil { + return 0, nil, fmt.Errorf("failed to write map's short label's %v-bit: %w", i, err) + } + } + return int(ln), key, nil + } + second, err := c.ReadBit() + if err != nil { + return 0, nil, fmt.Errorf("failed to read map's second bit of label's type: %w", err) + } + // hml_long$10 + if !second { + ln, err := c.ReadLimUint(size) + if err != nil { + return 0, nil, fmt.Errorf("failed to read map's long label's length: %w", err) + } + for i := 0; i < int(ln); i++ { + bit, err := c.ReadBit() + if err != nil { + return 0, nil, fmt.Errorf("failed to read map's long label's %v-bit: %w", i, err) + } + err = key.WriteBit(bit) + if err != nil { + return 0, nil, fmt.Errorf("failed to write map's long label's %v-bit: %w", i, err) + } + } + return int(ln), key, nil + } + // hml_same$11 + bitType, err := c.ReadBit() + if err != nil { + return 0, nil, fmt.Errorf("failed to read map's same label's bit: %w", err) + } + ln, err := c.ReadLimUint(size) + if err != nil { + return 0, nil, fmt.Errorf("failed to read map's same label's length: %w", err) + } + for i := 0; i < int(ln); i++ { + err = key.WriteBit(bitType) + if err != nil { + return 0, nil, fmt.Errorf("failed to write map's same label's %v-bit: %w", i, err) + } + } + return int(ln), key, nil +} + +func (m *MapValue) Marshal(cell *boc.Cell, ty parser.Map, encoder *Encoder) error { + keySize, ok := ty.K.GetFixedSize() + if !ok { + return fmt.Errorf("%s type is not comparable", ty.K.SumType) + } + + if len(m.keys) != len(m.values) { + return fmt.Errorf("map keys and values lengths do not match") + } + + if len(m.values) == 0 { + err := cell.WriteBit(false) + if err != nil { + return fmt.Errorf("failed to write map's emptiness bit: %w", err) + } + return nil + } + + err := cell.WriteBit(true) + if err != nil { + return fmt.Errorf("failed to write map's not-emptiness bit: %w", err) + } + + keys := make([]boc.BitString, len(m.keys)) + for i, k := range m.keys { + keyCell := boc.NewCell() + err = k.Marshal(keyCell, ty.K, encoder) + if err != nil { + return fmt.Errorf("failed to marshal map's %v key: %w", i, err) + } + keys[i] = keyCell.RawBitString() + } + + ref := boc.NewCell() + err = encodeMap(ref, keys, m.values, keySize, ty.V, encoder) + if err != nil { + return fmt.Errorf("failed to encode map: %w", err) + } + + err = cell.AddRef(ref) + if err != nil { + return fmt.Errorf("failed to add map's ref value: %w", err) + } + + return nil +} + +func encodeMap(c *boc.Cell, keys []boc.BitString, values []Value, keySize int, vt parser.Ty, encoder *Encoder) error { + if len(keys) == 0 || len(values) == 0 { + return fmt.Errorf("keys or values are empty") + } + label, err := encodeLabel(c, &keys[0], &keys[len(keys)-1], keySize) + if err != nil { + return fmt.Errorf("failed to encode map's label: %w", err) + } + keySize = keySize - label.BitsAvailableForRead() - 1 // l = n - m - 1 // see tlb + var leftKeys, rightKeys []boc.BitString + var leftValues, rightValues []Value + if len(keys) > 1 { + for i := range keys { + err = keys[i].Skip(label.BitsAvailableForRead()) // skip common label + if err != nil { + return fmt.Errorf("failed to skip map's key common label: %w", err) + } + isRight, err := keys[i].ReadBit() + if err != nil { + return fmt.Errorf("failed to read map's is right bit: %w", err) + } + if isRight { + rightKeys = append(rightKeys, keys[i].ReadRemainingBits()) + rightValues = append(rightValues, values[i]) + } else { + leftKeys = append(leftKeys, keys[i].ReadRemainingBits()) + leftValues = append(leftValues, values[i]) + } + } + l, err := c.NewRef() + if err != nil { + return fmt.Errorf("failed to create map's left value: %w", err) + } + err = encodeMap(l, leftKeys, leftValues, keySize, vt, encoder) + if err != nil { + return fmt.Errorf("failed to encode map's left value: %w", err) + } + r, err := c.NewRef() + if err != nil { + return fmt.Errorf("failed to create map's right value: %w", err) + } + err = encodeMap(r, rightKeys, rightValues, keySize, vt, encoder) + if err != nil { + return fmt.Errorf("failed to encode map's right value: %w", err) + } + return nil + } + // marshal value + err = values[0].Marshal(c, vt, encoder) + if err != nil { + return fmt.Errorf("failed to marshal map's values: %w", err) + } + return nil +} + +func encodeLabel(c *boc.Cell, keyFirst, keyLast *boc.BitString, keySize int) (boc.BitString, error) { + label := boc.NewBitString(keySize) + if keyFirst != keyLast { + bitLeft, err := keyFirst.ReadBit() + if err != nil { + return boc.BitString{}, fmt.Errorf("failed to read map's first label's bit: %w", err) + } + for keyFirst.BitsAvailableForRead() > 0 { + bitRight, err := keyLast.ReadBit() + if err != nil { + return boc.BitString{}, fmt.Errorf("failed to read map's last label's bit: %w", err) + } + if bitLeft != bitRight { + break + } + if err := label.WriteBit(bitLeft); err != nil { + return boc.BitString{}, fmt.Errorf("failed to write map's label bit: %w", err) + } + bitLeft, err = keyFirst.ReadBit() + if err != nil { + return boc.BitString{}, fmt.Errorf("failed to read map's first label's bit: %w", err) + } + } + } else { + label = keyFirst.Copy() + } + keyFirst.ResetCounter() + keyLast.ResetCounter() + labelLen := label.BitsAvailableForRead() + + // We must find the most compact way to serialize key + hmlShortSize := 2*labelLen + 2 + hmlLongSize := 2 + int(math.Ceil(math.Log2(float64(keySize)+1))) + labelLen + var encodeFunc func(*boc.Cell, int, boc.BitString) error + isShort := false + if hmlShortSize <= hmlLongSize { + isShort = true + encodeFunc = encodeShortLabel + } else { + encodeFunc = encodeLongLabel + } + + // If all bits in label are the same then we can use hml_same + isAllZero := true + isAllOne := true + for label.BitsAvailableForRead() > 0 { + bit, err := label.ReadBit() + if err != nil { + return boc.BitString{}, fmt.Errorf("failed to read map's label's bit: %w", err) + } + if bit { + isAllZero = false + } else { + isAllOne = false + } + } + label.ResetCounter() + if isAllZero || isAllOne { + hmlSameSize := 2 + 1 + int(math.Ceil(math.Log2(float64(keySize)+1))) + if isShort && hmlSameSize < hmlShortSize { + encodeFunc = encodeSameLabel + } else if !isShort && hmlSameSize < hmlLongSize { + encodeFunc = encodeSameLabel + } + } + + err := encodeFunc(c, keySize, label) + if err != nil { + return boc.BitString{}, fmt.Errorf("failed to encode map's label: %w", err) + } + return label, nil +} + +func encodeShortLabel(c *boc.Cell, keySize int, label boc.BitString) error { + //hml_short$0 {m:#} {n:#} len:(Unary ~n) {n <= m} s:(n * Bit) = HmLabel ~n m; + err := c.WriteBit(false) + if err != nil { + return fmt.Errorf("failed to write map's short label type: %w", err) + } + err = c.WriteUnary(uint(label.BitsAvailableForRead())) + if err != nil { + return fmt.Errorf("failed to write map's short label length: %w", err) + } + err = c.WriteBitString(label) + if err != nil { + return fmt.Errorf("failed to write map's short label value: %w", err) + } + return nil +} + +func encodeLongLabel(c *boc.Cell, keySize int, label boc.BitString) error { + // hml_long$10 {m:#} n:(#<= m) s:(n * Bit) = HmLabel ~n m; + err := c.WriteUint(0b10, 2) + if err != nil { + return fmt.Errorf("failed to write map's long label type: %w", err) + } + err = c.WriteLimUint(label.BitsAvailableForRead(), keySize) + if err != nil { + return fmt.Errorf("failed to write map's long label length: %w", err) + } + err = c.WriteBitString(label) + if err != nil { + return fmt.Errorf("failed to write map's long label value: %w", err) + } + return nil +} + +func encodeSameLabel(c *boc.Cell, keySize int, label boc.BitString) error { + //hml_same$11 {m:#} v:Bit n:(#<= m) = HmLabel ~n m; + err := c.WriteUint(0b11, 2) + if err != nil { + return fmt.Errorf("failed to write map's same label type: %w", err) + } + err = c.WriteBit(label.BinaryString()[0] == '1') + if err != nil { + return fmt.Errorf("failed to write map's same label bit: %w", err) + } + err = c.WriteLimUint(label.BitsAvailableForRead(), keySize) + if err != nil { + return fmt.Errorf("failed to write map's same label length: %w", err) + } + return nil +} + +func (m *MapValue) Equal(other any) bool { + otherMapValue, ok := other.(MapValue) + if !ok { + return false + } + if m.len != otherMapValue.len { + return false + } + for i := range m.keys { + if !m.keys[i].Equal(otherMapValue.keys[i]) { + return false + } + if !m.values[i].Equal(otherMapValue.values[i]) { + return false + } + } + return true +} + +func (m *MapValue) get(key Value) (Value, bool) { + for i, k := range m.keys { + if k.Equal(key) { + return m.values[i], true + } + } + + return Value{}, false +} + +func (m *MapValue) GetBySmallInt(v Int64) (Value, bool) { + key := Value{ + SumType: "SmallInt", + SmallInt: &v, + } + return m.get(key) +} + +func (m *MapValue) GetBySmallUInt(v UInt64) (Value, bool) { + key := Value{ + SumType: "SmallUint", + SmallUint: &v, + } + return m.get(key) +} + +func (m *MapValue) GetByBigInt(v BigInt) (Value, bool) { + key := Value{ + SumType: "BigInt", + BigInt: &v, + } + return m.get(key) +} + +func (m *MapValue) GetByBigUInt(v BigUInt) (Value, bool) { + key := Value{ + SumType: "BigUint", + BigUint: &v, + } + return m.get(key) +} + +func (m *MapValue) GetByBits(v Bits) (Value, bool) { + key := Value{ + SumType: "Bits", + Bits: &v, + } + return m.get(key) +} + +func (m *MapValue) GetByInternalAddress(v InternalAddress) (Value, bool) { + key := Value{ + SumType: "InternalAddress", + InternalAddress: &v, + } + return m.get(key) +} + +func (m *MapValue) set(key Value, value Value) (bool, error) { + for i, k := range m.keys { + if k.Equal(key) { + m.values[i] = value + return true, nil + } + } + + m.keys = append(m.keys, key) + m.values = append(m.values, value) + m.len++ + return true, nil +} + +func (m *MapValue) SetBySmallInt(k Int64, value Value) (bool, error) { + key := Value{ + SumType: "SmallInt", + SmallInt: &k, + } + return m.set(key, value) +} + +func (m *MapValue) SetBySmallUInt(k UInt64, value Value) (bool, error) { + key := Value{ + SumType: "SmallUint", + SmallUint: &k, + } + return m.set(key, value) +} + +func (m *MapValue) SetByBigInt(k BigInt, value Value) (bool, error) { + key := Value{ + SumType: "BigInt", + BigInt: &k, + } + return m.set(key, value) +} + +func (m *MapValue) SetByBigUInt(k BigUInt, value Value) (bool, error) { + key := Value{ + SumType: "BigUint", + BigUint: &k, + } + return m.set(key, value) +} + +func (m *MapValue) SetByBits(k Bits, value Value) (bool, error) { + key := Value{ + SumType: "Bits", + Bits: &k, + } + return m.set(key, value) +} + +func (m *MapValue) SetByInternalAddress(k InternalAddress, value Value) (bool, error) { + key := Value{ + SumType: "InternalAddress", + InternalAddress: &k, + } + return m.set(key, value) +} + +func (m *MapValue) delete(key Value) { + for i, k := range m.keys { + if k.Equal(key) { + m.keys[i] = Value{} + m.values[i] = Value{} + m.len-- + } + } +} + +func (m *MapValue) DeleteBySmallInt(k Int64) { + key := Value{ + SumType: "SmallInt", + SmallInt: &k, + } + m.delete(key) +} + +func (m *MapValue) DeleteBySmallUInt(k UInt64) { + key := Value{ + SumType: "SmallUint", + SmallUint: &k, + } + m.delete(key) +} + +func (m *MapValue) DeleteByBigInt(k BigInt) { + key := Value{ + SumType: "BigInt", + BigInt: &k, + } + m.delete(key) +} + +func (m *MapValue) DeleteByBigUInt(k BigUInt) { + key := Value{ + SumType: "BigUint", + BigUint: &k, + } + m.delete(key) +} + +func (m *MapValue) DeleteByBits(k Bits) { + key := Value{ + SumType: "Bits", + Bits: &k, + } + m.delete(key) +} + +func (m *MapValue) DeleteByInternalAddress(k InternalAddress) { + key := Value{ + SumType: "InternalAddress", + InternalAddress: &k, + } + m.delete(key) +} + +func (m *MapValue) Len() int { + return m.len +} + +func (m MapValue) MarshalJSON() ([]byte, error) { + if len(m.keys) != len(m.values) { + return nil, errors.New("map values and keys must contain equal length") + } + s := strings.Builder{} + s.WriteString("{") + if len(m.keys) > 0 { + for i, k := range m.keys { + if i != 0 { + s.WriteString(",") + } + key, err := json.Marshal(k) + if err != nil { + return nil, fmt.Errorf("failed to marshal map's key: %w", err) + } + validKey, err := getJustValue(string(key)) + if err != nil { + return nil, fmt.Errorf("failed to get map's key value: %w", err) + } + // there is no case that key's value may start with '[' since tensors and tuples are not fixed sizes + if validKey[0] == '{' { + return nil, fmt.Errorf("cannot use %v as a map key", key) + } + if validKey[0] != '"' { + validKey = fmt.Sprintf("\"%s\"", validKey) + } + s.WriteString(fmt.Sprintf("%v:", validKey)) + val, err := json.Marshal(m.values[i]) + if err != nil { + return nil, fmt.Errorf("failed to marshal map's value: %w", err) + } + s.Write(val) + } + } + s.WriteString("}") + + return []byte(s.String()), nil +} + +func getJustValue(key string) (string, error) { + if key[0] != '"' { + key = fmt.Sprintf("\"%s\"", key) + } + return strings.ReplaceAll(key, " ", ""), nil +} diff --git a/tolk/null.go b/tolk/null.go new file mode 100644 index 00000000..2235799b --- /dev/null +++ b/tolk/null.go @@ -0,0 +1,50 @@ +package tolk + +import ( + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tolk/parser" +) + +type NullValue struct{} + +func (n *NullValue) Equal(other any) bool { + _, ok := other.(NullValue) + if !ok { + return false + } + return true +} + +func (n *NullValue) Unmarshal(cell *boc.Cell, ty parser.NullLiteral, decoder *Decoder) error { + return nil +} + +func (n *NullValue) Marshal(cell *boc.Cell, ty parser.NullLiteral, encoder *Encoder) error { + return nil +} + +func (n NullValue) MarshalJSON() ([]byte, error) { + return []byte("null"), nil +} + +type VoidValue struct{} + +func (v *VoidValue) Equal(other any) bool { + _, ok := other.(VoidValue) + if !ok { + return false + } + return true +} + +func (v *VoidValue) Unmarshal(cell *boc.Cell, ty parser.Void, decoder *Decoder) error { + return nil +} + +func (v *VoidValue) Marshal(cell *boc.Cell, ty parser.Void, encoder *Encoder) error { + return nil +} + +func (v VoidValue) MarshalJSON() ([]byte, error) { + return []byte("null"), nil +} diff --git a/tolk/parser/shortcuts.go b/tolk/parser/shortcuts.go new file mode 100644 index 00000000..8bedb6e3 --- /dev/null +++ b/tolk/parser/shortcuts.go @@ -0,0 +1,243 @@ +package parser + +func NewIntNType(n int) Ty { + return Ty{ + SumType: "IntN", + IntN: &IntN{ + N: n, + }, + } +} + +func NewUIntNType(n int) Ty { + return Ty{ + SumType: "UintN", + UintN: &UintN{ + N: n, + }, + } +} + +func NewVarInt16Type() Ty { + return Ty{ + SumType: "VarIntN", + VarIntN: &VarIntN{ + N: 16, + }, + } +} + +func NewVarInt32Type() Ty { + return Ty{ + SumType: "VarIntN", + VarIntN: &VarIntN{ + N: 32, + }, + } +} + +func NewVarIntType(n int) Ty { + return Ty{ + SumType: "VarIntN", + VarIntN: &VarIntN{ + N: n, + }, + } +} + +func NewVarUInt16Type() Ty { + return Ty{ + SumType: "VarUintN", + VarUintN: &VarUintN{ + N: 16, + }, + } +} + +func NewVarUInt32Type() Ty { + return Ty{ + SumType: "VarUintN", + VarUintN: &VarUintN{ + N: 32, + }, + } +} + +func NewVarUIntType(n int) Ty { + return Ty{ + SumType: "VarUintN", + VarUintN: &VarUintN{ + N: n, + }, + } +} + +func NewBitsNType(n int) Ty { + return Ty{ + SumType: "BitsN", + BitsN: &BitsN{ + N: n, + }, + } +} + +func NewCoinsType() Ty { + return Ty{ + SumType: "Coins", + Coins: &Coins{}, + } +} + +func NewBoolType() Ty { + return Ty{ + SumType: "Bool", + Bool: &Bool{}, + } +} + +func NewCellType() Ty { + return Ty{ + SumType: "Cell", + Cell: &Cell{}, + } +} + +func NewRemainingType() Ty { + return Ty{ + SumType: "Remaining", + Remaining: &Remaining{}, + } +} + +func NewAddressType() Ty { + return Ty{ + SumType: "Address", + Address: &Address{}, + } +} + +func NewAddressOptType() Ty { + return Ty{ + SumType: "AddressOpt", + AddressOpt: &AddressOpt{}, + } +} + +func NewAddressExtType() Ty { + return Ty{ + SumType: "AddressExt", + AddressExt: &AddressExt{}, + } +} + +func NewAddressAnyType() Ty { + return Ty{ + SumType: "AddressAny", + AddressAny: &AddressAny{}, + } +} + +func NewNullableType(of Ty) Ty { + return Ty{ + SumType: "Nullable", + Nullable: &Nullable{ + Inner: of, + }, + } +} + +func NewCellOfType(of Ty) Ty { + return Ty{ + SumType: "CellOf", + CellOf: &CellOf{ + Inner: of, + }, + } +} + +func NewTensorType(of ...Ty) Ty { + return Ty{ + SumType: "Tensor", + Tensor: &Tensor{ + Items: of, + }, + } +} + +func NewTupleWithType(of ...Ty) Ty { + return Ty{ + SumType: "TupleWith", + TupleWith: &TupleWith{ + Items: of, + }, + } +} + +func NewMapType(key, value Ty) Ty { + return Ty{ + SumType: "Map", + Map: &Map{ + K: key, + V: value, + }, + } +} + +func NewEnumType(name string) Ty { + return Ty{ + SumType: "EnumRef", + EnumRef: &EnumRef{ + EnumName: name, + }, + } +} + +func NewAliasType(name string, typeArgs ...Ty) Ty { + return Ty{ + SumType: "AliasRef", + AliasRef: &AliasRef{ + AliasName: name, + TypeArgs: typeArgs, + }, + } +} + +func NewStructType(name string, typeArgs ...Ty) Ty { + return Ty{ + SumType: "StructRef", + StructRef: &StructRef{ + StructName: name, + TypeArgs: typeArgs, + }, + } +} + +func NewGenericType(nameT string) Ty { + return Ty{ + SumType: "Generic", + Generic: &Generic{ + NameT: nameT, + }, + } +} + +func NewUnionVariant(variant Ty, prefixValue string) UnionVariant { + return UnionVariant{ + PrefixStr: prefixValue, + VariantTy: variant, + } +} + +func NewUnionType(prefixLen int, prefixEatIntPlace bool, variants ...UnionVariant) Ty { + for i := range variants { + variants[i].PrefixLen = prefixLen + variants[i].PrefixEatInPlace = prefixEatIntPlace + } + + return Ty{ + SumType: "Union", + Union: &Union{ + Variants: variants, + }, + } +} diff --git a/tolk/parser/types.go b/tolk/parser/types.go new file mode 100644 index 00000000..31ee23f1 --- /dev/null +++ b/tolk/parser/types.go @@ -0,0 +1,764 @@ +package parser + +import ( + "encoding/json" + "fmt" + "math/big" + + "github.com/tonkeeper/tongo/utils" +) + +type Kind struct { + Kind string `json:"kind"` +} + +type ABI struct { + Namespace string `json:"namespace"` + ContractName string `json:"contractName"` + InheritsContract string `json:"inheritsContract,omitempty"` + Author string `json:"author,omitempty"` + Version string `json:"version,omitempty"` + Description string `json:"description,omitempty"` + Declarations []Declaration `json:"declarations"` + IncomingMessages []IncomingMessage `json:"incomingMessages"` + IncomingExternal *IncomingExternal `json:"incomingExternal,omitempty"` + OutgoingMessages []OutgoingMessage `json:"outgoingMessages"` + EmittedMessages []OutgoingMessage `json:"emittedEvents"` + GetMethods []GetMethod `json:"getMethods"` + ThrownErrors []ThrownError `json:"thrownErrors"` + CompilerName string `json:"compilerName"` + CompilerVersion string `json:"compilerVersion"` + CodeBoc64 string `json:"codeBoc64"` + CodeHashes []string `json:"codeHashes,omitempty"` +} + +func (a *ABI) GetGolangNamespace() string { + return utils.ToCamelCase(a.Namespace) +} + +func (a *ABI) GetGolangContractName() string { + return a.GetGolangNamespace() + utils.ToCamelCase(a.ContractName) +} + +type Declaration struct { + SumType string `json:"kind"` + PayloadType *string `json:"payloadType,omitempty"` // todo: think abt naming + StructDeclaration StructDeclaration + AliasDeclaration AliasDeclaration + EnumDeclaration EnumDeclaration +} + +func (d *Declaration) UnmarshalJSON(b []byte) error { + var r struct { + Kind string `json:"kind"` + PayloadType *string `json:"payloadType,omitempty"` + } + if err := json.Unmarshal(b, &r); err != nil { + return err + } + + d.SumType = r.Kind + d.PayloadType = r.PayloadType + switch d.SumType { + case "Struct": + if err := json.Unmarshal(b, &d.StructDeclaration); err != nil { + return err + } + case "Alias": + if err := json.Unmarshal(b, &d.AliasDeclaration); err != nil { + return err + } + case "Enum": + if err := json.Unmarshal(b, &d.EnumDeclaration); err != nil { + return err + } + default: + return fmt.Errorf("unknown declaration type %q", d.SumType) + } + + return nil +} + +func (d Declaration) MarshalJSON() ([]byte, error) { + var kind Kind + kind.Kind = d.SumType + + var payload []byte + prefix, err := json.Marshal(kind) + if err != nil { + return nil, err + } + + switch d.SumType { + case "Struct": + payload, err = json.Marshal(d.StructDeclaration) + if err != nil { + return nil, err + } + return utils.ConcatPrefixAndSuffixIfExists(prefix, payload), nil + case "Alias": + payload, err = json.Marshal(d.AliasDeclaration) + if err != nil { + return nil, err + } + return utils.ConcatPrefixAndSuffixIfExists(prefix, payload), nil + case "Enum": + payload, err = json.Marshal(d.EnumDeclaration) + if err != nil { + return nil, err + } + return utils.ConcatPrefixAndSuffixIfExists(prefix, payload), nil + default: + return nil, fmt.Errorf("unknown declaration type %q", d.SumType) + } +} + +type StructDeclaration struct { + Name string `json:"name"` + TypeParams []string `json:"typeParams,omitempty"` + Prefix *Prefix `json:"prefix,omitempty"` + Fields []Field `json:"fields"` +} + +type Prefix struct { + PrefixStr string `json:"prefixStr"` + PrefixLen int `json:"prefixLen"` +} + +type Field struct { + Name string `json:"name"` + IsPayload *bool `json:"isPayload,omitempty"` + Ty Ty `json:"ty"` + DefaultValue *DefaultValue `json:"defaultValue,omitempty"` + Description string `json:"description,omitempty"` +} + +type DefaultValue struct { + SumType string `json:"kind"` + IntDefaultValue struct { + V string `json:"v"` + } + BoolDefaultValue struct { + V bool `json:"v"` + } + SliceDefaultValue struct { + Hex string `json:"hex"` + } + AddressDefaultValue struct { + Address string `json:"addr"` + } + TensorDefaultValue struct { + Items []DefaultValue `json:"items"` + } + NullDefaultValue struct{} +} + +func (d *DefaultValue) UnmarshalJSON(b []byte) error { + var kind Kind + + if err := json.Unmarshal(b, &kind); err != nil { + return err + } + + switch kind.Kind { + case "int": + d.SumType = "IntDefaultValue" + if err := json.Unmarshal(b, &d.IntDefaultValue); err != nil { + return err + } + case "bool": + d.SumType = "BoolDefaultValue" + if err := json.Unmarshal(b, &d.BoolDefaultValue); err != nil { + return err + } + case "slice": + d.SumType = "SliceDefaultValue" + if err := json.Unmarshal(b, &d.SliceDefaultValue); err != nil { + return err + } + case "address": + d.SumType = "AddressDefaultValue" + if err := json.Unmarshal(b, &d.AddressDefaultValue); err != nil { + return err + } + case "tensor": + d.SumType = "TensorDefaultValue" + if err := json.Unmarshal(b, &d.TensorDefaultValue); err != nil { + return err + } + case "null": + d.SumType = "NullDefaultValue" + default: + return fmt.Errorf("unknown default value type %q", d.SumType) + } + + return nil +} + +func (d *DefaultValue) MarshalJSON() ([]byte, error) { + var kind Kind + var payload []byte + var err error + + switch d.SumType { + case "IntDefaultValue": + kind.Kind = "int" + payload, err = json.Marshal(d.IntDefaultValue) + if err != nil { + return nil, err + } + case "BoolDefaultValue": + kind.Kind = "bool" + payload, err = json.Marshal(d.BoolDefaultValue) + if err != nil { + return nil, err + } + case "SliceDefaultValue": + kind.Kind = "slice" + payload, err = json.Marshal(d.SliceDefaultValue) + if err != nil { + return nil, err + } + case "AddressDefaultValue": + kind.Kind = "address" + payload, err = json.Marshal(d.AddressDefaultValue) + if err != nil { + return nil, err + } + case "TensorDefaultValue": + kind.Kind = "tensor" + payload, err = json.Marshal(d.TensorDefaultValue) + if err != nil { + return nil, err + } + case "NullDefaultValue": + kind.Kind = "null" + default: + return nil, fmt.Errorf("unknown default value type %q", d.SumType) + } + + prefix, err := json.Marshal(kind) + if err != nil { + return nil, err + } + return utils.ConcatPrefixAndSuffixIfExists(prefix, payload), nil +} + +type AliasDeclaration struct { + Name string `json:"name"` + TargetTy Ty `json:"targetTy"` + TypeParams []string `json:"typeParams,omitempty"` + CustomPackToBuilder bool `json:"customPackToBuilder,omitempty"` + CustomUnpackFromSlice bool `json:"customUnpackFromSlice,omitempty"` +} + +type EnumDeclaration struct { + Name string `json:"name"` + EncodedAs Ty `json:"encodedAs"` + Members []EnumMember `json:"members"` +} + +type EnumMember struct { + Name string `json:"name"` + Value string `json:"value"` +} + +type Ty struct { + SumType string `json:"kind"` + Int *Int + IntN *IntN + UintN *UintN + VarIntN *VarIntN + VarUintN *VarUintN + BitsN *BitsN + Coins *Coins + Bool *Bool + Cell *Cell + Slice *Slice + Builder *Builder + Callable *Callable + Remaining *Remaining + Address *Address + AddressOpt *AddressOpt + AddressExt *AddressExt + AddressAny *AddressAny + Nullable *Nullable + CellOf *CellOf + Tensor *Tensor + TupleWith *TupleWith + Map *Map + EnumRef *EnumRef + AliasRef *AliasRef + StructRef *StructRef + Generic *Generic + Union *Union + TupleAny *TupleAny + NullLiteral *NullLiteral + Void *Void +} + +type Int struct{} + +type IntN struct { + N int `json:"n"` +} + +type UintN struct { + N int `json:"n"` +} + +type VarIntN struct { + N int `json:"n"` +} + +type VarUintN struct { + N int `json:"n"` +} + +type BitsN struct { + N int `json:"n"` +} + +type Coins struct { +} + +type Bool struct{} + +type Address struct{} + +type AddressOpt struct { +} + +type AddressExt struct{} + +type AddressAny struct{} + +type Cell struct{} + +type Slice struct{} + +type Builder struct{} + +type Callable struct{} + +type Remaining struct{} + +type Nullable struct { + Inner Ty `json:"inner"` +} + +type CellOf struct { + Inner Ty `json:"inner"` +} + +type Map struct { + K Ty `json:"k"` + V Ty `json:"v"` +} + +type NullLiteral struct{} + +type Void struct{} + +type EnumRef struct { + EnumName string `json:"enumName"` +} + +type StructRef struct { + StructName string `json:"structName"` + TypeArgs []Ty `json:"typeArgs,omitempty"` +} + +type AliasRef struct { + AliasName string `json:"aliasName"` + TypeArgs []Ty `json:"typeArgs,omitempty"` +} + +type Generic struct { + NameT string `json:"nameT"` +} + +type Tensor struct { + Items []Ty `json:"items"` +} + +type TupleWith struct { + Items []Ty `json:"items"` +} + +type TupleAny struct{} + +type Union struct { + Variants []UnionVariant `json:"variants"` +} + +func (t *Ty) UnmarshalJSON(b []byte) error { + var kind Kind + if err := json.Unmarshal(b, &kind); err != nil { + return err + } + switch kind.Kind { + case "intN": + t.SumType = "IntN" + if err := json.Unmarshal(b, &t.IntN); err != nil { + return err + } + case "uintN": + t.SumType = "UintN" + if err := json.Unmarshal(b, &t.UintN); err != nil { + return err + } + case "varintN": + t.SumType = "VarIntN" + if err := json.Unmarshal(b, &t.VarIntN); err != nil { + return err + } + case "varuintN": + t.SumType = "VarUintN" + if err := json.Unmarshal(b, &t.VarUintN); err != nil { + return err + } + case "bitsN": + t.SumType = "BitsN" + if err := json.Unmarshal(b, &t.BitsN); err != nil { + return err + } + case "nullable": + t.SumType = "Nullable" + if err := json.Unmarshal(b, &t.Nullable); err != nil { + return err + } + case "cellOf": + t.SumType = "CellOf" + if err := json.Unmarshal(b, &t.CellOf); err != nil { + return err + } + case "tensor": + t.SumType = "Tensor" + if err := json.Unmarshal(b, &t.Tensor); err != nil { + return err + } + case "tupleWith": + t.SumType = "TupleWith" + if err := json.Unmarshal(b, &t.TupleWith); err != nil { + return err + } + case "mapKV": + t.SumType = "Map" + if err := json.Unmarshal(b, &t.Map); err != nil { + return err + } + case "EnumRef": + t.SumType = "EnumRef" + if err := json.Unmarshal(b, &t.EnumRef); err != nil { + return err + } + case "StructRef": + t.SumType = "StructRef" + if err := json.Unmarshal(b, &t.StructRef); err != nil { + return err + } + case "AliasRef": + t.SumType = "AliasRef" + if err := json.Unmarshal(b, &t.AliasRef); err != nil { + return err + } + case "genericT": + t.SumType = "Generic" + if err := json.Unmarshal(b, &t.Generic); err != nil { + return err + } + case "union": + t.SumType = "Union" + if err := json.Unmarshal(b, &t.Union); err != nil { + return err + } + case "int": + t.SumType = "Int" + t.IntN = &IntN{} + case "coins": + t.SumType = "Coins" + t.Coins = &Coins{} + case "bool": + t.SumType = "Bool" + t.Bool = &Bool{} + case "cell": + t.SumType = "Cell" + t.Cell = &Cell{} + case "slice": + t.SumType = "Slice" + t.Slice = &Slice{} + case "builder": + t.SumType = "Builder" + t.Builder = &Builder{} + case "remaining": + t.SumType = "Remaining" + t.Remaining = &Remaining{} + case "address": + t.SumType = "Address" + t.Address = &Address{} + case "addressOpt": + t.SumType = "AddressOpt" + t.AddressOpt = &AddressOpt{} + case "addressExt": + t.SumType = "AddressExt" + t.AddressExt = &AddressExt{} + case "addressAny": + t.SumType = "AddressAny" + t.AddressAny = &AddressAny{} + case "tupleAny": + t.SumType = "TupleAny" + t.TupleAny = &TupleAny{} + case "nullLiteral": + t.SumType = "NullLiteral" + t.NullLiteral = &NullLiteral{} + case "callable": + t.SumType = "Callable" + t.Callable = &Callable{} + case "void": + t.SumType = "Void" + t.Void = &Void{} + default: + return fmt.Errorf("unknown ty type %q", kind.Kind) + } + + return nil +} + +func (t *Ty) MarshalJSON() ([]byte, error) { + var kind Kind + var prefix []byte + var payload []byte + var err error + + switch t.SumType { + case "IntN": + kind.Kind = "intN" + payload, err = json.Marshal(t.IntN) + if err != nil { + return nil, err + } + case "UintN": + kind.Kind = "uintN" + payload, err = json.Marshal(t.UintN) + if err != nil { + return nil, err + } + case "VarIntN": + kind.Kind = "varintN" + payload, err = json.Marshal(t.VarIntN) + if err != nil { + return nil, err + } + case "VarUintN": + kind.Kind = "varuintN" + payload, err = json.Marshal(t.VarUintN) + if err != nil { + return nil, err + } + case "BitsN": + kind.Kind = "bitsN" + payload, err = json.Marshal(t.BitsN) + if err != nil { + return nil, err + } + case "Nullable": + kind.Kind = "nullable" + payload, err = json.Marshal(t.Nullable) + if err != nil { + return nil, err + } + case "CellOf": + kind.Kind = "cellOf" + payload, err = json.Marshal(t.CellOf) + if err != nil { + return nil, err + } + case "Tensor": + kind.Kind = "tensor" + payload, err = json.Marshal(t.Tensor) + if err != nil { + return nil, err + } + case "TupleWith": + kind.Kind = "tupleWith" + payload, err = json.Marshal(t.TupleWith) + if err != nil { + return nil, err + } + case "Map": + kind.Kind = "mapKV" + payload, err = json.Marshal(t.Map) + if err != nil { + return nil, err + } + case "EnumRef": + kind.Kind = "EnumRef" + payload, err = json.Marshal(t.EnumRef) + if err != nil { + return nil, err + } + case "StructRef": + kind.Kind = "StructRef" + payload, err = json.Marshal(t.StructRef) + if err != nil { + return nil, err + } + case "AliasRef": + kind.Kind = "AliasRef" + payload, err = json.Marshal(t.AliasRef) + if err != nil { + return nil, err + } + case "Generic": + kind.Kind = "genericT" + payload, err = json.Marshal(t.Generic) + if err != nil { + return nil, err + } + case "Union": + kind.Kind = "union" + payload, err = json.Marshal(t.Union) + if err != nil { + return nil, err + } + case "Int": + kind.Kind = "int" + case "Coins": + kind.Kind = "coins" + case "Bool": + kind.Kind = "bool" + case "Cell": + kind.Kind = "cell" + case "Slice": + kind.Kind = "slice" + case "Builder": + kind.Kind = "builder" + case "Remaining": + kind.Kind = "remaining" + case "Address": + kind.Kind = "address" + case "AddressOpt": + kind.Kind = "addressOpt" + case "AddressExt": + kind.Kind = "addressExt" + case "AddressAny": + kind.Kind = "addressAny" + case "TupleAny": + kind.Kind = "tupleAny" + case "NullLiteral": + kind.Kind = "nullLiteral" + case "Callable": + kind.Kind = "callable" + case "Void": + kind.Kind = "void" + default: + return nil, fmt.Errorf("unknown ty type %q", t.SumType) + } + + prefix, err = json.Marshal(kind) + if err != nil { + return nil, err + } + return utils.ConcatPrefixAndSuffixIfExists(prefix, payload), nil +} + +func (t *Ty) GetFixedSize() (int, bool) { + switch t.SumType { + case "IntN": + return t.IntN.N, true + case "UintN": + return t.UintN.N, true + case "BitsN": + return t.BitsN.N, true + case "Bool": + return 1, true + case "Address": + return 267, true + default: + return 0, false + } +} + +type UnionVariant struct { + PrefixStr string `json:"prefixStr"` + PrefixLen int `json:"prefixLen"` + PrefixEatInPlace bool `json:"prefixEatInPlace,omitempty"` + VariantTy Ty `json:"variantTy"` +} + +type IncomingMessage struct { + BodyTy Ty `json:"bodyTy"` + MinimalMsgValue *big.Int `json:"minimalMsgValue,omitempty"` + Description string `json:"description,omitempty"` + PreferredSendMode int16 `json:"preferredSendMode,omitempty"` +} + +func (m *IncomingMessage) GetMsgName() (string, error) { + return getMsgName(m.BodyTy) +} + +type IncomingExternal struct { + BodyTy Ty `json:"bodyTy"` + Description string `json:"description,omitempty"` +} + +func (m *IncomingExternal) GetMsgName() (string, error) { + return getMsgName(m.BodyTy) +} + +type OutgoingMessage struct { + BodyTy Ty `json:"bodyTy"` + Description string `json:"description,omitempty"` +} + +func (m *OutgoingMessage) GetMsgName() (string, error) { + return getMsgName(m.BodyTy) +} + +func getMsgName(ty Ty) (string, error) { + switch ty.SumType { + case "StructRef": + return ty.StructRef.StructName, nil + case "AliasRef": + return ty.AliasRef.AliasName, nil + default: + return "", fmt.Errorf("cannot get name for %q body", ty.SumType) + } +} + +type GetMethod struct { + TvmMethodID int `json:"tvmMethodId"` + Name string `json:"name"` + Parameters []Parameter `json:"parameters"` + ReturnTy Ty `json:"returnTy"` + Description string `json:"description,omitempty"` +} + +func (g GetMethod) GolangFunctionName() string { + return utils.ToCamelCase(g.Name) +} + +func (g GetMethod) FullResultName(contractName string) string { + res := "" + if contractName != "" { + res = contractName + "_" + } + res += utils.ToCamelCase(g.Name) + + return res + "Result" +} + +func (g GetMethod) UsedByIntrospection() bool { + return len(g.Parameters) == 0 +} + +type Parameter struct { + Name string `json:"name"` + Ty Ty `json:"ty"` +} + +type ThrownError struct { + Name string `json:"constName"` + ErrCode int `json:"errCode"` +} diff --git a/tolk/refs.go b/tolk/refs.go new file mode 100644 index 00000000..0897a8dd --- /dev/null +++ b/tolk/refs.go @@ -0,0 +1,537 @@ +package tolk + +import ( + "encoding/json" + "fmt" + "math/big" + "slices" + "strings" + + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tolk/parser" + "golang.org/x/exp/maps" +) + +type Prefix struct { + Len int16 `json:"len"` + Prefix uint64 `json:"prefix"` +} + +type Struct struct { + hasPrefix bool + name string + prefix Prefix + fieldNames []string + fieldValues []Value +} + +func (s *Struct) Unmarshal(cell *boc.Cell, ty parser.StructRef, decoder *Decoder) error { + if decoder.abiRefs.structRefs == nil { + return fmt.Errorf("struct has struct reference but no abi has been given") + } + strct, found := decoder.abiRefs.structRefs[ty.StructName] + if !found { + return fmt.Errorf("struct with name %s was not found in given abi", ty.StructName) + } + tolkStruct := Struct{ + fieldNames: make([]string, 0), + fieldValues: make([]Value, 0), + name: ty.StructName, + } + if strct.Prefix != nil { + prefixLen := strct.Prefix.PrefixLen + if prefixLen > 64 { + return fmt.Errorf("struct %v prefix length must be lower than 64", strct.Name) + } + + prefix, err := cell.ReadUint(prefixLen) + if err != nil { + return fmt.Errorf("failed to read struct's %v-bit length prefix: %w", prefixLen, err) + } + actualPrefix, err := binHexToUint64(strct.Prefix.PrefixStr) + if err != nil { + return fmt.Errorf("failed to parse struct's prefix %v to integer: %w", prefix, err) + } + + if prefix != actualPrefix { + return fmt.Errorf("struct %v prefix does not match actual prefix %v", strct.Name, actualPrefix) + } + tolkStruct.hasPrefix = true + tolkStruct.prefix = Prefix{ + Len: int16(prefixLen), + Prefix: prefix, + } + } + + oldGenericMap := decoder.abiRefs.genericRefs + genericMap, err := resolveGeneric(ty.TypeArgs, strct.TypeParams, &decoder.abiRefs) + if err != nil { + return fmt.Errorf("failed to resolve struct's generic value: %w", err) + } + decoder.abiRefs.genericRefs = genericMap + + for _, field := range strct.Fields { + fieldVal := Value{} + + isPayloadResolved := false + if field.IsPayload != nil && *field.IsPayload { + fieldVal, isPayloadResolved, err = s.resolvePayload(cell, field.Ty, decoder) + if err != nil { + return fmt.Errorf("failed to resolve payload for field %v: %w", field.Name, err) + } + } + if !isPayloadResolved { + err = fieldVal.Unmarshal(cell, field.Ty, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal struct's field %s: %w", field.Name, err) + } + } + + tolkStruct.fieldNames = append(tolkStruct.fieldNames, field.Name) + tolkStruct.fieldValues = append(tolkStruct.fieldValues, fieldVal) + } + + decoder.abiRefs.genericRefs = oldGenericMap + *s = tolkStruct + + return nil +} + +// try to resolve payload from all known abi in trace +func (s *Struct) resolvePayload(cell *boc.Cell, ty parser.Ty, decoder *Decoder) (Value, bool, error) { + switch ty.SumType { + case "Remaining": + isRef, err := cell.ReadBit() + if err != nil { + return Value{}, false, fmt.Errorf("failed to read isRef prefix: %w", err) + } + payload := cell.CopyRemaining() + if isRef { + payload, err = payload.NextRef() + if err != nil { + return Value{}, false, fmt.Errorf("failed to read payload ref: %w", err) + } + } + v, isResolved, err := decoder.resolvePayload(payload) + if err != nil { + return Value{}, false, fmt.Errorf("failed to resolve payload: %w", err) + } + if isResolved { + return v, true, nil + } + case "Cell": + payload, err := cell.NextRef() + if err != nil { + return Value{}, false, fmt.Errorf("failed to read payload ref: %w", err) + } + payload = payload.CopyRemaining() + v, isResolved, err := decoder.resolvePayload(payload) + if err != nil { + return Value{}, false, fmt.Errorf("failed to resolve payload: %w", err) + } + if isResolved { + return v, true, nil + } + } + + return Value{}, false, nil +} + +func (s *Struct) Marshal(cell *boc.Cell, ty parser.StructRef, encoder *Encoder) error { + if encoder.abiRefs.structRefs == nil { + return fmt.Errorf("struct has struct reference but no abi has been given") + } + strct, found := encoder.abiRefs.structRefs[ty.StructName] + if !found { + return fmt.Errorf("struct with name %s was not found in given abi", ty.StructName) + } + + if strct.Prefix != nil { + actualPrefix, err := binHexToUint64(strct.Prefix.PrefixStr) + if err != nil { + return fmt.Errorf("failed to parse struct's prefix %v to integer: %w", strct.Prefix.PrefixStr, err) + } + + if s.prefix.Prefix != actualPrefix { + return fmt.Errorf("struct %v prefix does not match actual prefix %v", strct.Name, actualPrefix) + } + + err = cell.WriteUint(s.prefix.Prefix, int(s.prefix.Len)) + if err != nil { + return fmt.Errorf("failed to write struct's %v-bit prefix %v: %w", s.prefix.Len, s.prefix.Prefix, err) + } + } + + oldGenericMap := encoder.abiRefs.genericRefs + genericMap, err := resolveGeneric(ty.TypeArgs, strct.TypeParams, &encoder.abiRefs) + if err != nil { + return fmt.Errorf("failed to resolve struct's generic value: %w", err) + } + encoder.abiRefs.genericRefs = genericMap + + for _, field := range strct.Fields { + val, ok := s.GetField(field.Name) + if !ok { + return fmt.Errorf("struct %v has no field %v", strct.Name, field.Name) + } + + err = val.Marshal(cell, field.Ty, encoder) + if err != nil { + return fmt.Errorf("failed to marshal struct's field %s: %w", field.Name, err) + } + } + + encoder.abiRefs.genericRefs = oldGenericMap + + return nil +} + +func (s *Struct) GetField(field string) (Value, bool) { + for i, name := range s.fieldNames { + if name == field { + return s.fieldValues[i], true + } + } + return Value{}, false +} + +func (s *Struct) MustGetField(field string) Value { + for i, name := range s.fieldNames { + if name == field { + return s.fieldValues[i] + } + } + panic("field with name " + field + " is not found") +} + +func (s *Struct) SetField(field string, v Value) bool { + for i, name := range s.fieldNames { + if name == field { + s.fieldValues[i] = v + return true + } + } + return false +} + +func (s *Struct) RemoveField(field string) { + for i, name := range s.fieldNames { + if name == field { + s.fieldNames = append(s.fieldNames[:i], s.fieldNames[i+1:]...) + s.fieldValues = append(s.fieldValues[:i], s.fieldValues[i+1:]...) + } + } +} + +func (s *Struct) GetPrefix() (Prefix, bool) { + if !s.hasPrefix { + return Prefix{}, false + } + + return s.prefix, true +} + +func (s *Struct) Equal(o any) bool { + otherStruct, ok := o.(Struct) + if !ok { + return false + } + if s.hasPrefix != otherStruct.hasPrefix { + return false + } + if s.hasPrefix { + if s.prefix != otherStruct.prefix { + return false + } + } + if !slices.Equal(s.fieldNames, otherStruct.fieldNames) { + return false + } + for i, value := range s.fieldValues { + if !value.Equal(otherStruct.fieldValues[i]) { + return false + } + } + return true +} + +func (s Struct) MarshalJSON() ([]byte, error) { + builder := strings.Builder{} + builder.WriteRune('{') + for i, name := range s.fieldNames { + if i != 0 { + builder.WriteRune(',') + } + builder.WriteString(fmt.Sprintf("\"%s\":", name)) + val, err := json.Marshal(&s.fieldValues[i]) + if err != nil { + return nil, fmt.Errorf("failed to marshal struct's field %s: %w", name, err) + } + builder.Write(val) + } + builder.WriteRune('}') + return []byte(builder.String()), nil +} + +type EnumValue struct { + ActualValue Value + Name string + Value big.Int +} + +func (e *EnumValue) Unmarshal(cell *boc.Cell, ty parser.EnumRef, decoder *Decoder) error { + if decoder.abiRefs.enumRefs == nil { + return fmt.Errorf("struct has enum reference but no abi has been given") + } + enum, found := decoder.abiRefs.enumRefs[ty.EnumName] + if !found { + return fmt.Errorf("enum with name %s was not found in given abi", ty.EnumName) + } + + enumVal := Value{} + err := enumVal.Unmarshal(cell, enum.EncodedAs, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal enum's value: %w", err) + } + var bigEnumVal big.Int + switch enum.EncodedAs.SumType { + case "IntN": + if enum.EncodedAs.IntN.N > 64 { + bigEnumVal = big.Int(*enumVal.BigInt) + } else { + bigEnumVal = *big.NewInt(int64(*enumVal.SmallInt)) + } + case "UintN": + if enum.EncodedAs.UintN.N > 64 { + bigEnumVal = big.Int(*enumVal.BigUint) + } else { + bigEnumVal = *new(big.Int).SetUint64(uint64(*enumVal.SmallUint)) + } + case "VarIntN": + bigEnumVal = big.Int(*enumVal.VarInt) + case "VarUintN": + bigEnumVal = big.Int(*enumVal.VarUint) + default: + return fmt.Errorf("enum encode type must be integer, got: %s", enum.EncodedAs.SumType) + } + + for _, member := range enum.Members { + val, ok := new(big.Int).SetString(member.Value, 10) + if !ok { + return fmt.Errorf("invalid enum %v value %v for member %s", ty.EnumName, member.Value, member.Name) + } + + if val.Cmp(&bigEnumVal) == 0 { + *e = EnumValue{ + ActualValue: enumVal, + Name: member.Name, + Value: *val, + } + + return nil + } + } + + return fmt.Errorf("enum value didn't match any values") +} + +func (e EnumValue) MarshalJSON() ([]byte, error) { + data, err := json.Marshal(e.Name) + if err != nil { + return nil, fmt.Errorf("failed to marshal enum's value: %w", err) + } + return data, nil +} + +func (e *EnumValue) Marshal(cell *boc.Cell, ty parser.EnumRef, encoder *Encoder) error { + if encoder.abiRefs.enumRefs == nil { + return fmt.Errorf("struct has enum reference but no abi has been given") + } + enum, found := encoder.abiRefs.enumRefs[ty.EnumName] + if !found { + return fmt.Errorf("enum with name %s was not found in given abi", ty.EnumName) + } + + err := e.ActualValue.Marshal(cell, enum.EncodedAs, encoder) + if err != nil { + return fmt.Errorf("failed to marshal enum's value: %w", err) + } + + for _, member := range enum.Members { + val, ok := new(big.Int).SetString(member.Value, 10) + if !ok { + return fmt.Errorf("invalid enum %v value %v for member %s", ty.EnumName, member.Value, member.Name) + } + + if val.Cmp(&e.Value) == 0 { + return nil + } + } + return fmt.Errorf("enum value not matched, got: %s", e.Value.String()) +} + +func (e *EnumValue) Equal(other any) bool { + otherEnumValue, ok := other.(EnumValue) + if !ok { + return false + } + if e.Name != otherEnumValue.Name { + return false + } + if e.Value.Cmp(&otherEnumValue.Value) != 0 { + return false + } + return e.ActualValue.Equal(otherEnumValue.ActualValue) +} + +type AliasValue Value + +func (a *AliasValue) Equal(other any) bool { + otherAlias, ok := other.(AliasValue) + if !ok { + return false + } + v := Value(*a) + return v.Equal(Value(otherAlias)) +} + +func (a *AliasValue) Unmarshal(cell *boc.Cell, ty parser.AliasRef, decoder *Decoder) error { + if decoder.abiRefs.aliasRefs == nil { + return fmt.Errorf("struct has alias reference but no abi has been given") + } + alias, found := decoder.abiRefs.aliasRefs[ty.AliasName] + if !found { + return fmt.Errorf("alias with name %s was not found in given abi", ty.AliasName) + } + + if alias.CustomUnpackFromSlice { + if decoder.customUnpackResolver == nil { + return fmt.Errorf("custom unmarshal alias %v with custom unpack method", ty.AliasName) + } + if err := decoder.customUnpackResolver(ty, cell, a); err != nil { + return fmt.Errorf("failed to unmarshal alias with custom unpack: %w", err) + } + return nil + } + + oldGenericMap := decoder.abiRefs.genericRefs + genericMap, err := resolveGeneric(ty.TypeArgs, alias.TypeParams, &decoder.abiRefs) + if err != nil { + return fmt.Errorf("failed to resolve alias' generic value: %w", err) + } + decoder.abiRefs.genericRefs = genericMap + + val := Value{} + err = val.Unmarshal(cell, alias.TargetTy, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal alias value: %w", err) + } + + decoder.abiRefs.genericRefs = oldGenericMap + *a = AliasValue(val) + + return nil +} + +func (a *AliasValue) Marshal(cell *boc.Cell, ty parser.AliasRef, encoder *Encoder) error { + if encoder.abiRefs.aliasRefs == nil { + return fmt.Errorf("struct has alias reference but no abi has been given") + } + alias, found := encoder.abiRefs.aliasRefs[ty.AliasName] + if !found { + return fmt.Errorf("alias with name %s was not found in given abi", ty.AliasName) + } + + if alias.CustomPackToBuilder { + if encoder.customPackResolver == nil { + return fmt.Errorf("custom marshal alias %v with custom pack method", ty.AliasName) + } + if err := encoder.customPackResolver(ty, cell, a); err != nil { + return fmt.Errorf("failed to marshal alias with custom pack: %w", err) + } + return nil + } + + oldGenericMap := encoder.abiRefs.genericRefs + genericMap, err := resolveGeneric(ty.TypeArgs, alias.TypeParams, &encoder.abiRefs) + if err != nil { + return fmt.Errorf("failed to resolve alias' generic value: %w", err) + } + encoder.abiRefs.genericRefs = genericMap + + val := Value(*a) + err = val.Marshal(cell, alias.TargetTy, encoder) + if err != nil { + return fmt.Errorf("failed to marshal alias value: %w", err) + } + + encoder.abiRefs.genericRefs = oldGenericMap + + return nil +} + +type GenericValue Value + +func (g *GenericValue) Equal(other any) bool { + otherGeneric, ok := other.(GenericValue) + if !ok { + return false + } + v := Value(*g) + return v.Equal(Value(otherGeneric)) +} + +func (g *GenericValue) Unmarshal(cell *boc.Cell, ty parser.Generic, decoder *Decoder) error { + currentTy, found := decoder.abiRefs.genericRefs[ty.NameT] + if !found { + return fmt.Errorf("cannot resolve generic's type %v ", ty.NameT) + } + val := Value{} + err := val.Unmarshal(cell, currentTy, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal generic's value: %w", err) + } + + *g = GenericValue(val) + + return nil +} + +func (g *GenericValue) Marshal(cell *boc.Cell, ty parser.Generic, encoder *Encoder) error { + currentTy, found := encoder.abiRefs.genericRefs[ty.NameT] + if !found { + return fmt.Errorf("cannot resolve generic's type %v ", ty.NameT) + } + val := Value(*g) + err := val.Marshal(cell, currentTy, encoder) + if err != nil { + return fmt.Errorf("failed to marshal generic's value: %w", err) + } + + return nil +} + +func resolveGeneric(typeArgs []parser.Ty, typeParams []string, abiRefs *abiRefs) (map[string]parser.Ty, error) { + genericMap := make(map[string]parser.Ty) + if abiRefs.genericRefs != nil { + maps.Copy(genericMap, abiRefs.genericRefs) + } + + for i, genericTy := range typeArgs { + genericMap[typeParams[i]] = genericTy + + if genericTy.SumType == "Generic" { + if abiRefs.genericRefs == nil { + return nil, fmt.Errorf("cannot resolve generic's type %v", genericTy.Generic.NameT) + } + + ty, found := abiRefs.genericRefs[genericTy.Generic.NameT] + if !found { + return nil, fmt.Errorf("generic's type %v not found", genericTy.Generic.NameT) + } + genericMap[typeParams[i]] = ty + } + } + + return genericMap, nil +} diff --git a/tolk/runtime.go b/tolk/runtime.go new file mode 100644 index 00000000..5c0cdd55 --- /dev/null +++ b/tolk/runtime.go @@ -0,0 +1,169 @@ +package tolk + +import ( + "fmt" + + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tolk/parser" +) + +func Unmarshal(cell *boc.Cell, ty parser.Ty) (*Value, error) { + d := NewDecoder() + return d.Unmarshal(cell, ty) +} + +func Marshal(v *Value, ty parser.Ty) (*boc.Cell, error) { + e := NewEncoder() + return e.Marshal(v, ty) +} + +type abiRefs struct { + structRefs map[string]parser.StructDeclaration + aliasRefs map[string]parser.AliasDeclaration + enumRefs map[string]parser.EnumDeclaration + genericRefs map[string]parser.Ty + opcodeRefs map[uint64][]parser.StructDeclaration +} + +type customUnpackResolver = func(parser.AliasRef, *boc.Cell, *AliasValue) error + +type Decoder struct { + abiRefs abiRefs + customUnpackResolver customUnpackResolver +} + +func NewDecoder() *Decoder { + return &Decoder{} +} + +func (d *Decoder) WithABIs(abis ...parser.ABI) error { + d.abiRefs = abiRefs{ + structRefs: make(map[string]parser.StructDeclaration), + aliasRefs: make(map[string]parser.AliasDeclaration), + enumRefs: make(map[string]parser.EnumDeclaration), + genericRefs: make(map[string]parser.Ty), + opcodeRefs: make(map[uint64][]parser.StructDeclaration), + } + for _, abi := range abis { + for _, declr := range abi.Declarations { + switch declr.SumType { + case "Struct": + d.abiRefs.structRefs[declr.StructDeclaration.Name] = declr.StructDeclaration + if declr.StructDeclaration.Prefix != nil { + prefix, err := binHexToUint64(declr.StructDeclaration.Prefix.PrefixStr) + if err != nil { + return fmt.Errorf("failed to parse prefix struct %v prefix: %w", declr.StructDeclaration.Name, err) + } + d.abiRefs.opcodeRefs[prefix] = append(d.abiRefs.opcodeRefs[prefix], declr.StructDeclaration) + } + case "Alias": + d.abiRefs.aliasRefs[declr.AliasDeclaration.Name] = declr.AliasDeclaration + case "Enum": + d.abiRefs.enumRefs[declr.EnumDeclaration.Name] = declr.EnumDeclaration + } + } + } + return nil +} + +func (d *Decoder) WithCustomUnpackResolver(customUnpackResolver customUnpackResolver) { + d.customUnpackResolver = customUnpackResolver +} + +func (d *Decoder) Unmarshal(cell *boc.Cell, ty parser.Ty) (*Value, error) { + res := &Value{} + err := res.Unmarshal(cell, ty, d) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal tolk value: %w", err) + } + return res, nil +} + +func (d *Decoder) UnmarshalMessage(cell *boc.Cell) (*Value, error) { + res, isResolved, err := d.resolvePayload(cell) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal tolk value: %w", err) + } + if isResolved { + return &res, nil + } + res = Value{ + SumType: "Remaining", + Remaining: (*RemainingValue)(cell), + } + return &res, nil +} + +func (d *Decoder) resolvePayload(payload *boc.Cell) (Value, bool, error) { + payloadOpcode, err := payload.ReadUint(32) // payload always 32 bit length + if err != nil { + return Value{}, false, fmt.Errorf("failed to read payload's opcode: %w", err) + } + payload.ResetCounters() // reset opcode + + guessedStructs := d.abiRefs.opcodeRefs[payloadOpcode] + for _, strct := range guessedStructs { + v, err := d.Unmarshal(payload, parser.NewStructType(strct.Name)) + if err != nil { + continue + } + return *v, true, nil + } + + // todo: maybe try every known struct to unmarshal to? + return Value{}, false, nil +} + +type customPackResolver = func(parser.AliasRef, *boc.Cell, *AliasValue) error + +type Encoder struct { + abiRefs abiRefs + customPackResolver customPackResolver +} + +func NewEncoder() *Encoder { + return &Encoder{} +} + +func (e *Encoder) WithABIs(abis ...parser.ABI) error { + e.abiRefs = abiRefs{ + structRefs: make(map[string]parser.StructDeclaration), + aliasRefs: make(map[string]parser.AliasDeclaration), + enumRefs: make(map[string]parser.EnumDeclaration), + genericRefs: make(map[string]parser.Ty), + opcodeRefs: make(map[uint64][]parser.StructDeclaration), + } + for _, abi := range abis { + for _, declr := range abi.Declarations { + switch declr.SumType { + case "Struct": + e.abiRefs.structRefs[declr.StructDeclaration.Name] = declr.StructDeclaration + if declr.StructDeclaration.Prefix != nil { + prefix, err := binHexToUint64(declr.StructDeclaration.Prefix.PrefixStr) + if err != nil { + return fmt.Errorf("failed to parse prefix struct %v prefix: %w", declr.StructDeclaration.Name, err) + } + e.abiRefs.opcodeRefs[prefix] = append(e.abiRefs.opcodeRefs[prefix], declr.StructDeclaration) + } + case "Alias": + e.abiRefs.aliasRefs[declr.AliasDeclaration.Name] = declr.AliasDeclaration + case "Enum": + e.abiRefs.enumRefs[declr.EnumDeclaration.Name] = declr.EnumDeclaration + } + } + } + return nil +} + +func (e *Encoder) WithCustomPackResolver(customPackResolver customPackResolver) { + e.customPackResolver = customPackResolver +} + +func (e *Encoder) Marshal(v *Value, ty parser.Ty) (*boc.Cell, error) { + cell := boc.NewCell() + err := v.Marshal(cell, ty, e) + if err != nil { + return nil, fmt.Errorf("failed to marshal tolk value: %w", err) + } + return cell, nil +} diff --git a/tolk/runtime_benchmark_test.go b/tolk/runtime_benchmark_test.go new file mode 100644 index 00000000..e3b2e8f1 --- /dev/null +++ b/tolk/runtime_benchmark_test.go @@ -0,0 +1,368 @@ +package tolk + +import ( + "encoding/json" + "fmt" + "os" + "testing" + + "github.com/tonkeeper/tongo/abi" + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tlb" + "github.com/tonkeeper/tongo/tolk/parser" +) + +func BenchmarkRuntimeUnmarshalling(b *testing.B) { + type Case struct { + name string + ty parser.Ty + cell boc.Cell + abiFiles []string + customUnpackResolver func(decoder *Decoder) func(alias parser.AliasRef, cell *boc.Cell, value *AliasValue) error + } + for _, curr := range []Case{ + { + name: "unmarshal small int", + ty: parser.NewIntNType(24), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c72410101010005000006ff76c41616db06"), + }, + { + name: "unmarshal big int", + ty: parser.NewIntNType(183), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010101001900002dfffffffffffffffffffffffffffffffffff99bfeac6423a6f0b50c"), + }, + { + name: "unmarshal small uint", + ty: parser.NewUIntNType(53), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010101000900000d00000000001d34e435eafd"), + }, + { + name: "unmarshal big uint", + ty: parser.NewUIntNType(257), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010101002300004100000000000000000000000000000000000000000000000000009fc4212a38ba40b11cce12"), + }, + { + name: "unmarshal var int 16", + ty: parser.NewVarInt16Type(), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010101000600000730c98588449b6923"), + }, + { + name: "unmarshal var uint 32", + ty: parser.NewVarUInt32Type(), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010101000800000b28119ab36b44d3a86c0f"), + }, + { + name: "unmarshal bits24", + ty: parser.NewBitsNType(24), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010101000500000631323318854035"), + }, + { + name: "unmarshal coins", + ty: parser.NewCoinsType(), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c72410101010007000009436ec6e0189ebbd7f4"), + }, + { + name: "unmarshal bool", + ty: parser.NewBoolType(), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010101000300000140f6d24034"), + }, + { + name: "unmarshal cell", + ty: parser.NewCellType(), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c724101020100090001000100080000007ba52a3292"), + }, + { + name: "unmarshal remaining", + ty: parser.NewRemainingType(), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010101000900000dc0800000000ab8d04726e4"), + }, + { + name: "unmarshal internal address", + ty: parser.NewAddressType(), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010101002400004380107bfaaa5cc6e5368e5f9799188bd798cd22e04ab16d1d8ea4fc37480741e6351064a3e1a6"), + }, + { + name: "unmarshal not exists optional address", + ty: parser.NewAddressOptType(), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c724101010100030000012094418655"), + }, + { + name: "unmarshal exists optional address", + ty: parser.NewAddressOptType(), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010101002400004380107bfaaa5cc6e5368e5f9799188bd798cd22e04ab16d1d8ea4fc37480741e6351064a3e1a6"), + }, + { + name: "unmarshal external address", + ty: parser.NewAddressExtType(), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010101000600000742082850fcbd94fd"), + }, + { + name: "unmarshal none address any", + ty: parser.NewAddressAnyType(), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c724101010100030000012094418655"), + }, + { + name: "unmarshal internal address any", + ty: parser.NewAddressAnyType(), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010101002400004380107bfaaa5cc6e5368e5f9799188bd798cd22e04ab16d1d8ea4fc37480741e6351064a3e1a6"), + }, + { + name: "unmarshal external address any", + ty: parser.NewAddressAnyType(), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010101000600000742082850fcbd94fd"), + }, + { + name: "unmarshal var address any", + ty: parser.NewAddressAnyType(), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010101000900000dc0800000000ab8d04726e4"), + }, + { + name: "unmarshal not exists nullable", + ty: parser.NewNullableType(parser.NewRemainingType()), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010101000300000140f6d24034"), + }, + { + name: "unmarshal exists nullable", + ty: parser.NewNullableType(parser.NewCellType()), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010201000b000101c001000900000c0ae007880db9"), + }, + { + name: "unmarshal ref", + ty: parser.NewCellOfType(parser.NewIntNType(65)), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010201000e000100010011000000000009689e40e150b4c5"), + }, + { + name: "unmarshal empty tensor", + ty: parser.NewTensorType(), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c724101010100020000004cacb9cd"), + }, + { + name: "unmarshal not empty tensor", + ty: parser.NewTensorType( + parser.NewUIntNType(123), + parser.NewBoolType(), + parser.NewCoinsType(), + parser.NewTensorType( + parser.NewIntNType(23), + parser.NewNullableType(parser.NewIntNType(2)), + ), + parser.NewVarIntType(32), + ), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010101001f00003900000000000000000000000000021cb43b9aca00fffd550bfbaae07401a2a98117"), + }, + { + name: "unmarshal small-int-key map", + ty: parser.NewMapType(parser.NewIntNType(32), parser.NewBoolType()), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010201000c000101c001000ba00000007bc09a662c32"), + }, + { + name: "unmarshal small-uint-key map", + ty: parser.NewMapType(parser.NewUIntNType(16), parser.NewAddressType()), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c72410104010053000101c0010202cb02030045a7400b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe80045a3cff5555555555555555555555555555555555555555555555555555555555555555888440ce8"), + }, + { + name: "unmarshal big-uint-key map", + ty: parser.NewMapType(parser.NewUIntNType(78), parser.NewCellType()), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010301001a000101c0010115a70000000000000047550902000b000000001ab01d5bf1a9"), + }, + { + name: "unmarshal bits-key map", + ty: parser.NewMapType( + parser.NewBitsNType(16), + parser.NewMapType( + parser.NewIntNType(64), + parser.NewTensorType( + parser.NewAddressType(), + parser.NewCoinsType(), + ), + ), + ), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010301003b000101c0010106a0828502005ea0000000000000003e400b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe43b9aca00b89cdc86"), + }, + { + name: "unmarshal address-key map", + ty: parser.NewMapType(parser.NewAddressType(), parser.NewCoinsType()), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010201002f000101c0010051a17002c44ea652d4092859c67da44e4ca3add6565b0e2897d640a2c51bfb370d8877f9409502f9002016fdc16e"), + }, + { + name: "unmarshal union with dec prefix", + ty: parser.NewUnionType( + 1, true, + parser.NewUnionVariant(parser.NewIntNType(16), "0"), + parser.NewUnionVariant(parser.NewIntNType(128), "1"), + ), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010101001300002180000000000000000000000003b5577dc0660d6029"), + }, + { + name: "unmarshal union with bin prefix", + ty: parser.NewUnionType( + 3, false, + parser.NewUnionVariant(parser.NewStructType("AddressWithPrefix"), "0b001"), + parser.NewUnionVariant(parser.NewStructType("MapWithPrefix"), "0b011"), + parser.NewUnionVariant(parser.NewStructType("CellWithPrefix"), "0b111"), + ), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010201002e0001017801004fa17002c44ea652d4092859c67da44e4ca3add6565b0e2897d640a2c51bfb370d8877f900a4d89920c413c650"), + abiFiles: []string{"testdata/bin_union.json"}, + }, + { + name: "unmarshal union with hex prefix", + ty: parser.NewUnionType( + 32, false, + parser.NewUnionVariant(parser.NewStructType("UInt66WithPrefix"), "0x12345678"), + parser.NewUnionVariant(parser.NewStructType("UInt33WithPrefix"), "0xdeadbeef"), + parser.NewUnionVariant(parser.NewStructType("UInt4WithPrefix"), "0x89abcdef"), + ), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010101000b000011deadbeef00000000c0d75977b9"), + abiFiles: []string{"testdata/hex_union.json"}, + }, + { + name: "unmarshal a-lot-refs from alias", + ty: parser.NewAliasType("GoodNamingForMsg"), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c724101040100b7000377deadbeef80107bfaaa5cc6e5368e5f9799188bd798cd22e04ab16d1d8ea4fc37480741e635087735940143ffffffffffffffffffffffffffff63c006010203004b80010df454cebee868f611ba8c0d4a9371fb73105396505783293a7625f75db3b9880bebc20100438006e05909e22b2e5e6087533314ee56505f85212914bd5547941a2a658ac62fe101004f801622753296a04942ce33ed2272651d6eb2b2d87144beb2051628dfd9b86c43bfcc12309ce54001e09a48b8"), + abiFiles: []string{"testdata/refs.json"}, + }, + { + name: "unmarshal a-lot-refs from struct", + ty: parser.NewStructType("ManyRefsMsg"), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c724101040100b7000377deadbeef80107bfaaa5cc6e5368e5f9799188bd798cd22e04ab16d1d8ea4fc37480741e635087735940143ffffffffffffffffffffffffffff63c006010203004b80010df454cebee868f611ba8c0d4a9371fb73105396505783293a7625f75db3b9880bebc20100438006e05909e22b2e5e6087533314ee56505f85212914bd5547941a2a658ac62fe101004f801622753296a04942ce33ed2272651d6eb2b2d87144beb2051628dfd9b86c43bfcc12309ce54001e09a48b8"), + abiFiles: []string{"testdata/refs.json"}, + }, + { + name: "unmarshal a-lot-generics from struct", + ty: parser.NewStructType("ManyRefsMsg", parser.NewUIntNType(16)), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c72410103010043000217d017d7840343b9aca0000108010200080000007b005543b9aca001017d78402005889d4ca5a81250b38cfb489c99475bacacb61c512fac81458a37f66e1b10eff422fc7647"), + abiFiles: []string{"testdata/generics.json"}, + }, + { + name: "unmarshal a-lot-generics from alias", + ty: parser.NewAliasType("GoodNamingForMsg", parser.NewUIntNType(16)), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c72410103010043000217d017d7840343b9aca0000108010200080000007b005543b9aca001017d78402005889d4ca5a81250b38cfb489c99475bacacb61c512fac81458a37f66e1b10eff422fc7647"), + abiFiles: []string{"testdata/generics.json"}, + }, + { + name: "unmarshal struct with default values", + ty: parser.NewStructType("DefaultTest"), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010101003100005d80000002414801622753296a04942ce33ed2272651d6eb2b2d87144beb2051628dfd9b86c43bfd00000156ac2c4c70811a9dde"), + abiFiles: []string{"testdata/default_values.json"}, + }, + { + name: "unmarshal a-lot-numbers", + ty: parser.NewStructType("Numbers"), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c72410101010033000062000000000000000000000000000000000000000000000000000000000000000000000000000000f1106aecc4c800020926dc62f014"), + abiFiles: []string{"testdata/numbers.json"}, + }, + { + name: "unmarshal a-lot-random-fields", + ty: parser.NewStructType("RandomFields"), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c7241010301007800028b79480107bfaaa5cc6e5368e5f9799188bd798cd22e04ab16d1d8ea4fc37480741e6350e038d7eb37c5e80000000ab50ee6b28000000000000016e4c000006c175300001801bc01020001c00051000000000005120041efeaa9731b94da397e5e64622f5e63348b812ac5b4763a93f0dd201d0798d4409e337ceb"), + abiFiles: []string{"testdata/random_fields.json"}, + }, + { + name: "unmarshal alias with custom unpack", + ty: parser.NewAliasType("MyAlias"), + cell: *boc.MustDeserializeSinglRootHex("b5ee9c724101010100470000890000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043b9aca00886e91196"), + abiFiles: []string{"testdata/custom_pack_unpack.json"}, + customUnpackResolver: func(decoder *Decoder) func(alias parser.AliasRef, cell *boc.Cell, value *AliasValue) error { + return func(alias parser.AliasRef, cell *boc.Cell, value *AliasValue) error { + err := cell.Skip(512) + if err != nil { + return fmt.Errorf("failed to 512 bits from alias") + } + val, err := decoder.Unmarshal(cell, parser.NewStructType("My")) + if err != nil { + return fmt.Errorf("failed to unmarshal alias' coins") + } + *value = AliasValue(*val) + return nil + } + }, + }, + } { + abis := make([]parser.ABI, len(curr.abiFiles)) + for i, abiFile := range curr.abiFiles { + data, err := os.ReadFile(abiFile) + if err != nil { + b.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + b.Fatal(err) + } + abis[i] = abi + } + b.Run(curr.name, func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + curr.cell.ResetCounters() + decoder := NewDecoder() + if err := decoder.WithABIs(abis...); err != nil { + b.Fatal(err) + } + if curr.customUnpackResolver != nil { + decoder.WithCustomUnpackResolver(curr.customUnpackResolver(decoder)) + } + _, err := decoder.Unmarshal(&curr.cell, curr.ty) + if err != nil { + b.Fatal(err) + } + } + }) + } +} + +func BenchmarkRuntimeMessageUnmarshalling(b *testing.B) { + type Case struct { + name string + cell boc.Cell + abiFiles []string + } + for _, curr := range []Case{ + { + name: "unmarshal jetton transfer message stonfi", + cell: *boc.MustDeserializeSinglRootHex("b5ee9c720102030100011b0001ae0f8a7ea5546de4efb35a04c230f424080125c28235ca8d125e676591513d520721b1fe99f7722f4c87723ce7ee0dfb73a300268806c2c709c47ec1b610073c38ef75cf6066e9e4368b10ddbdc015d0e59c98881c9c38010101e16664de2a801244183034d9fd59a236f71ec4271be377399056dda4cc3a5ebf5dc40967df64100268806c2c709c47ec1b610073c38ef75cf6066e9e4368b10ddbdc015d0e59c98a004d100d858e1388fd836c200e7871deeb9ec0cdd3c86d1621bb7b802ba1cb39310000000034d3e7f3c002009542ecec75480134403616384e23f60db08039e1c77bae7b03374f21b45886edee00ae872ce4c4000005400f4684b10a661eaa395f87d4a660e6dfc3bec187a8b24f6f362c0c6b1d20f1b5d8"), + abiFiles: []string{"testdata/jetton_transfer.json", "testdata/payloads.json"}, + }, + } { + abis := make([]parser.ABI, len(curr.abiFiles)) + for i, abiFile := range curr.abiFiles { + data, err := os.ReadFile(abiFile) + if err != nil { + b.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + b.Fatal(err) + } + abis[i] = abi + } + b.Run(curr.name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + curr.cell.ResetCounters() + decoder := NewDecoder() + if err := decoder.WithABIs(abis...); err != nil { + b.Fatal(err) + } + _, err := decoder.UnmarshalMessage(&curr.cell) + if err != nil { + b.Fatal(err) + } + } + }) + } +} + +func BenchmarkTLBMessageUnmarshalling(b *testing.B) { + cell := boc.MustDeserializeSinglRootHex("b5ee9c720102030100011b0001ae0f8a7ea5546de4efb35a04c230f424080125c28235ca8d125e676591513d520721b1fe99f7722f4c87723ce7ee0dfb73a300268806c2c709c47ec1b610073c38ef75cf6066e9e4368b10ddbdc015d0e59c98881c9c38010101e16664de2a801244183034d9fd59a236f71ec4271be377399056dda4cc3a5ebf5dc40967df64100268806c2c709c47ec1b610073c38ef75cf6066e9e4368b10ddbdc015d0e59c98a004d100d858e1388fd836c200e7871deeb9ec0cdd3c86d1621bb7b802ba1cb39310000000034d3e7f3c002009542ecec75480134403616384e23f60db08039e1c77bae7b03374f21b45886edee00ae872ce4c4000005400f4684b10a661eaa395f87d4a660e6dfc3bec187a8b24f6f362c0c6b1d20f1b5d8") + for i := 0; i < b.N; i++ { + cell.ResetCounters() + var body abi.InMsgBody + decoder := tlb.NewDecoder() + if err := decoder.Unmarshal(cell, &body); err != nil { + b.Fatal(err) + } + } +} diff --git a/tolk/runtime_test.go b/tolk/runtime_test.go new file mode 100644 index 00000000..2ce2d354 --- /dev/null +++ b/tolk/runtime_test.go @@ -0,0 +1,4401 @@ +package tolk + +import ( + "bytes" + "encoding/json" + "fmt" + "math/big" + "os" + "testing" + + "github.com/tonkeeper/tongo" + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tolk/parser" + "github.com/tonkeeper/tongo/ton" +) + +const jsonFilesPath = "testdata/json/" + +func TestRuntime_UnmarshalSmallInt(t *testing.T) { + inputFilename := "small_int" + ty := parser.NewIntNType(24) + + currCell, err := boc.DeserializeBocHex("b5ee9c72410101010005000006ff76c41616db06") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetSmallInt() + if !ok { + t.Errorf("v.GetSmallInt() not successeded") + } + if val != -35132 { + t.Errorf("val != -35132, got %v", val) + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalBigInt(t *testing.T) { + inputFilename := "big_int" + ty := parser.NewIntNType(183) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101001900002dfffffffffffffffffffffffffffffffffff99bfeac6423a6f0b50c") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetBigInt() + if !ok { + t.Errorf("v.GetBigInt() not successeded") + } + if val.Cmp(big.NewInt(-3513294376431)) != 0 { + t.Errorf("val != -3513294376431, got %v", val) + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalSmallUInt(t *testing.T) { + inputFilename := "small_uint" + ty := parser.NewUIntNType(53) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000900000d00000000001d34e435eafd") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetSmallUInt() + if !ok { + t.Errorf("v.GetSmallUInt() not successeded") + } + if val != 934 { + t.Errorf("val != 934, got %v", val) + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalBigUInt(t *testing.T) { + inputFilename := "big_uint" + ty := parser.NewUIntNType(257) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101002300004100000000000000000000000000000000000000000000000000009fc4212a38ba40b11cce12") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetBigUInt() + if !ok { + t.Errorf("v.GetBigUInt() not successeded") + } + if val.Cmp(big.NewInt(351329437643124)) != 0 { + t.Errorf("val != 351329437643124, got %v", val.String()) + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalVarInt(t *testing.T) { + inputFilename := "var_int" + ty := parser.NewVarInt16Type() + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000600000730c98588449b6923") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetVarInt() + if !ok { + t.Errorf("v.GetVarInt() not successeded") + } + if val.Cmp(big.NewInt(825432)) != 0 { + t.Errorf("val != 825432, got %v", val.String()) + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalVarUInt(t *testing.T) { + inputFilename := "var_uint" + ty := parser.NewVarUInt32Type() + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000800000b28119ab36b44d3a86c0f") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetVarUInt() + if !ok { + t.Errorf("v.GetVarUInt() not successeded") + } + if val.Cmp(big.NewInt(9451236712)) != 0 { + t.Errorf("val != 9451236712, got %v", val.String()) + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalBits(t *testing.T) { + inputFilename := "bits" + ty := parser.NewBitsNType(24) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000500000631323318854035") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetBits() + if !ok { + t.Errorf("v.GetBits() not successeded") + } + if bytes.Equal(val.Buffer(), []byte{55, 56, 57}) { + t.Errorf("val != {55, 56, 57}, got %v", val) + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalCoins(t *testing.T) { + inputFilename := "coins" + ty := parser.NewCoinsType() + + currCell, err := boc.DeserializeBocHex("b5ee9c72410101010007000009436ec6e0189ebbd7f4") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetCoins() + if !ok { + t.Errorf("v.GetCoins() not successeded") + } + if val.Cmp(big.NewInt(921464321)) != 0 { + t.Errorf("val != 921464321, got %v", val) + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalBool(t *testing.T) { + inputFilename := "bool" + ty := parser.NewBoolType() + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000300000140f6d24034") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetBool() + if !ok { + t.Errorf("v.GetBool() not successeded") + } + if val { + t.Error("val is true") + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalCell(t *testing.T) { + inputFilename := "cell" + ty := parser.NewCellType() + + currCell, err := boc.DeserializeBocHex("b5ee9c724101020100090001000100080000007ba52a3292") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetCell() + if !ok { + t.Errorf("v.GetCell() not successeded") + } + hs, err := val.HashString() + if err != nil { + t.Fatal(err) + } + if hs != "644e68a539c5107401d194bc82169cbf0ad1635796891551e0750705ab2d74ae" { + t.Errorf("val.Hash() != 644e68a539c5107401d194bc82169cbf0ad1635796891551e0750705ab2d74ae, got %v", hs) + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalRemaining(t *testing.T) { + inputFilename := "remaining" + ty := parser.NewRemainingType() + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000900000dc0800000000ab8d04726e4") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetRemaining() + if !ok { + t.Errorf("v.GetCell() not successeded") + } + hs, err := val.HashString() + if err != nil { + t.Fatal(err) + } + if hs != "f1c4e07fbd1786411c2caa9ac9f5d7240aa2007a2a1d5e5ac44f8a168cd4e36b" { + t.Errorf("val.Hash() != f1c4e07fbd1786411c2caa9ac9f5d7240aa2007a2a1d5e5ac44f8a168cd4e36b, got %v", hs) + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalAddress(t *testing.T) { + inputFilename := "internal_address" + ty := parser.NewAddressType() + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101002400004380107bfaaa5cc6e5368e5f9799188bd798cd22e04ab16d1d8ea4fc37480741e6351064a3e1a6") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetAddress() + if !ok { + t.Errorf("v.GetAddress() not successeded") + } + if val.ToRaw() != "0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8" { + t.Errorf("val.GetAddress() != 0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8, got %v", val.ToRaw()) + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalNotExitsOptionalAddress(t *testing.T) { + inputFilename := "not_exists_optional_address" + ty := parser.NewAddressOptType() + + currCell, err := boc.DeserializeBocHex("b5ee9c724101010100030000012094418655") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetOptionalAddress() + if !ok { + t.Errorf("v.GetOptionalAddress() not successeded") + } + + if val.SumType != "NoneAddress" { + t.Errorf("val.GetAddress() != none address") + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalExistsOptionalAddress(t *testing.T) { + inputFilename := "exists_optional_address" + ty := parser.NewAddressOptType() + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101002400004380107bfaaa5cc6e5368e5f9799188bd798cd22e04ab16d1d8ea4fc37480741e6351064a3e1a6") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetOptionalAddress() + if !ok { + t.Errorf("v.GetOptionalAddress() not successeded") + } + + if val.SumType == "InternalAddress" && val.InternalAddress.ToRaw() != "0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8" { + t.Errorf("val.GetAddress() != 0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8, got %v", val.InternalAddress.ToRaw()) + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalExternalAddress(t *testing.T) { + inputFilename := "external_address" + ty := parser.NewAddressExtType() + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000600000742082850fcbd94fd") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetExternalAddress() + if !ok { + t.Errorf("v.GetExternalAddress() not successeded") + } + addressPart := boc.NewBitString(16) + err = addressPart.WriteBytes([]byte{97, 98}) + if err != nil { + t.Fatal(err) + } + if val.Len != 8 && bytes.Equal(val.Address.Buffer(), []byte{97, 98}) { + t.Errorf("val.GetExternalAddress() != {97, 98}, got %v", val.Address.Buffer()) + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalAnyNoneAddress(t *testing.T) { + inputFilename := "any_none_address" + ty := parser.NewAddressAnyType() + + currCell, err := boc.DeserializeBocHex("b5ee9c724101010100030000012094418655") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetAnyAddress() + if !ok { + t.Errorf("v.GetAnyAddress() not successeded") + } + if val.SumType != "NoneAddress" { + t.Errorf("val.GetAddress() != none address") + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalAnyInternalAddress(t *testing.T) { + inputFilename := "any_internal_address" + ty := parser.NewAddressAnyType() + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101002400004380107bfaaa5cc6e5368e5f9799188bd798cd22e04ab16d1d8ea4fc37480741e6351064a3e1a6") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetAnyAddress() + if !ok { + t.Errorf("v.GetAnyAddress() not successeded") + } + if val.SumType == "InternalAddress" && val.InternalAddress.ToRaw() != "0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8" { + t.Errorf("val.GetAddress() != 0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8, got %v", val.InternalAddress.ToRaw()) + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalAnyExternalAddress(t *testing.T) { + inputFilename := "any_external_address" + ty := parser.NewAddressAnyType() + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000600000742082850fcbd94fd") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetAnyAddress() + if !ok { + t.Errorf("v.GetAnyAddress() not successeded") + } + addressPart := boc.NewBitString(16) + err = addressPart.WriteBytes([]byte{97, 98}) + if err != nil { + t.Fatal(err) + } + if val.SumType == "ExternalAddress" && val.ExternalAddress.Len != 8 && bytes.Equal(val.ExternalAddress.Address.Buffer(), []byte{97, 98}) { + t.Errorf("val.GetExternalAddress() != {97, 98}, got %v", val.ExternalAddress.Address.Buffer()) + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalAnyVarAddress(t *testing.T) { + inputFilename := "any_var_address" + ty := parser.NewAddressAnyType() + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000900000dc0800000000ab8d04726e4") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetAnyAddress() + if !ok { + t.Errorf("v.GetAnyAddress() not successeded") + } + if val.SumType != "VarAddress" { + t.Errorf("val.GetAddress() != VarAddress") + } + if val.VarAddress.Len != 8 { + t.Errorf("val.VarAddress.Len != 8, got %v", val.VarAddress.Len) + } + if val.VarAddress.Workchain != 0 { + t.Errorf("val.VarAddress.Workchain != 0, got %v", val.VarAddress.Workchain) + } + if bytes.Equal(val.VarAddress.Address.Buffer(), []byte{97, 98}) { + t.Errorf("val.GetExternalAddress() != {97, 98}, got %v", val.ExternalAddress.Address.Buffer()) + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalNotExistsNullable(t *testing.T) { + inputFilename := "not_exists_nullable" + ty := parser.NewNullableType(parser.NewRemainingType()) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000300000140f6d24034") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetOptionalValue() + if !ok { + t.Errorf("v.GetOptionalValue() not successeded") + } + if val.IsExists { + t.Errorf("v.GetOptionalValue() is exists") + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalExistsNullable(t *testing.T) { + inputFilename := "exists_nullable" + ty := parser.NewNullableType(parser.NewCellType()) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010201000b000101c001000900000c0ae007880db9") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetOptionalValue() + if !ok { + t.Errorf("v.GetOptionalValue() not successeded") + } + if !val.IsExists { + t.Errorf("v.GetOptionalValue() != exists") + } + innerVal, ok := val.Val.GetCell() + if !ok { + t.Errorf("v.GetOptionalValue().GetCell() not successeded") + } + hs, err := innerVal.HashString() + if err != nil { + t.Fatal(err) + } + if hs != "df05386a55563049a4834a4cc1ec0dc22f3dcb63c04f7258ae475c5d28981773" { + t.Errorf("v.GetOptionalValue().GetCell() != df05386a55563049a4834a4cc1ec0dc22f3dcb63c04f7258ae475c5d28981773, got %v", hs) + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalRef(t *testing.T) { + inputFilename := "ref" + ty := parser.NewCellOfType(parser.NewIntNType(65)) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010201000e000100010011000000000009689e40e150b4c5") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetRefValue() + if !ok { + t.Errorf("v.GetRefValue() not successeded") + } + innerVal, ok := val.GetBigInt() + if !ok { + t.Errorf("v.GetRefValue().GetBigInt() not successeded") + } + if innerVal.Cmp(big.NewInt(1233212)) != 0 { + t.Errorf("v.GetRefValue().GetBigInt() != 1233212, got %v", innerVal.String()) + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalEmptyTensor(t *testing.T) { + inputFilename := "empty_tensor" + ty := parser.NewTensorType() + + currCell, err := boc.DeserializeBocHex("b5ee9c724101010100020000004cacb9cd") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetTensor() + if !ok { + t.Errorf("v.GetTensor() not successeded") + } + + if len(val) != 0 { + t.Errorf("v.GetTensor() != empty") + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalTensor(t *testing.T) { + inputFilename := "tensor" + ty := parser.NewTensorType( + parser.NewUIntNType(123), + parser.NewBoolType(), + parser.NewCoinsType(), + parser.NewTensorType( + parser.NewIntNType(23), + parser.NewNullableType(parser.NewIntNType(2)), + ), + parser.NewVarIntType(32), + ) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101001f00003900000000000000000000000000021cb43b9aca00fffd550bfbaae07401a2a98117") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetTensor() + if !ok { + t.Errorf("v.GetTensor() not successeded") + } + + val0, ok := val[0].GetBigUInt() + if !ok { + t.Errorf("val[0].GetBigUInt() not successeded") + } + if val0.Cmp(big.NewInt(4325)) != 0 { + t.Errorf("val[0].GetBigUInt() != 4325, got %v", val0.String()) + } + + val1, ok := val[1].GetBool() + if !ok { + t.Errorf("val[1].GetBool() not successeded") + } + if !val1 { + t.Error("val[1].GetBool() is false") + } + + val2, ok := val[2].GetCoins() + if !ok { + t.Errorf("val[2].GetCoins() not successeded") + } + if val2.Cmp(big.NewInt(1_000_000_000)) != 0 { + t.Errorf("val[2].GetCoins() != 1000000000, got %v", val2.String()) + } + + val3, ok := val[3].GetTensor() + if !ok { + t.Errorf("val[3].GetTensor() not successeded") + } + + val30, ok := val3[0].GetSmallInt() + if !ok { + t.Errorf("val[3][0].GetSmallInt() not successeded") + } + if val30 != -342 { + t.Errorf("val[3][0].GetSmallInt() != -342, got %v", val30) + } + + optVal31, ok := val3[1].GetOptionalValue() + if !ok { + t.Errorf("val[3][1].GetOptionalValue() not successeded") + } + if !optVal31.IsExists { + t.Errorf("val[3][1].GetOptionalValue() != exists") + } + val31, ok := optVal31.Val.GetSmallInt() + if !ok { + t.Errorf("val[3][1].GetOptionalValue().GetSmallInt() not successeded") + } + if val31 != 0 { + t.Errorf("val[3][1].GetOptionalValue().GetSmallInt() != 0, got %v", val31) + } + + val4, ok := val[4].GetVarInt() + if !ok { + t.Errorf("val[4].GetVarInt() not successeded") + } + if val4.Cmp(big.NewInt(-9_304_000_000)) != 0 { + t.Errorf("val[4].GetVarInt() != -9304000000, got %v", val4.String()) + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalIntKeyMap(t *testing.T) { + inputFilename := "int_key_map" + ty := parser.NewMapType(parser.NewIntNType(32), parser.NewBoolType()) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010201000c000101c001000ba00000007bc09a662c32") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetMap() + if !ok { + t.Errorf("v.GetMap() not successeded") + } + val123, ok := val.GetBySmallInt(Int64(123)) + if !ok { + t.Errorf("val[123] not found") + } + val123Val, ok := val123.GetBool() + if !ok { + t.Errorf("val[123].GetBool() not successeded") + } + if !val123Val { + t.Errorf("val[123] is false") + } + + _, ok = val.GetBySmallInt(Int64(0)) + if ok { + t.Errorf("val[0] was found") + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalUIntKeyMap(t *testing.T) { + inputFilename := "uint_key_map" + ty := parser.NewMapType(parser.NewUIntNType(16), parser.NewAddressType()) + + currCell, err := boc.DeserializeBocHex("b5ee9c72410104010053000101c0010202cb02030045a7400b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe80045a3cff5555555555555555555555555555555555555555555555555555555555555555888440ce8") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetMap() + if !ok { + t.Errorf("v.GetMap() not successeded") + } + val23, ok := val.GetBySmallUInt(UInt64(23)) + if !ok { + t.Errorf("val[23] not found") + } + val23Val, ok := val23.GetAddress() + if !ok { + t.Errorf("val[23].GetAddress() not successeded") + } + if val23Val.ToRaw() != "-1:5555555555555555555555555555555555555555555555555555555555555555" { + t.Errorf("val[23] != -1:5555555555555555555555555555555555555555555555555555555555555555, got %v", val23Val.ToRaw()) + } + + val14, ok := val.GetBySmallUInt(UInt64(14)) + if !ok { + t.Errorf("val[14] not found") + } + val14Val, ok := val14.GetAddress() + if !ok { + t.Errorf("val[14].GetAddress() not successeded") + } + if val14Val.ToRaw() != "0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe" { + t.Errorf("val[14] != 0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe, got %v", val23Val.ToRaw()) + } + + _, ok = val.GetBySmallInt(Int64(0)) + if ok { + t.Errorf("val[0] was found") + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalBigIntKeyMap(t *testing.T) { + inputFilename := "big_int_key_map" + ty := parser.NewMapType(parser.NewUIntNType(78), parser.NewCellType()) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010301001a000101c0010115a70000000000000047550902000b000000001ab01d5bf1a9") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetMap() + if !ok { + t.Errorf("v.GetMap() not successeded") + } + val1, ok := val.GetByBigUInt(BigUInt(*big.NewInt(2337412))) + if !ok { + t.Errorf("val[2337412] not found") + } + val1Val, ok := val1.GetCell() + if !ok { + t.Errorf("val[2337412].GetCell() not successeded") + } + hs1, err := val1Val.HashString() + if err != nil { + t.Fatal(err) + } + if hs1 != "8be375797c46a090b06973ee57e96b1d1ae127609c400ceba7194e77e41c5150" { + t.Errorf("val[2337412].GetCell().GetHashString() != 8be375797c46a090b06973ee57e96b1d1ae127609c400ceba7194e77e41c5150, got %v", hs1) + } + + _, ok = val.GetByBigInt(BigInt(*big.NewInt(34))) + if ok { + t.Errorf("val[34] was found") + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalBitsKeyMap(t *testing.T) { + inputFilename := "bits_int_key_map" + ty := parser.NewMapType( + parser.NewBitsNType(16), + parser.NewMapType( + parser.NewIntNType(64), + parser.NewTensorType( + parser.NewAddressType(), + parser.NewCoinsType(), + ), + ), + ) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010301003b000101c0010106a0828502005ea0000000000000003e400b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe43b9aca00b89cdc86") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetMap() + if !ok { + t.Errorf("v.GetMap() not successeded") + } + key1 := boc.NewBitString(16) + err = key1.WriteBytes([]byte{65, 66}) + if err != nil { + t.Fatal(err) + } + val1, ok := val.GetByBits(Bits(key1)) + if !ok { + t.Errorf("val[{65, 66}] not found") + } + + mp, ok := val1.GetMap() + if !ok { + t.Errorf("val[{65, 66}].GetMap() not successeded") + } + val1_124, ok := mp.GetBySmallInt(124) + if !ok { + t.Errorf("val[{65, 66}][124] not found") + } + val1_124Val, ok := val1_124.GetTensor() + if !ok { + t.Errorf("val[{65, 66}][124].GetTensor() not successeded") + } + val1_124Val0, ok := val1_124Val[0].GetAddress() + if !ok { + t.Errorf("val[{65, 66}][124][0].GetAddress() not successeded") + } + if val1_124Val0.ToRaw() != "0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe" { + t.Errorf("val[{65, 66}][124][0].GetAddress() != 0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe, got %v", val1_124Val0.ToRaw()) + } + + val1_124Val1, ok := val1_124Val[1].GetCoins() + if !ok { + t.Errorf("val[{97, 98}][124][1].GetCoins() not successeded") + } + if val1_124Val1.Cmp(big.NewInt(1_000_000_000)) != 0 { + t.Errorf("val[{97, 98}][124][1].GetCoins() != 1_000_000_000, got %v", val1_124Val1.String()) + } + + key2 := boc.NewBitString(16) + err = key2.WriteBytes([]byte{98, 99}) + if err != nil { + t.Fatal(err) + } + _, ok = val.GetByBits(Bits(key2)) + if ok { + t.Errorf("val[{98, 99}] was found") + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalAddressKeyMap(t *testing.T) { + inputFilename := "address_key_map" + ty := parser.NewMapType(parser.NewAddressType(), parser.NewCoinsType()) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010201002f000101c0010051a17002c44ea652d4092859c67da44e4ca3add6565b0e2897d640a2c51bfb370d8877f9409502f9002016fdc16e") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetMap() + if !ok { + t.Errorf("v.GetMap() not successeded") + } + // todo: create converter + addr := tongo.MustParseAddress("EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs") + val1, ok := val.GetByInternalAddress(InternalAddress{ + Workchain: int8(addr.ID.Workchain), + Address: addr.ID.Address, + }) + if !ok { + t.Errorf("val[\"EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs\"] not found") + } + val1Val, ok := val1.GetCoins() + if !ok { + t.Errorf("val[\"EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs\"].GetCoins() not successeded") + } + if val1Val.Cmp(big.NewInt(10_000_000_000)) != 0 { + t.Errorf("val[\"EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs\"].GetCoins() != 10_000_000_000, got %v", val1Val) + } + + addr = tongo.MustParseAddress("UQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqEBI") + _, ok = val.GetByInternalAddress(InternalAddress{ + Workchain: int8(addr.ID.Workchain), + Address: addr.ID.Address, + }) + if ok { + t.Errorf("val[\"UQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqEBI\"] was found") + } + + err = compareExpectedJson(inputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalUnionWithDecPrefix(t *testing.T) { + jsonInputFilename := "union_with_dec_prefix" + ty := parser.NewUnionType( + 1, true, + parser.NewUnionVariant(parser.NewIntNType(16), "0"), + parser.NewUnionVariant(parser.NewIntNType(128), "1"), + ) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101001300002180000000000000000000000003b5577dc0660d6029") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetUnion() + if !ok { + t.Errorf("v.GetUnion() not successeded") + } + if val.Prefix.Len != 1 { + t.Errorf("val.Prefix.Len != 1") + } + if val.Prefix.Prefix != 1 { + t.Errorf("val.Prefix != 1, got %v", val.Prefix.Prefix) + } + + unionVal, ok := val.Val.GetBigInt() + if !ok { + t.Errorf("val.Val.GetBigInt() not successeded") + } + if unionVal.Cmp(big.NewInt(124432123)) != 0 { + t.Errorf("val.Val.GetBigInt() != 124432123, got %v", unionVal.String()) + } + + err = compareExpectedJson(jsonInputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalUnionWithBinPrefix(t *testing.T) { + jsonInputFilename := "union_with_bin_prefix" + inputFilename := "testdata/bin_union.json" + data, err := os.ReadFile(inputFilename) + if err != nil { + t.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + t.Fatal(err) + } + + ty := parser.NewUnionType( + 3, false, + parser.NewUnionVariant(parser.NewStructType("AddressWithPrefix"), "0b001"), + parser.NewUnionVariant(parser.NewStructType("MapWithPrefix"), "0b011"), + parser.NewUnionVariant(parser.NewStructType("CellWithPrefix"), "0b111"), + ) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010201002e0001017801004fa17002c44ea652d4092859c67da44e4ca3add6565b0e2897d640a2c51bfb370d8877f900a4d89920c413c650") + if err != nil { + t.Fatal(err) + } + decoder := NewDecoder() + err = decoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + v, err := decoder.Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetUnion() + if !ok { + t.Errorf("v.GetUnion() not successeded") + } + if val.Prefix.Len != 3 { + t.Errorf("val.Prefix.Len != 3, got %v", val.Prefix.Len) + } + if val.Prefix.Prefix != 3 { + t.Errorf("val.Prefix.Prefix != 3, got %v", val.Prefix.Prefix) + } + + mapStruct, ok := val.Val.GetStruct() + if !ok { + t.Errorf("val.GetStruct() not successeded") + } + mapStructVal, ok := mapStruct.GetField("v") + if !ok { + t.Errorf("val[v] not successeded") + } + unionVal, ok := mapStructVal.GetMap() + if !ok { + t.Errorf("val[v].GetMap() not successeded") + } + addr := tongo.MustParseAddress("EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs") + mapVal, ok := unionVal.GetByInternalAddress(InternalAddress{ + Workchain: int8(addr.ID.Workchain), + Address: addr.ID.Address, + }) + if !ok { + t.Errorf("val.GetMap()[\"EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs\"] not successeded") + } + mapCoins, ok := mapVal.GetCoins() + if !ok { + t.Errorf("val.GetMap()[\"EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs\"].GetCoins() not successeded") + } + if mapCoins.Cmp(big.NewInt(43213412)) != 0 { + t.Errorf("val.GetMap()[\"EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs\"].GetCoins() != 43213412, got %v", mapCoins.String()) + } + + err = compareExpectedJson(jsonInputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalUnionWithHexPrefix(t *testing.T) { + jsonInputFilename := "union_with_hex_prefix" + inputFilename := "testdata/hex_union.json" + data, err := os.ReadFile(inputFilename) + if err != nil { + t.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + t.Fatal(err) + } + + ty := parser.NewUnionType( + 32, false, + parser.NewUnionVariant(parser.NewStructType("UInt66WithPrefix"), "0x12345678"), + parser.NewUnionVariant(parser.NewStructType("UInt33WithPrefix"), "0xdeadbeef"), + parser.NewUnionVariant(parser.NewStructType("UInt4WithPrefix"), "0x89abcdef"), + ) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000b000011deadbeef00000000c0d75977b9") + if err != nil { + t.Fatal(err) + } + decoder := NewDecoder() + err = decoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + v, err := decoder.Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + val, ok := v.GetUnion() + if !ok { + t.Errorf("v.GetUnion() not successeded") + } + if val.Prefix.Len != 32 { + t.Errorf("val.Prefix.Len != 32, got %v", val.Prefix.Len) + } + if val.Prefix.Prefix != 0xdeadbeef { + t.Errorf("val.Prefix.Prefix != 0xdeadbeef, got %x", val.Prefix.Prefix) + } + + structVal, ok := val.Val.GetStruct() + if !ok { + t.Errorf("val.Val.GetStruct() not successeded") + } + structV, ok := structVal.GetField("v") + if !ok { + t.Errorf("val.Val[v] not successeded") + } + unionVal, ok := structV.GetSmallUInt() + if !ok { + t.Errorf("val.GetSmallUInt() not successeded") + } + if unionVal != 1 { + t.Errorf("val.GetSmallUInt() != 1, got %v", unionVal) + } + + err = compareExpectedJson(jsonInputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalALotRefsFromAlias(t *testing.T) { + jsonInputFilename := "a_lot_refs_from_alias" + inputFilename := "testdata/refs.json" + data, err := os.ReadFile(inputFilename) + if err != nil { + t.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + t.Fatal(err) + } + + ty := parser.NewAliasType("GoodNamingForMsg") + + currCell, err := boc.DeserializeBocHex("b5ee9c724101040100b7000377deadbeef80107bfaaa5cc6e5368e5f9799188bd798cd22e04ab16d1d8ea4fc37480741e635087735940143ffffffffffffffffffffffffffff63c006010203004b80010df454cebee868f611ba8c0d4a9371fb73105396505783293a7625f75db3b9880bebc20100438006e05909e22b2e5e6087533314ee56505f85212914bd5547941a2a658ac62fe101004f801622753296a04942ce33ed2272651d6eb2b2d87144beb2051628dfd9b86c43bfcc12309ce54001e09a48b8") + if err != nil { + t.Fatal(err) + } + decoder := NewDecoder() + err = decoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + v, err := decoder.Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + currAlias, ok := v.GetAlias() + if !ok { + t.Errorf("v.GetAlias() not successeded") + } + currStruct, ok := currAlias.GetStruct() + if !ok { + t.Fatalf("struct not found") + } + pref, ok := currStruct.GetPrefix() + if !ok { + t.Fatalf("currStruct.Prefix not found") + } + if pref.Len != 32 { + t.Errorf("pref.Len != 32, got %v", pref.Len) + } + if pref.Prefix != 0xdeadbeef { + t.Errorf("val.Prefix.Prefix != 0xdeadbeef, got %x", pref.Prefix) + } + + user1, ok := currStruct.GetField("user1") + if !ok { + t.Fatalf("currStruct[user1] not found") + } + user1Alias, ok := user1.GetAlias() + if !ok { + t.Fatalf("currStruct[user1].GetAlias() not found") + } + user1Val, ok := user1Alias.GetStruct() + if !ok { + t.Fatalf("currStruct[user1].GetStruct() not successeded") + } + + user1Addr, ok := user1Val.GetField("addr") + if !ok { + t.Fatalf("currStruct[user1][addr] not found") + } + user1AddrVal, ok := user1Addr.GetAddress() + if !ok { + t.Fatalf("currStruct[user1][addr].GetAddress() not successeded") + } + if user1AddrVal.ToRaw() != "0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8" { + t.Errorf("user1AddrVal.ToRaw() != 0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8, got %v", user1AddrVal.ToRaw()) + } + + user1Balance, ok := user1Val.GetField("balance") + if !ok { + t.Fatalf("currStruct[user1][balance] not found") + } + user1BalanceVal, ok := user1Balance.GetCoins() + if !ok { + t.Fatalf("currStruct[user1][balance].GetCoins() not successeded") + } + if user1BalanceVal.Cmp(big.NewInt(1_000_000_000)) != 0 { + t.Errorf("currStruct[user1][balance].GetCoins() != 1000000000, got %v", user1BalanceVal.String()) + } + + user2, ok := currStruct.GetField("user2") + if !ok { + t.Fatalf("currStruct[user2] not found") + } + user2Opt, ok := user2.GetOptionalValue() + if !ok { + t.Fatalf("currStruct[user2].GetOptionalValue() not successeded") + } + if !user2Opt.IsExists { + t.Errorf("currStruct[user2] is not exists") + } + user2Ref, ok := user2Opt.Val.GetRefValue() + if !ok { + t.Fatalf("currStruct[user2].GetRefValue() not successeded") + } + user2Alias, ok := user2Ref.GetAlias() + if !ok { + t.Fatalf("currStruct[user2].GetAlias() not found") + } + user2Val, ok := user2Alias.GetStruct() + if !ok { + t.Fatalf("currStruct[user2].GetStruct() not successeded") + } + + user2Addr, ok := user2Val.GetField("addr") + if !ok { + t.Fatalf("currStruct[user2][addr] not found") + } + user2AddrVal, ok := user2Addr.GetAddress() + if !ok { + t.Fatalf("currStruct[user2][addr].GetAddress() not successeded") + } + if user2AddrVal.ToRaw() != "0:086fa2a675f74347b08dd4606a549b8fdb98829cb282bc1949d3b12fbaed9dcc" { + t.Errorf("user1AddrVal.ToRaw() != 0:086fa2a675f74347b08dd4606a549b8fdb98829cb282bc1949d3b12fbaed9dcc, got %v", user2AddrVal.ToRaw()) + } + + user2Balance, ok := user2Val.GetField("balance") + if !ok { + t.Fatalf("currStruct[user2][balance] not found") + } + user2BalanceVal, ok := user2Balance.GetCoins() + if !ok { + t.Fatalf("currStruct[user2][balance].GetCoins() not successeded") + } + if user2BalanceVal.Cmp(big.NewInt(100_000_000)) != 0 { + t.Errorf("currStruct[user2][balance].GetCoins() != 100000000, got %v", user2BalanceVal.String()) + } + + user3, ok := currStruct.GetField("user3") + if !ok { + t.Fatalf("currStruct[user3] not found") + } + user3Val, ok := user3.GetCell() + if !ok { + t.Fatalf("currStruct[user3].GetCell() not successeded") + } + hs, err := user3Val.HashString() + if err != nil { + t.Fatal(err) + } + if hs != "47f4b117a301111ec48d763a3cd668a246c174efd2df9ba8bd1db406f017453a" { + t.Errorf("currStruct[user3][hashString].Hash != 47f4b117a301111ec48d763a3cd668a246c174efd2df9ba8bd1db406f017453a, got %v", hs) + } + + user4, ok := currStruct.GetField("user4") + if !ok { + t.Fatalf("currStruct[user4] not found") + } + user4Opt, ok := user4.GetOptionalValue() + if !ok { + t.Fatalf("currStruct[user4].GetOptionalValue() not successeded") + } + if user4Opt.IsExists { + t.Errorf("currStruct[user4] exists") + } + + user5, ok := currStruct.GetField("user5") + if !ok { + t.Fatalf("currStruct[user2] not found") + } + user5Ref, ok := user5.GetRefValue() + if !ok { + t.Fatalf("currStruct[user5].GetRefValue() not successeded") + } + user5Val, ok := user5Ref.GetStruct() + if !ok { + t.Fatalf("currStruct[user5].GetStruct() not successeded") + } + + user5Addr, ok := user5Val.GetField("addr") + if !ok { + t.Fatalf("currStruct[user5][addr] not found") + } + user5AddrVal, ok := user5Addr.GetAddress() + if !ok { + t.Fatalf("currStruct[user5][addr].GetAddress() not successeded") + } + if user5AddrVal.ToRaw() != "0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe" { + t.Errorf("user1AddrVal.ToRaw() != 0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe, got %v", user5AddrVal.ToRaw()) + } + + user5Balance, ok := user5Val.GetField("balance") + if !ok { + t.Fatalf("currStruct[user5][balance] not found") + } + user5BalanceVal, ok := user5Balance.GetCoins() + if !ok { + t.Fatalf("currStruct[user5][balance].GetCoins() not successeded") + } + if user5BalanceVal.Cmp(big.NewInt(10_000_000_000_000)) != 0 { + t.Errorf("currStruct[user5][balance].GetCoins() != 10000000000000, got %v", user5BalanceVal.String()) + } + + role, ok := currStruct.GetField("role") + if !ok { + t.Fatalf("currStruct[role] not found") + } + roleEnum, ok := role.GetEnum() + if !ok { + t.Fatalf("currStruct[role].GetEnum() not successeded") + } + if roleEnum.Value.Cmp(big.NewInt(1)) != 0 { + t.Errorf("currStruct[role].GetEnum().Value != 1, got %v", roleEnum.Value.String()) + } + if roleEnum.Name != "Aboba" { + t.Errorf("currStruct[role].GetEnum().Name != Aboba, got %v", roleEnum.Name) + } + + oper1, ok := currStruct.GetField("oper1") + if !ok { + t.Fatalf("currStruct[oper1] not found") + } + oper1Enum, ok := oper1.GetEnum() + if !ok { + t.Fatalf("currStruct[oper1].GetEnum() not successeded") + } + if oper1Enum.Value.Cmp(big.NewInt(0)) != 0 { + t.Errorf("currStruct[oper1].GetEnum().Value != 0, got %v", oper1Enum.Value.String()) + } + if oper1Enum.Name != "Add" { + t.Errorf("currStruct[oper1].GetEnum().Name != Add, got %v", oper1Enum.Name) + } + + oper2, ok := currStruct.GetField("oper2") + if !ok { + t.Fatalf("currStruct[oper2] not found") + } + oper2Enum, ok := oper2.GetEnum() + if !ok { + t.Fatalf("currStruct[oper2].GetEnum() not successeded") + } + if oper2Enum.Value.Cmp(big.NewInt(-10000)) != 0 { + t.Errorf("currStruct[oper2].GetEnum().Value != -10000, got %v", oper2Enum.Value.String()) + } + if oper2Enum.Name != "TopUp" { + t.Errorf("currStruct[oper2].GetEnum().Name != TopUp, got %v", oper2Enum.Name) + } + + oper3, ok := currStruct.GetField("oper3") + if !ok { + t.Fatalf("currStruct[oper3] not found") + } + oper3Enum, ok := oper3.GetEnum() + if !ok { + t.Fatalf("currStruct[oper3].GetEnum() not successeded") + } + if oper3Enum.Value.Cmp(big.NewInt(1)) != 0 { + t.Errorf("currStruct[oper3].GetEnum().Value != 1, got %v", oper3Enum.Value.String()) + } + if oper3Enum.Name != "Something" { + t.Errorf("currStruct[oper3].GetEnum().Name != Something, got %v", oper3Enum.Name) + } + + err = compareExpectedJson(jsonInputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalALotRefsFromStruct(t *testing.T) { + jsonInputFilename := "a_lot_refs_from_struct" + inputFilename := "testdata/refs.json" + data, err := os.ReadFile(inputFilename) + if err != nil { + t.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + t.Fatal(err) + } + + ty := parser.NewStructType("ManyRefsMsg") + + currCell, err := boc.DeserializeBocHex("b5ee9c724101040100b7000377deadbeef80107bfaaa5cc6e5368e5f9799188bd798cd22e04ab16d1d8ea4fc37480741e635087735940143ffffffffffffffffffffffffffff63c006010203004b80010df454cebee868f611ba8c0d4a9371fb73105396505783293a7625f75db3b9880bebc20100438006e05909e22b2e5e6087533314ee56505f85212914bd5547941a2a658ac62fe101004f801622753296a04942ce33ed2272651d6eb2b2d87144beb2051628dfd9b86c43bfcc12309ce54001e09a48b8") + if err != nil { + t.Fatal(err) + } + decoder := NewDecoder() + err = decoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + v, err := decoder.Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + currStruct, ok := v.GetStruct() + if !ok { + t.Fatalf("struct not found") + } + pref, ok := currStruct.GetPrefix() + if !ok { + t.Fatalf("currStruct.Prefix not found") + } + if pref.Len != 32 { + t.Errorf("pref.Len != 32, got %v", pref.Len) + } + if pref.Prefix != 0xdeadbeef { + t.Errorf("val.Prefix.Prefix != 0xdeadbeef, got %x", pref.Prefix) + } + + user1, ok := currStruct.GetField("user1") + if !ok { + t.Fatalf("currStruct[user1] not found") + } + user1Alias, ok := user1.GetAlias() + if !ok { + t.Fatalf("currStruct[user1].GetAlias() not found") + } + user1Val, ok := user1Alias.GetStruct() + if !ok { + t.Fatalf("currStruct[user1].GetStruct() not successeded") + } + + user1Addr, ok := user1Val.GetField("addr") + if !ok { + t.Fatalf("currStruct[user1][addr] not found") + } + user1AddrVal, ok := user1Addr.GetAddress() + if !ok { + t.Fatalf("currStruct[user1][addr].GetAddress() not successeded") + } + if user1AddrVal.ToRaw() != "0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8" { + t.Errorf("user1AddrVal.ToRaw() != 0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8, got %v", user1AddrVal.ToRaw()) + } + + user1Balance, ok := user1Val.GetField("balance") + if !ok { + t.Fatalf("currStruct[user1][balance] not found") + } + user1BalanceVal, ok := user1Balance.GetCoins() + if !ok { + t.Fatalf("currStruct[user1][balance].GetCoins() not successeded") + } + if user1BalanceVal.Cmp(big.NewInt(1_000_000_000)) != 0 { + t.Errorf("currStruct[user1][balance].GetCoins() != 1000000000, got %v", user1BalanceVal.String()) + } + + user2, ok := currStruct.GetField("user2") + if !ok { + t.Fatalf("currStruct[user2] not found") + } + user2Opt, ok := user2.GetOptionalValue() + if !ok { + t.Fatalf("currStruct[user2].GetOptionalValue() not successeded") + } + if !user2Opt.IsExists { + t.Errorf("currStruct[user2] is not exists") + } + user2Ref, ok := user2Opt.Val.GetRefValue() + if !ok { + t.Fatalf("currStruct[user2].GetRefValue() not successeded") + } + user2Alias, ok := user2Ref.GetAlias() + if !ok { + t.Fatalf("currStruct[user2].GetAlias() not found") + } + user2Val, ok := user2Alias.GetStruct() + if !ok { + t.Fatalf("currStruct[user2].GetStruct() not successeded") + } + + user2Addr, ok := user2Val.GetField("addr") + if !ok { + t.Fatalf("currStruct[user2][addr] not found") + } + user2AddrVal, ok := user2Addr.GetAddress() + if !ok { + t.Fatalf("currStruct[user2][addr].GetAddress() not successeded") + } + if user2AddrVal.ToRaw() != "0:086fa2a675f74347b08dd4606a549b8fdb98829cb282bc1949d3b12fbaed9dcc" { + t.Errorf("user1AddrVal.ToRaw() != 0:086fa2a675f74347b08dd4606a549b8fdb98829cb282bc1949d3b12fbaed9dcc, got %v", user2AddrVal.ToRaw()) + } + + user2Balance, ok := user2Val.GetField("balance") + if !ok { + t.Fatalf("currStruct[user2][balance] not found") + } + user2BalanceVal, ok := user2Balance.GetCoins() + if !ok { + t.Fatalf("currStruct[user2][balance].GetCoins() not successeded") + } + if user2BalanceVal.Cmp(big.NewInt(100_000_000)) != 0 { + t.Errorf("currStruct[user2][balance].GetCoins() != 100000000, got %v", user2BalanceVal.String()) + } + + user3, ok := currStruct.GetField("user3") + if !ok { + t.Fatalf("currStruct[user3] not found") + } + user3Val, ok := user3.GetCell() + if !ok { + t.Fatalf("currStruct[user3].GetCell() not successeded") + } + hs, err := user3Val.HashString() + if err != nil { + t.Fatal(err) + } + if hs != "47f4b117a301111ec48d763a3cd668a246c174efd2df9ba8bd1db406f017453a" { + t.Errorf("currStruct[user3][hashString].Hash != 47f4b117a301111ec48d763a3cd668a246c174efd2df9ba8bd1db406f017453a, got %v", hs) + } + + user4, ok := currStruct.GetField("user4") + if !ok { + t.Fatalf("currStruct[user4] not found") + } + user4Opt, ok := user4.GetOptionalValue() + if !ok { + t.Fatalf("currStruct[user4].GetOptionalValue() not successeded") + } + if user4Opt.IsExists { + t.Errorf("currStruct[user4] exists") + } + + user5, ok := currStruct.GetField("user5") + if !ok { + t.Fatalf("currStruct[user2] not found") + } + user5Ref, ok := user5.GetRefValue() + if !ok { + t.Fatalf("currStruct[user5].GetRefValue() not successeded") + } + user5Val, ok := user5Ref.GetStruct() + if !ok { + t.Fatalf("currStruct[user5].GetStruct() not successeded") + } + + user5Addr, ok := user5Val.GetField("addr") + if !ok { + t.Fatalf("currStruct[user5][addr] not found") + } + user5AddrVal, ok := user5Addr.GetAddress() + if !ok { + t.Fatalf("currStruct[user5][addr].GetAddress() not successeded") + } + if user5AddrVal.ToRaw() != "0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe" { + t.Errorf("user1AddrVal.ToRaw() != 0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe, got %v", user5AddrVal.ToRaw()) + } + + user5Balance, ok := user5Val.GetField("balance") + if !ok { + t.Fatalf("currStruct[user5][balance] not found") + } + user5BalanceVal, ok := user5Balance.GetCoins() + if !ok { + t.Fatalf("currStruct[user5][balance].GetCoins() not successeded") + } + if user5BalanceVal.Cmp(big.NewInt(10_000_000_000_000)) != 0 { + t.Errorf("currStruct[user5][balance].GetCoins() != 10000000000000, got %v", user5BalanceVal.String()) + } + + role, ok := currStruct.GetField("role") + if !ok { + t.Fatalf("currStruct[role] not found") + } + roleEnum, ok := role.GetEnum() + if !ok { + t.Fatalf("currStruct[role].GetEnum() not successeded") + } + if roleEnum.Value.Cmp(big.NewInt(1)) != 0 { + t.Errorf("currStruct[role].GetEnum().Value != 1, got %v", roleEnum.Value.String()) + } + if roleEnum.Name != "Aboba" { + t.Errorf("currStruct[role].GetEnum().Name != Aboba, got %v", roleEnum.Name) + } + + oper1, ok := currStruct.GetField("oper1") + if !ok { + t.Fatalf("currStruct[oper1] not found") + } + oper1Enum, ok := oper1.GetEnum() + if !ok { + t.Fatalf("currStruct[oper1].GetEnum() not successeded") + } + if oper1Enum.Value.Cmp(big.NewInt(0)) != 0 { + t.Errorf("currStruct[oper1].GetEnum().Value != 0, got %v", oper1Enum.Value.String()) + } + if oper1Enum.Name != "Add" { + t.Errorf("currStruct[oper1].GetEnum().Name != Add, got %v", oper1Enum.Name) + } + + oper2, ok := currStruct.GetField("oper2") + if !ok { + t.Fatalf("currStruct[oper2] not found") + } + oper2Enum, ok := oper2.GetEnum() + if !ok { + t.Fatalf("currStruct[oper2].GetEnum() not successeded") + } + if oper2Enum.Value.Cmp(big.NewInt(-10000)) != 0 { + t.Errorf("currStruct[oper2].GetEnum().Value != -10000, got %v", oper2Enum.Value.String()) + } + if oper2Enum.Name != "TopUp" { + t.Errorf("currStruct[oper2].GetEnum().Name != TopUp, got %v", oper2Enum.Name) + } + + oper3, ok := currStruct.GetField("oper3") + if !ok { + t.Fatalf("currStruct[oper3] not found") + } + oper3Enum, ok := oper3.GetEnum() + if !ok { + t.Fatalf("currStruct[oper3].GetEnum() not successeded") + } + if oper3Enum.Value.Cmp(big.NewInt(1)) != 0 { + t.Errorf("currStruct[oper3].GetEnum().Value != 1, got %v", oper3Enum.Value.String()) + } + if oper3Enum.Name != "Something" { + t.Errorf("currStruct[oper3].GetEnum().Name != Something, got %v", oper3Enum.Name) + } + + err = compareExpectedJson(jsonInputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalALotGenericsFromStruct(t *testing.T) { + jsonInputFilename := "a_lot_generics_from_struct" + inputFilename := "testdata/generics.json" + data, err := os.ReadFile(inputFilename) + if err != nil { + t.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + t.Fatal(err) + } + + ty := parser.NewStructType("ManyRefsMsg", parser.NewUIntNType(16)) + + currCell, err := boc.DeserializeBocHex("b5ee9c72410103010043000217d017d7840343b9aca0000108010200080000007b005543b9aca001017d78402005889d4ca5a81250b38cfb489c99475bacacb61c512fac81458a37f66e1b10eff422fc7647") + if err != nil { + t.Fatal(err) + } + decoder := NewDecoder() + err = decoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + v, err := decoder.Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + currStruct, ok := v.GetStruct() + if !ok { + t.Fatalf("struct not found") + } + + payloadV, ok := currStruct.GetField("payload") + if !ok { + t.Fatalf("currStruct[payload] not found") + } + payloadStruct, ok := payloadV.GetStruct() + if !ok { + t.Fatalf("currStruct[payload].GetStruct() not found") + } + payloadRef, ok := payloadStruct.GetField("value") + if !ok { + t.Fatalf("currStruct[payload][value] not found") + } + payloadUnion, ok := payloadRef.GetUnion() + if !ok { + t.Fatalf("currStruct[payload][value].GetUnion() not found") + } + payload, ok := payloadUnion.Val.GetRefValue() + if !ok { + t.Fatalf("currStruct[payload].GetRefValue() not successeded") + } + // todo: remove GetGeneric because its not convenient + payloadGeneric, ok := payload.GetGeneric() + if !ok { + t.Fatalf("currStruct[payload].GetGeneric() not found") + } + payloadVal, ok := payloadGeneric.GetSmallUInt() + if !ok { + t.Fatalf("currStruct[payload].GetSmallUInt() not found") + } + if payloadVal != 123 { + t.Errorf("currStruct[payload].GetSmallUInt() != 123, got %v", payloadVal) + } + + either, ok := currStruct.GetField("either") + if !ok { + t.Fatalf("currStruct[either] not found") + } + eitherAlias, ok := either.GetAlias() + if !ok { + t.Fatalf("currStruct[either].GetAlias() not found") + } + eitherUnion, ok := eitherAlias.GetUnion() + if !ok { + t.Fatalf("currStruct[either].GetUnion() not successeded") + } + eitherStruct, ok := eitherUnion.Val.GetStruct() + if !ok { + t.Fatalf("currStruct[either].GetStruct() not successeded") + } + eitherV, ok := eitherStruct.GetField("value") + if !ok { + t.Fatalf("currStruct[either][value] not successeded") + } + eitherValGeneric, ok := eitherV.GetGeneric() + if !ok { + t.Fatalf("currStruct[either][value].GetGeneric() not found") + } + eitherVal, ok := eitherValGeneric.GetCoins() + if !ok { + t.Fatalf("currStruct[either][value].GetCoins() not successeded") + } + if eitherVal.Cmp(big.NewInt(100000000)) != 0 { + t.Fatalf("currStruct[either][value].GetCoins() != 1000000000, got %v", eitherVal.String()) + } + + anotherEither, ok := currStruct.GetField("anotherEither") + if !ok { + t.Fatalf("currStruct[anotherEither] not found") + } + anotherEitherAlias, ok := anotherEither.GetAlias() + if !ok { + t.Fatalf("currStruct[anotherEither].GetAlias() not found") + } + anotherEitherAliasAlias, ok := anotherEitherAlias.GetAlias() + if !ok { + t.Fatalf("currStruct[anotherEither].GetAlias().GetAlias() not found") + } + anotherEitherUnion, ok := anotherEitherAliasAlias.GetUnion() + if !ok { + t.Fatalf("currStruct[anotherEither].GetUnion() not successeded") + } + anotherEitherStruct, ok := anotherEitherUnion.Val.GetStruct() + if !ok { + t.Fatalf("currStruct[anotherEither].GetStruct() not successeded") + } + anotherEitherV, ok := anotherEitherStruct.GetField("value") + if !ok { + t.Fatalf("currStruct[anotherEither][value] not successeded") + } + anotherEitherVGeneric, ok := anotherEitherV.GetGeneric() + if !ok { + t.Fatalf("currStruct[anotherEither][value].GetGeneric() not found") + } + anotherEitherVal, ok := anotherEitherVGeneric.GetTensor() + if !ok { + t.Fatalf("currStruct[anotherEither][value].GetTensor() not successeded") + } + + anotherEitherValBool, ok := anotherEitherVal[0].GetBool() + if !ok { + t.Fatalf("currStruct[anotherEither][value][0].GetBool() not successeded") + } + if !anotherEitherValBool { + t.Fatalf("currStruct[anotherEither][value][0].GetBool() is false") + } + anotherEitherValCoins, ok := anotherEitherVal[1].GetCoins() + if !ok { + t.Fatalf("currStruct[anotherEither][value][0].GetCoins() not successeded") + } + if anotherEitherValCoins.Cmp(big.NewInt(1_000_000_000)) != 0 { + t.Fatalf("currStruct[anotherEither][value][0].GetCoins() != 1000000000, got %v", anotherEitherValCoins.String()) + } + + doubler, ok := currStruct.GetField("doubler") + if !ok { + t.Fatalf("currStruct[doubler] not found") + } + doublerRef, ok := doubler.GetRefValue() + if !ok { + t.Fatalf("currStruct[doubler].GetRefValue() not successeded") + } + doublerRefAlias, ok := doublerRef.GetAlias() + if !ok { + t.Fatalf("currStruct[doubler].GetAlias() not found") + } + doublerTensor, ok := doublerRefAlias.GetTensor() + if !ok { + t.Fatalf("currStruct[doubler].GetTensor() not successeded") + } + + doublerGenric0, ok := doublerTensor[0].GetGeneric() + if !ok { + t.Fatalf("currStruct[doubler][0].GetGeneric() not successeded") + } + doublerTensor0, ok := doublerGenric0.GetTensor() + if !ok { + t.Fatalf("currStruct[doubler][0].GetTensor() not successeded") + } + doublerTensor0Coins, ok := doublerTensor0[0].GetCoins() + if !ok { + t.Fatalf("currStruct[doubler][0][0].GetCoins() not successeded") + } + if doublerTensor0Coins.Cmp(big.NewInt(1_000_000_000)) != 0 { + t.Fatalf("currStruct[doubler][0][0].GetBigInt() != 1000000000, got %v", doublerTensor0Coins.String()) + } + + doublerTensor0Addr, ok := doublerTensor0[1].GetOptionalAddress() + if !ok { + t.Fatalf("currStruct[doubler][0][1].GetOptionalAddress() not successeded") + } + if doublerTensor0Addr.SumType != "NoneAddress" { + t.Fatalf("currStruct[doubler][0][1].GetOptionalAddress() != NoneAddress") + } + + doublerUnion1, ok := doublerTensor[1].GetGeneric() + if !ok { + t.Fatalf("currStruct[doubler][1].GetGeneric() not successeded") + } + doublerTensor1, ok := doublerUnion1.GetTensor() + if !ok { + t.Fatalf("currStruct[doubler][1] not successeded") + } + doublerTensor1Coins, ok := doublerTensor1[0].GetCoins() + if !ok { + t.Fatalf("currStruct[doubler][1][0].GetCoins() not successeded") + } + if doublerTensor1Coins.Cmp(big.NewInt(100_000_000)) != 0 { + t.Fatalf("currStruct[doubler][1][0].GetCoins() != 100000000, got %v", doublerTensor1Coins.String()) + } + + doublerTensor1Addr, ok := doublerTensor1[1].GetOptionalAddress() + if !ok { + t.Fatalf("currStruct[doubler][1][1].GetOptionalAddress() not successeded") + } + if doublerTensor1Addr.SumType != "InternalAddress" { + t.Fatalf("currStruct[doubler][1][1].GetOptionalAddress() != InternalAddress") + } + if doublerTensor1Addr.InternalAddress.ToRaw() != "0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe" { + t.Fatalf("currStruct[doubler][1][1] != 0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe, got %v", doublerTensor1Addr.InternalAddress.ToRaw()) + } + + myVal, ok := currStruct.GetField("myVal") + if !ok { + t.Fatalf("currStruct[myVal] not found") + } + myValAlias, ok := myVal.GetAlias() + if !ok { + t.Fatalf("currStruct[myVal].GetAlias() not found") + } + myValGeneric, ok := myValAlias.GetGeneric() + if !ok { + t.Fatalf("currStruct[myVal].GetGeneric() not found") + } + myValVal, ok := myValGeneric.GetSmallUInt() + if !ok { + t.Fatalf("currStruct[myVal].GetSmallUInt() not successed") + } + if myValVal != 16 { + t.Fatalf("currStruct[myVal] != 16, got %v", myValVal) + } + + err = compareExpectedJson(jsonInputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalALotGenericsFromAlias(t *testing.T) { + jsonInputFilename := "a_lot_generics_from_alias" + inputFilename := "testdata/generics.json" + data, err := os.ReadFile(inputFilename) + if err != nil { + t.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + t.Fatal(err) + } + + ty := parser.NewAliasType("GoodNamingForMsg", parser.NewUIntNType(16)) + + currCell, err := boc.DeserializeBocHex("b5ee9c72410103010043000217d017d7840343b9aca0000108010200080000007b005543b9aca001017d78402005889d4ca5a81250b38cfb489c99475bacacb61c512fac81458a37f66e1b10eff422fc7647") + if err != nil { + t.Fatal(err) + } + decoder := NewDecoder() + err = decoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + v, err := decoder.Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + currAlias, ok := v.GetAlias() + if !ok { + t.Fatalf("v.GetAlias() not found") + } + currStruct, ok := currAlias.GetStruct() + if !ok { + t.Fatalf("struct not found") + } + + payloadV, ok := currStruct.GetField("payload") + if !ok { + t.Fatalf("currStruct[payload] not found") + } + payloadStruct, ok := payloadV.GetStruct() + if !ok { + t.Fatalf("currStruct[payload].GetStruct() not found") + } + payloadRef, ok := payloadStruct.GetField("value") + if !ok { + t.Fatalf("currStruct[payload][value] not found") + } + payloadUnion, ok := payloadRef.GetUnion() + if !ok { + t.Fatalf("currStruct[payload][value].GetUnion() not found") + } + payload, ok := payloadUnion.Val.GetRefValue() + if !ok { + t.Fatalf("currStruct[payload].GetRefValue() not successeded") + } + // todo: remove GetGeneric because its not convenient + payloadGeneric, ok := payload.GetGeneric() + if !ok { + t.Fatalf("currStruct[payload].GetGeneric() not found") + } + payloadVal, ok := payloadGeneric.GetSmallUInt() + if !ok { + t.Fatalf("currStruct[payload].GetSmallUInt() not found") + } + if payloadVal != 123 { + t.Errorf("currStruct[payload].GetSmallUInt() != 123, got %v", payloadVal) + } + + either, ok := currStruct.GetField("either") + if !ok { + t.Fatalf("currStruct[either] not found") + } + eitherAlias, ok := either.GetAlias() + if !ok { + t.Fatalf("currStruct[either].GetAlias() not found") + } + eitherUnion, ok := eitherAlias.GetUnion() + if !ok { + t.Fatalf("currStruct[either].GetUnion() not successeded") + } + eitherStruct, ok := eitherUnion.Val.GetStruct() + if !ok { + t.Fatalf("currStruct[either].GetStruct() not successeded") + } + eitherV, ok := eitherStruct.GetField("value") + if !ok { + t.Fatalf("currStruct[either][value] not successeded") + } + eitherValGeneric, ok := eitherV.GetGeneric() + if !ok { + t.Fatalf("currStruct[either][value].GetGeneric() not found") + } + eitherVal, ok := eitherValGeneric.GetCoins() + if !ok { + t.Fatalf("currStruct[either][value].GetCoins() not successeded") + } + if eitherVal.Cmp(big.NewInt(100000000)) != 0 { + t.Fatalf("currStruct[either][value].GetCoins() != 1000000000, got %v", eitherVal.String()) + } + + anotherEither, ok := currStruct.GetField("anotherEither") + if !ok { + t.Fatalf("currStruct[anotherEither] not found") + } + anotherEitherAlias, ok := anotherEither.GetAlias() + if !ok { + t.Fatalf("currStruct[anotherEither].GetAlias() not found") + } + anotherEitherAliasAlias, ok := anotherEitherAlias.GetAlias() + if !ok { + t.Fatalf("currStruct[anotherEither].GetAlias().GetAlias() not found") + } + anotherEitherUnion, ok := anotherEitherAliasAlias.GetUnion() + if !ok { + t.Fatalf("currStruct[anotherEither].GetUnion() not successeded") + } + anotherEitherStruct, ok := anotherEitherUnion.Val.GetStruct() + if !ok { + t.Fatalf("currStruct[anotherEither].GetStruct() not successeded") + } + anotherEitherV, ok := anotherEitherStruct.GetField("value") + if !ok { + t.Fatalf("currStruct[anotherEither][value] not successeded") + } + anotherEitherVGeneric, ok := anotherEitherV.GetGeneric() + if !ok { + t.Fatalf("currStruct[anotherEither][value].GetGeneric() not found") + } + anotherEitherVal, ok := anotherEitherVGeneric.GetTensor() + if !ok { + t.Fatalf("currStruct[anotherEither][value].GetTensor() not successeded") + } + + anotherEitherValBool, ok := anotherEitherVal[0].GetBool() + if !ok { + t.Fatalf("currStruct[anotherEither][value][0].GetBool() not successeded") + } + if !anotherEitherValBool { + t.Fatalf("currStruct[anotherEither][value][0].GetBool() is false") + } + anotherEitherValCoins, ok := anotherEitherVal[1].GetCoins() + if !ok { + t.Fatalf("currStruct[anotherEither][value][0].GetCoins() not successeded") + } + if anotherEitherValCoins.Cmp(big.NewInt(1_000_000_000)) != 0 { + t.Fatalf("currStruct[anotherEither][value][0].GetCoins() != 1000000000, got %v", anotherEitherValCoins.String()) + } + + doubler, ok := currStruct.GetField("doubler") + if !ok { + t.Fatalf("currStruct[doubler] not found") + } + doublerRef, ok := doubler.GetRefValue() + if !ok { + t.Fatalf("currStruct[doubler].GetRefValue() not successeded") + } + doublerRefAlias, ok := doublerRef.GetAlias() + if !ok { + t.Fatalf("currStruct[doubler].GetAlias() not found") + } + doublerTensor, ok := doublerRefAlias.GetTensor() + if !ok { + t.Fatalf("currStruct[doubler].GetTensor() not successeded") + } + + doublerGenric0, ok := doublerTensor[0].GetGeneric() + if !ok { + t.Fatalf("currStruct[doubler][0].GetGeneric() not successeded") + } + doublerTensor0, ok := doublerGenric0.GetTensor() + if !ok { + t.Fatalf("currStruct[doubler][0].GetTensor() not successeded") + } + doublerTensor0Coins, ok := doublerTensor0[0].GetCoins() + if !ok { + t.Fatalf("currStruct[doubler][0][0].GetCoins() not successeded") + } + if doublerTensor0Coins.Cmp(big.NewInt(1_000_000_000)) != 0 { + t.Fatalf("currStruct[doubler][0][0].GetBigInt() != 1000000000, got %v", doublerTensor0Coins.String()) + } + + doublerTensor0Addr, ok := doublerTensor0[1].GetOptionalAddress() + if !ok { + t.Fatalf("currStruct[doubler][0][1].GetOptionalAddress() not successeded") + } + if doublerTensor0Addr.SumType != "NoneAddress" { + t.Fatalf("currStruct[doubler][0][1].GetOptionalAddress() != NoneAddress") + } + + doublerUnion1, ok := doublerTensor[1].GetGeneric() + if !ok { + t.Fatalf("currStruct[doubler][1].GetGeneric() not successeded") + } + doublerTensor1, ok := doublerUnion1.GetTensor() + if !ok { + t.Fatalf("currStruct[doubler][1] not successeded") + } + doublerTensor1Coins, ok := doublerTensor1[0].GetCoins() + if !ok { + t.Fatalf("currStruct[doubler][1][0].GetCoins() not successeded") + } + if doublerTensor1Coins.Cmp(big.NewInt(100_000_000)) != 0 { + t.Fatalf("currStruct[doubler][1][0].GetCoins() != 100000000, got %v", doublerTensor1Coins.String()) + } + + doublerTensor1Addr, ok := doublerTensor1[1].GetOptionalAddress() + if !ok { + t.Fatalf("currStruct[doubler][1][1].GetOptionalAddress() not successeded") + } + if doublerTensor1Addr.SumType != "InternalAddress" { + t.Fatalf("currStruct[doubler][1][1].GetOptionalAddress() != InternalAddress") + } + if doublerTensor1Addr.InternalAddress.ToRaw() != "0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe" { + t.Fatalf("currStruct[doubler][1][1] != 0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe, got %v", doublerTensor1Addr.InternalAddress.ToRaw()) + } + + myVal, ok := currStruct.GetField("myVal") + if !ok { + t.Fatalf("currStruct[myVal] not found") + } + myValAlias, ok := myVal.GetAlias() + if !ok { + t.Fatalf("currStruct[myVal].GetAlias() not found") + } + myValGeneric, ok := myValAlias.GetGeneric() + if !ok { + t.Fatalf("currStruct[myVal].GetGeneric() not found") + } + myValVal, ok := myValGeneric.GetSmallUInt() + if !ok { + t.Fatalf("currStruct[myVal].GetSmallUInt() not successed") + } + if myValVal != 16 { + t.Fatalf("currStruct[myVal] != 16, got %v", myValVal) + } + + err = compareExpectedJson(jsonInputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalStructWithDefaultValues(t *testing.T) { + jsonInputFilename := "a_lot_generics_with_default_values" + inputFilename := "testdata/default_values.json" + data, err := os.ReadFile(inputFilename) + if err != nil { + t.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + t.Fatal(err) + } + + ty := parser.NewStructType("DefaultTest") + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101003100005d80000002414801622753296a04942ce33ed2272651d6eb2b2d87144beb2051628dfd9b86c43bfd00000156ac2c4c70811a9dde") + if err != nil { + t.Fatal(err) + } + decoder := NewDecoder() + err = decoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + v, err := decoder.Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + currStruct, ok := v.GetStruct() + if !ok { + t.Fatalf("struct not found") + } + + optNum1, ok := currStruct.GetField("num1") + if !ok { + t.Fatalf("currStruct[num1] not found") + } + num1, ok := optNum1.GetOptionalValue() + if !ok { + t.Fatalf("currStruct[num1].GetOptionalValue() not successeded") + } + if !num1.IsExists { + t.Fatalf("currStruct[num1] is not exists") + } + num1Val, ok := num1.Val.GetSmallUInt() + if !ok { + t.Fatalf("currStruct[num1].GetSmallUInt() not successeded") + } + if num1Val != 4 { + t.Fatalf("currStruct[num1].GetSmallUInt() != 4, got %v", num1Val) + } + + optNum2, ok := currStruct.GetField("num2") + if !ok { + t.Fatalf("currStruct[num2] not found") + } + num2, ok := optNum2.GetOptionalValue() + if !ok { + t.Fatalf("currStruct[num2].GetOptionalValue() not successeded") + } + if !num2.IsExists { + t.Fatalf("currStruct[num2] is not exists") + } + num2Alias, ok := num2.Val.GetAlias() + if !ok { + t.Fatalf("currStruct[num2].GetAlias() not found") + } + num2Val, ok := num2Alias.GetSmallInt() + if !ok { + t.Fatalf("currStruct[num2].GetSmallInt() not successeded") + } + if num2Val != 5 { + t.Fatalf("currStruct[num2].GetSmallInt() != 5, got %v", num2Val) + } + + optSlice3, ok := currStruct.GetField("slice3") + if !ok { + t.Fatalf("currStruct[slice3] not found") + } + slice3, ok := optSlice3.GetOptionalValue() + if !ok { + t.Fatalf("currStruct[slice3].GetOptionalValue() not successeded") + } + if !slice3.IsExists { + t.Fatalf("currStruct[slice3] is not exists") + } + slice3Val, ok := slice3.Val.GetRemaining() + if !ok { + t.Fatalf("currStruct[slice3].GetRemaining() not successeded") + } + hs, err := slice3Val.HashString() + if err != nil { + t.Fatal(err) + } + if hs != "55e960f1409af0d7670e382c61276a559fa9330185984d91faffebf32d5fa383" { + t.Fatalf("currStruct[slice3].GetRemaining().Hash() != 55e960f1409af0d7670e382c61276a559fa9330185984d91faffebf32d5fa383, got %v", hs) + } + + optAddr4, ok := currStruct.GetField("addr4") + if !ok { + t.Fatalf("currStruct[addr4] not found") + } + addr4, ok := optAddr4.GetOptionalAddress() + if !ok { + t.Fatalf("currStruct[addr4].GetOptionalAddress() not successeded") + } + if addr4.SumType != "NoneAddress" { + t.Fatalf("currStruct[addr4].GetOptionalAddress() != NoneAddress") + } + + optAddr5, ok := currStruct.GetField("addr5") + if !ok { + t.Fatalf("currStruct[addr5] not found") + } + addr5, ok := optAddr5.GetOptionalAddress() + if !ok { + t.Fatalf("currStruct[addr5].GetOptionalAddress() not successeded") + } + if addr5.SumType != "InternalAddress" { + t.Fatalf("currStruct[addr5].GetOptionalAddress() != InternalAddress") + } + if addr5.InternalAddress.ToRaw() != "0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe" { + t.Fatalf("currStruct[addr5].GetOptionalAddress() != 0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe, got %v", addr5.InternalAddress.ToRaw()) + } + + optTensor6, ok := currStruct.GetField("tensor6") + if !ok { + t.Fatalf("currStruct[tensor6] not found") + } + tensor6, ok := optTensor6.GetOptionalValue() + if !ok { + t.Fatalf("currStruct[tensor6].GetOptionalValue() not successeded") + } + if !tensor6.IsExists { + t.Fatalf("currStruct[tensor6] is not exists") + } + tensor6Val, ok := tensor6.Val.GetTensor() + if !ok { + t.Fatalf("currStruct[tensor6].GetTensor() not successeded") + } + tensor6Val0, ok := tensor6Val[0].GetSmallInt() + if !ok { + t.Fatalf("currStruct[tensor6][0].GetSmallInt() not successed") + } + if tensor6Val0 != 342 { + t.Fatalf("currStruct[tensor6][0] != 342, got %v", tensor6Val0) + } + + tensor6Val1, ok := tensor6Val[1].GetBool() + if !ok { + t.Fatalf("currStruct[tensor6][1].GetBool() not successed") + } + if !tensor6Val1 { + t.Fatalf("currStruct[tensor6][0] is false") + } + + optNum7, ok := currStruct.GetField("num7") + if !ok { + t.Fatalf("currStruct[num7] not found") + } + num7, ok := optNum7.GetOptionalValue() + if !ok { + t.Fatalf("currStruct[num7].GetOptionalValue() not successeded") + } + if num7.IsExists { + t.Fatalf("currStruct[num7] exists") + } + + err = compareExpectedJson(jsonInputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalALotNumbers(t *testing.T) { + jsonInputFilename := "a_lot_numbers" + inputFilename := "testdata/numbers.json" + data, err := os.ReadFile(inputFilename) + if err != nil { + t.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + t.Fatal(err) + } + + ty := parser.NewStructType("Numbers") + + currCell, err := boc.DeserializeBocHex("b5ee9c72410101010033000062000000000000000000000000000000000000000000000000000000000000000000000000000000f1106aecc4c800020926dc62f014") + if err != nil { + t.Fatal(err) + } + decoder := NewDecoder() + err = decoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + v, err := decoder.Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + currStruct, ok := v.GetStruct() + if !ok { + t.Fatalf("struct not found") + } + num1, ok := currStruct.GetField("num1") + if !ok { + t.Fatalf("num1 not found") + } + val1, ok := num1.GetSmallUInt() + if !ok { + t.Fatalf("num1.GetSmallUInt() not successeded") + } + if val1 != 0 { + t.Fatalf("num1 != 0, got %v", val1) + } + + num3, ok := currStruct.GetField("num3") + if !ok { + t.Fatalf("num3 not found") + } + val3, ok := num3.GetBigUInt() + if !ok { + t.Fatalf("num3.GetBigUInt() not successeded") + } + if val3.Cmp(big.NewInt(241)) != 0 { + t.Fatalf("num3 != 241, got %v", val3.String()) + } + + num4, ok := currStruct.GetField("num4") + if !ok { + t.Fatalf("num4 not found") + } + val4, ok := num4.GetVarUInt() + if !ok { + t.Fatalf("num4.GetVarUInt() not successeded") + } + if val4.Cmp(big.NewInt(3421)) != 0 { + t.Fatalf("num4 != 3421, got %s", val4.String()) + } + + num5, ok := currStruct.GetField("num5") + if !ok { + t.Fatalf("num5 not found") + } + val5, ok := num5.GetBool() + if !ok { + t.Fatalf("num5.GetBool() not successeded") + } + if !val5 { + t.Fatalf("num5 != true") + } + + num7, ok := currStruct.GetField("num7") + if !ok { + t.Fatalf("num7 not found") + } + val7, ok := num7.GetBits() + if !ok { + t.Fatalf("num7.GetBits() not successeded") + } + if !bytes.Equal(val7.Buffer(), []byte{49, 50}) { + t.Fatalf("num7 != \"12\", got %v", val7.Buffer()) + } + + num8, ok := currStruct.GetField("num8") + if !ok { + t.Fatalf("num8 not found") + } + val8, ok := num8.GetSmallInt() + if !ok { + t.Fatalf("num8.GetSmallInt() not successeded") + } + if val8 != 0 { + t.Fatalf("num8 != 0, got %v", val8) + } + + num9, ok := currStruct.GetField("num9") + if !ok { + t.Fatalf("num9 not found") + } + val9, ok := num9.GetVarInt() + if !ok { + t.Fatalf("num9.GetVarInt() not successeded") + } + if val9.Cmp(big.NewInt(2342)) != 0 { + t.Fatalf("num9 != 2342, got %s", val9.String()) + } + + err = compareExpectedJson(jsonInputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalALotRandomFields(t *testing.T) { + jsonInputFilename := "a_lot_random_fields" + inputFilename := "testdata/random_fields.json" + data, err := os.ReadFile(inputFilename) + if err != nil { + t.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + t.Fatal(err) + } + + ty := parser.NewStructType("RandomFields") + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010301007800028b79480107bfaaa5cc6e5368e5f9799188bd798cd22e04ab16d1d8ea4fc37480741e6350e038d7eb37c5e80000000ab50ee6b28000000000000016e4c000006c175300001801bc01020001c00051000000000005120041efeaa9731b94da397e5e64622f5e63348b812ac5b4763a93f0dd201d0798d4409e337ceb") + if err != nil { + t.Fatal(err) + } + decoder := NewDecoder() + err = decoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + v, err := decoder.Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + addr := ton.MustParseAccountID("UQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqEBI") + + currStruct, ok := v.GetStruct() + if !ok { + t.Fatalf("struct not found") + } + pref, ok := currStruct.GetPrefix() + if !ok { + t.Fatalf("struct prefix not found") + } + if pref.Len != 12 { + t.Fatalf("pref.Len != 12, got %d", pref.Len) + } + if pref.Prefix != 1940 { + t.Fatalf("struct prefix != 1940, got %d", pref) + } + + destInt, ok := currStruct.GetField("dest_int") + if !ok { + t.Fatalf("dest_int not found") + } + destIntVal, ok := destInt.GetAddress() + if !ok { + t.Fatalf("num1.GetAddress() not successeded") + } + if destIntVal.ToRaw() != addr.ToRaw() { + t.Fatalf("destInt != UQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqEBI") + } + + amount, ok := currStruct.GetField("amount") + if !ok { + t.Fatalf("amount not found") + } + amountVal, ok := amount.GetCoins() + if !ok { + t.Fatalf("amount.GetCoins() not successeded") + } + expectedAmount, ok := big.NewInt(0).SetString("500000123400000", 10) + if !ok { + t.Fatalf("cannot set 500000123400000 value to big.Int") + } + if amountVal.Cmp(expectedAmount) != 0 { + t.Fatalf("amount != 500000123400000, got %s", amountVal.String()) + } + + destExt, ok := currStruct.GetField("dest_ext") + if !ok { + t.Fatalf("num4 dest_ext found") + } + destExtVal, ok := destExt.GetAnyAddress() + if !ok { + t.Fatalf("num4.GetAnyAddress() not successeded") + } + if destExtVal.SumType != "NoneAddress" { + t.Fatalf("destExt != a none address") + } + + intVector, ok := currStruct.GetField("intVector") + if !ok { + t.Fatalf("intVector not found") + } + intVectorVal, ok := intVector.GetTensor() + if !ok { + t.Fatalf("num5.GetTensor() not successeded") + } + val1, ok := intVectorVal[0].GetSmallInt() + if !ok { + t.Fatalf("intVector[0].GetSmallInt() not successeded") + } + if val1 != 342 { + t.Fatalf("intVector[0].GetSmallInt() != 342, got %v", val1) + } + + optVal2, ok := intVectorVal[1].GetOptionalValue() + if !ok { + t.Fatalf("intVector[1].GetOptionalValue() not successeded") + } + if !optVal2.IsExists { + t.Fatalf("intVector[1].GetOptionalValue() != exists") + } + val2, ok := optVal2.Val.GetCoins() + if !ok { + t.Fatalf("intVector[1].GetOptionalValue().GetCoins() not successeded") + } + if val2.Cmp(big.NewInt(1000000000)) != 0 { + t.Fatalf("intVector[1].GetOptionalValue().GetCoins() != 1000000000, got %v", val1) + } + + val3, ok := intVectorVal[2].GetSmallUInt() + if !ok { + t.Fatalf("intVector[2].GetSmallUInt() not successeded") + } + if val3 != 23443 { + t.Fatalf("intVector[2].GetSmallUInt() != 23443, got %v", val1) + } + + needsMoreRef, ok := currStruct.GetField("needs_more") + if !ok { + t.Fatalf("needs_more not found") + } + needsMore, ok := needsMoreRef.GetRefValue() + if !ok { + t.Fatalf("needsMoreRef.GetRefValue() not successeded") + } + needsMoreVal, ok := needsMore.GetBool() + if !ok { + t.Fatalf("needsMore.GetBool() not successeded") + } + if !needsMoreVal { + t.Fatalf("needsMore != true") + } + + somePayload, ok := currStruct.GetField("some_payload") + if !ok { + t.Fatalf("some_payload not found") + } + somePayloadVal, ok := somePayload.GetCell() + if !ok { + t.Fatalf("num8.GetCell() not successeded") + } + somePayloadHash, err := somePayloadVal.HashString() + if err != nil { + t.Fatalf("somePayload.HashString() not successeded") + } + if somePayloadHash != "f2017ee9d429c16689ba2243d26d2a070a1e8e4a6106cee2129a049deee727d9" { + t.Fatalf("somePayloadHash != f2017ee9d429c16689ba2243d26d2a070a1e8e4a6106cee2129a049deee727d9, got %v", somePayloadHash) + } + + myInt, ok := currStruct.GetField("my_int") + if !ok { + t.Fatalf("my_int not found") + } + myIntAlias, ok := myInt.GetAlias() + if !ok { + t.Fatalf("my_int.GetAlias() not successeded") + } + myIntVal, ok := myIntAlias.GetSmallInt() + if !ok { + t.Fatalf("my_int.GetSmallInt() not successeded") + } + if myIntVal != 432 { + t.Fatalf("my_int != 432, got %v", myIntVal) + } + + someUnion, ok := currStruct.GetField("some_union") + if !ok { + t.Fatalf("my_int not found") + } + someUnionVal, ok := someUnion.GetUnion() + if !ok { + t.Fatalf("someUnion.GetSmallInt() not successeded") + } + unionVal, ok := someUnionVal.Val.GetSmallInt() + if !ok { + t.Fatalf("someUnion.GetSmallInt() not successeded") + } + if unionVal != 30000 { + t.Fatalf("some_union != 30000, got %v", someUnionVal) + } + + default1, ok := currStruct.GetField("default_1") + if !ok { + t.Fatalf("default_1 not found") + } + default1Val, ok := default1.GetSmallInt() + if !ok { + t.Fatalf("default1.GetSmallInt() not successeded") + } + if default1Val != 1 { + t.Fatalf("default1 != 1, got %v", default1Val) + } + + optDefault2, ok := currStruct.GetField("default_2") + if !ok { + t.Fatalf("default_2 not found") + } + default2, ok := optDefault2.GetOptionalValue() + if !ok { + t.Fatalf("default2.GetOptionalValue() not successeded") + } + if !default2.IsExists { + t.Fatalf("default2.GetOptionalValue() != exists") + } + default2Val, ok := default2.Val.GetSmallInt() + if !ok { + t.Fatalf("default2.GetSmallInt() not successeded") + } + if default2Val != 55 { + t.Fatalf("default2 != 55, got %v", default2Val) + } + + err = compareExpectedJson(jsonInputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_UnmarshalAliasWithCustomUnpack(t *testing.T) { + jsonInputFilename := "custom_pack_unpack" + inputFilename := "testdata/custom_pack_unpack.json" + data, err := os.ReadFile(inputFilename) + if err != nil { + t.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + t.Fatal(err) + } + + ty := parser.NewAliasType("MyAlias") + + currCell, err := boc.DeserializeBocHex("b5ee9c724101010100470000890000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043b9aca00886e91196") + if err != nil { + t.Fatal(err) + } + decoder := NewDecoder() + err = decoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + decoder.WithCustomUnpackResolver(func(alias parser.AliasRef, cell *boc.Cell, value *AliasValue) error { + err := cell.Skip(512) + if err != nil { + return fmt.Errorf("failed to 512 bits from alias") + } + val, err := decoder.Unmarshal(cell, parser.NewStructType("My")) + if err != nil { + return fmt.Errorf("failed to unmarshal alias' coins") + } + *value = AliasValue(*val) + return nil + }) + v, err := decoder.Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + currAlias, ok := v.GetAlias() + if !ok { + t.Fatalf("alias not found") + } + currStruct, ok := currAlias.GetStruct() + if !ok { + t.Fatalf("num1 not found") + } + amount, ok := currStruct.GetField("amount") + if !ok { + t.Fatalf("struct[amount] not found") + } + amountVal := amount.MustGetCoins() + if amountVal.Cmp(big.NewInt(1_000_000_000)) != 0 { + t.Fatalf("amount != 1_000_000_000, got %v", amountVal) + } + + err = compareExpectedJson(jsonInputFilename, *v) + if err != nil { + t.Fatal(err) + } +} + +func TestRuntime_MarshalSmallInt(t *testing.T) { + ty := parser.NewIntNType(24) + + currCell, err := boc.DeserializeBocHex("b5ee9c72410101010005000006ff76c41616db06") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalSmallUInt(t *testing.T) { + ty := parser.NewUIntNType(53) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000900000d00000000001d34e435eafd") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalBigInt(t *testing.T) { + ty := parser.NewIntNType(183) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101001900002dfffffffffffffffffffffffffffffffffff99bfeac6423a6f0b50c") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalBigUInt(t *testing.T) { + ty := parser.NewUIntNType(257) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101002300004100000000000000000000000000000000000000000000000000009fc4212a38ba40b11cce12") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalVarInt(t *testing.T) { + ty := parser.NewVarInt16Type() + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000600000730c98588449b6923") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalVarUInt(t *testing.T) { + ty := parser.NewVarUInt32Type() + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000800000b28119ab36b44d3a86c0f") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalBits(t *testing.T) { + ty := parser.NewBitsNType(24) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000500000631323318854035") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalCoins(t *testing.T) { + ty := parser.NewCoinsType() + + currCell, err := boc.DeserializeBocHex("b5ee9c72410101010007000009436ec6e0189ebbd7f4") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalBool(t *testing.T) { + ty := parser.NewBoolType() + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000300000140f6d24034") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalCell(t *testing.T) { + ty := parser.NewCellType() + + currCell, err := boc.DeserializeBocHex("b5ee9c724101020100090001000100080000007ba52a3292") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalRemaining(t *testing.T) { + ty := parser.NewRemainingType() + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000900000dc0800000000ab8d04726e4") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalAddress(t *testing.T) { + ty := parser.NewAddressType() + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101002400004380107bfaaa5cc6e5368e5f9799188bd798cd22e04ab16d1d8ea4fc37480741e6351064a3e1a6") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalNotExitsOptionalAddress(t *testing.T) { + ty := parser.NewAddressOptType() + + currCell, err := boc.DeserializeBocHex("b5ee9c724101010100030000012094418655") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalExistsOptionalAddress(t *testing.T) { + ty := parser.NewAddressOptType() + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101002400004380107bfaaa5cc6e5368e5f9799188bd798cd22e04ab16d1d8ea4fc37480741e6351064a3e1a6") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalExternalAddress(t *testing.T) { + ty := parser.NewAddressExtType() + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000600000742082850fcbd94fd") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalAnyNoneAddress(t *testing.T) { + ty := parser.NewAddressAnyType() + + currCell, err := boc.DeserializeBocHex("b5ee9c724101010100030000012094418655") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalAnyInternalAddress(t *testing.T) { + ty := parser.NewAddressAnyType() + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101002400004380107bfaaa5cc6e5368e5f9799188bd798cd22e04ab16d1d8ea4fc37480741e6351064a3e1a6") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalAnyExternalAddress(t *testing.T) { + ty := parser.NewAddressAnyType() + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000600000742082850fcbd94fd") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalAnyVarAddress(t *testing.T) { + ty := parser.NewAddressAnyType() + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000900000dc0800000000ab8d04726e4") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalNotExistsNullable(t *testing.T) { + ty := parser.NewNullableType(parser.NewRemainingType()) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000300000140f6d24034") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalExistsNullable(t *testing.T) { + ty := parser.NewNullableType(parser.NewCellType()) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010201000b000101c001000900000c0ae007880db9") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalRef(t *testing.T) { + ty := parser.NewCellOfType(parser.NewIntNType(65)) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010201000e000100010011000000000009689e40e150b4c5") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalEmptyTensor(t *testing.T) { + ty := parser.NewTensorType() + + currCell, err := boc.DeserializeBocHex("b5ee9c724101010100020000004cacb9cd") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalTensor(t *testing.T) { + ty := parser.NewTensorType( + parser.NewUIntNType(123), + parser.NewBoolType(), + parser.NewCoinsType(), + parser.NewTensorType( + parser.NewIntNType(23), + parser.NewNullableType(parser.NewIntNType(2)), + ), + parser.NewVarIntType(32), + ) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101001f00003900000000000000000000000000021cb43b9aca00fffd550bfbaae07401a2a98117") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalIntKeyMap(t *testing.T) { + ty := parser.NewMapType(parser.NewIntNType(32), parser.NewBoolType()) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010201000c000101c001000ba00000007bc09a662c32") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalUIntKeyMap(t *testing.T) { + ty := parser.NewMapType(parser.NewUIntNType(16), parser.NewAddressType()) + + currCell, err := boc.DeserializeBocHex("b5ee9c72410104010053000101c0010202cb02030045a7400b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe80045a3cff5555555555555555555555555555555555555555555555555555555555555555888440ce8") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Fatal(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalBigIntKeyMap(t *testing.T) { + ty := parser.NewMapType(parser.NewUIntNType(78), parser.NewCellType()) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010301001a000101c0010115a70000000000000047550902000b000000001ab01d5bf1a9") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalBitsKeyMap(t *testing.T) { + ty := parser.NewMapType( + parser.NewBitsNType(16), + parser.NewMapType( + parser.NewIntNType(64), + parser.NewTensorType( + parser.NewAddressType(), + parser.NewCoinsType(), + ), + ), + ) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010301003b000101c0010106a0828502005ea0000000000000003e400b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe43b9aca00b89cdc86") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalAddressKeyMap(t *testing.T) { + ty := parser.NewMapType(parser.NewAddressType(), parser.NewCoinsType()) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010201002f000101c0010051a17002c44ea652d4092859c67da44e4ca3add6565b0e2897d640a2c51bfb370d8877f9409502f9002016fdc16e") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalUnionWithDecPrefix(t *testing.T) { + ty := parser.NewUnionType( + 1, true, + parser.NewUnionVariant(parser.NewIntNType(16), "0"), + parser.NewUnionVariant(parser.NewIntNType(128), "1"), + ) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101001300002180000000000000000000000003b5577dc0660d6029") + if err != nil { + t.Fatal(err) + } + v, err := Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + newCell, err := Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalUnionWithBinPrefix(t *testing.T) { + inputFilename := "testdata/bin_union.json" + data, err := os.ReadFile(inputFilename) + if err != nil { + t.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + t.Fatal(err) + } + + ty := parser.NewUnionType( + 3, false, + parser.NewUnionVariant(parser.NewStructType("AddressWithPrefix"), "0b001"), + parser.NewUnionVariant(parser.NewStructType("MapWithPrefix"), "0b011"), + parser.NewUnionVariant(parser.NewStructType("CellWithPrefix"), "0b111"), + ) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010201002e0001017801004fa17002c44ea652d4092859c67da44e4ca3add6565b0e2897d640a2c51bfb370d8877f900a4d89920c413c650") + if err != nil { + t.Fatal(err) + } + decoder := NewDecoder() + err = decoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + v, err := decoder.Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + encoder := NewEncoder() + err = encoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + newCell, err := encoder.Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalUnionWithHexPrefix(t *testing.T) { + inputFilename := "testdata/hex_union.json" + data, err := os.ReadFile(inputFilename) + if err != nil { + t.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + t.Fatal(err) + } + + ty := parser.NewUnionType( + 32, false, + parser.NewUnionVariant(parser.NewStructType("UInt66WithPrefix"), "0x12345678"), + parser.NewUnionVariant(parser.NewStructType("UInt33WithPrefix"), "0xdeadbeef"), + parser.NewUnionVariant(parser.NewStructType("UInt4WithPrefix"), "0x89abcdef"), + ) + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101000b000011deadbeef00000000c0d75977b9") + if err != nil { + t.Fatal(err) + } + decoder := NewDecoder() + err = decoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + v, err := decoder.Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + encoder := NewEncoder() + err = encoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + newCell, err := encoder.Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalALotRefsFromAlias(t *testing.T) { + inputFilename := "testdata/refs.json" + data, err := os.ReadFile(inputFilename) + if err != nil { + t.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + t.Fatal(err) + } + + ty := parser.NewAliasType("GoodNamingForMsg") + + currCell, err := boc.DeserializeBocHex("b5ee9c724101040100b7000377deadbeef80107bfaaa5cc6e5368e5f9799188bd798cd22e04ab16d1d8ea4fc37480741e635087735940143ffffffffffffffffffffffffffff63c006010203004b80010df454cebee868f611ba8c0d4a9371fb73105396505783293a7625f75db3b9880bebc20100438006e05909e22b2e5e6087533314ee56505f85212914bd5547941a2a658ac62fe101004f801622753296a04942ce33ed2272651d6eb2b2d87144beb2051628dfd9b86c43bfcc12309ce54001e09a48b8") + if err != nil { + t.Fatal(err) + } + decoder := NewDecoder() + err = decoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + v, err := decoder.Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + encoder := NewEncoder() + err = encoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + newCell, err := encoder.Marshal(v, ty) + if err != nil { + t.Fatal(err) + } + + cb := currCell[0].Refs()[0].RawBitString() + fmt.Println(cb.BinaryString()) + + nb := newCell.Refs()[0].RawBitString() + fmt.Println(nb.BinaryString()) + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalALotRefsFromStruct(t *testing.T) { + inputFilename := "testdata/refs.json" + data, err := os.ReadFile(inputFilename) + if err != nil { + t.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + t.Fatal(err) + } + ty := parser.NewStructType("ManyRefsMsg") + + currCell, err := boc.DeserializeBocHex("b5ee9c724101040100b7000377deadbeef80107bfaaa5cc6e5368e5f9799188bd798cd22e04ab16d1d8ea4fc37480741e635087735940143ffffffffffffffffffffffffffff63c006010203004b80010df454cebee868f611ba8c0d4a9371fb73105396505783293a7625f75db3b9880bebc20100438006e05909e22b2e5e6087533314ee56505f85212914bd5547941a2a658ac62fe101004f801622753296a04942ce33ed2272651d6eb2b2d87144beb2051628dfd9b86c43bfcc12309ce54001e09a48b8") + if err != nil { + t.Fatal(err) + } + decoder := NewDecoder() + err = decoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + v, err := decoder.Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + encoder := NewEncoder() + err = encoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + newCell, err := encoder.Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalALotGenericsFromStruct(t *testing.T) { + inputFilename := "testdata/generics.json" + data, err := os.ReadFile(inputFilename) + if err != nil { + t.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + t.Fatal(err) + } + + ty := parser.NewStructType("ManyRefsMsg", parser.NewUIntNType(16)) + + currCell, err := boc.DeserializeBocHex("b5ee9c72410103010043000217d017d7840343b9aca0000108010200080000007b005543b9aca001017d78402005889d4ca5a81250b38cfb489c99475bacacb61c512fac81458a37f66e1b10eff422fc7647") + if err != nil { + t.Fatal(err) + } + decoder := NewDecoder() + err = decoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + v, err := decoder.Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + encoder := NewEncoder() + err = encoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + newCell, err := encoder.Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalALotGenericsFromAlias(t *testing.T) { + inputFilename := "testdata/generics.json" + data, err := os.ReadFile(inputFilename) + if err != nil { + t.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + t.Fatal(err) + } + + ty := parser.NewAliasType("GoodNamingForMsg", parser.NewUIntNType(16)) + + currCell, err := boc.DeserializeBocHex("b5ee9c72410103010043000217d017d7840343b9aca0000108010200080000007b005543b9aca001017d78402005889d4ca5a81250b38cfb489c99475bacacb61c512fac81458a37f66e1b10eff422fc7647") + if err != nil { + t.Fatal(err) + } + decoder := NewDecoder() + err = decoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + v, err := decoder.Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + encoder := NewEncoder() + err = encoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + newCell, err := encoder.Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalStructWithDefaultValues(t *testing.T) { + inputFilename := "testdata/default_values.json" + data, err := os.ReadFile(inputFilename) + if err != nil { + t.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + t.Fatal(err) + } + + ty := parser.NewStructType("DefaultTest") + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010101003100005d80000002414801622753296a04942ce33ed2272651d6eb2b2d87144beb2051628dfd9b86c43bfd00000156ac2c4c70811a9dde") + if err != nil { + t.Fatal(err) + } + decoder := NewDecoder() + err = decoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + v, err := decoder.Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + encoder := NewEncoder() + err = encoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + newCell, err := encoder.Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalALotNumbers(t *testing.T) { + inputFilename := "testdata/numbers.json" + data, err := os.ReadFile(inputFilename) + if err != nil { + t.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + t.Fatal(err) + } + + ty := parser.NewStructType("Numbers") + + currCell, err := boc.DeserializeBocHex("b5ee9c72410101010033000062000000000000000000000000000000000000000000000000000000000000000000000000000000f1106aecc4c800020926dc62f014") + if err != nil { + t.Fatal(err) + } + decoder := NewDecoder() + err = decoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + v, err := decoder.Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + encoder := NewEncoder() + err = encoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + newCell, err := encoder.Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalALotRandomFields(t *testing.T) { + inputFilename := "testdata/random_fields.json" + data, err := os.ReadFile(inputFilename) + if err != nil { + t.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + t.Fatal(err) + } + + ty := parser.NewStructType("RandomFields") + + currCell, err := boc.DeserializeBocHex("b5ee9c7241010301007800028b79480107bfaaa5cc6e5368e5f9799188bd798cd22e04ab16d1d8ea4fc37480741e6350e038d7eb37c5e80000000ab50ee6b28000000000000016e4c000006c175300001801bc01020001c00051000000000005120041efeaa9731b94da397e5e64622f5e63348b812ac5b4763a93f0dd201d0798d4409e337ceb") + if err != nil { + t.Fatal(err) + } + decoder := NewDecoder() + err = decoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + v, err := decoder.Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + encoder := NewEncoder() + err = encoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + newCell, err := encoder.Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func TestRuntime_MarshalAliasWithCustomUnpack(t *testing.T) { + inputFilename := "testdata/custom_pack_unpack.json" + data, err := os.ReadFile(inputFilename) + if err != nil { + t.Fatal(err) + } + + var abi parser.ABI + err = json.Unmarshal(data, &abi) + if err != nil { + t.Fatal(err) + } + + ty := parser.NewAliasType("MyAlias") + + currCell, err := boc.DeserializeBocHex("b5ee9c724101010100470000890000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043b9aca00886e91196") + if err != nil { + t.Fatal(err) + } + decoder := NewDecoder() + err = decoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + decoder.WithCustomUnpackResolver(func(alias parser.AliasRef, cell *boc.Cell, value *AliasValue) error { + err := cell.Skip(512) + if err != nil { + return fmt.Errorf("failed to 512 bits from alias") + } + val, err := decoder.Unmarshal(cell, parser.NewStructType("My")) + if err != nil { + return fmt.Errorf("failed to unmarshal alias' coins") + } + *value = AliasValue(*val) + return nil + }) + v, err := decoder.Unmarshal(currCell[0], ty) + if err != nil { + t.Fatal(err) + } + + encoder := NewEncoder() + err = encoder.WithABIs(abi) + if err != nil { + t.Fatal(err) + } + encoder.WithCustomPackResolver(func(ref parser.AliasRef, cell *boc.Cell, value *AliasValue) error { + err := cell.WriteUint(0, 256) + if err != nil { + return fmt.Errorf("failed to write 256 bits to alias") + } + err = cell.WriteUint(0, 256) + if err != nil { + return fmt.Errorf("failed to write 256 bits to alias again") + } + val := Value(*value) + cl, err := encoder.Marshal(&val, parser.NewStructType("My")) + if err != nil { + return fmt.Errorf("failed to marshal alias' coins") + } + err = cell.WriteBitString(cl.ReadRemainingBits()) + if err != nil { + return fmt.Errorf("failed to write marshalled bits to alias") + } + for _, clRef := range cl.Refs() { + err = cell.AddRef(clRef) + if err != nil { + return fmt.Errorf("failed to add ref to alias") + } + } + return nil + }) + newCell, err := encoder.Marshal(v, ty) + if err != nil { + t.Error(err) + } + + oldHs, err := currCell[0].HashString() + if err != nil { + t.Fatal(err) + } + newHs, err := newCell.HashString() + if err != nil { + t.Fatal(err) + } + if oldHs != newHs { + t.Errorf("input and output cells are different") + } + +} + +func compareExpectedJson(inputFilename string, v Value) error { + val := struct { + Value Value `json:"value"` + }{ + Value: v, + } + pathPrefix := jsonFilesPath + inputFilename + actual, err := json.MarshalIndent(val, "", " ") + if err != nil { + return err + } + err = os.WriteFile(pathPrefix+".output.json", actual, os.ModePerm) + if err != nil { + return err + } + expected, err := os.ReadFile(pathPrefix + ".json") + if err != nil { + return err + } + if !bytes.Equal(actual, expected) { + return fmt.Errorf("%s got different results", pathPrefix) + } + return nil +} diff --git a/tolk/testdata/bin_union.json b/tolk/testdata/bin_union.json new file mode 100644 index 00000000..8d25bb9d --- /dev/null +++ b/tolk/testdata/bin_union.json @@ -0,0 +1,72 @@ +{ + "contractName": "Test", + "declarations": [ + { + "kind": "Struct", + "name": "AddressWithPrefix", + "prefix": { + "prefixStr": "0b001", + "prefixLen": 3 + }, + "fields": [ + { + "name": "v", + "ty": { + "kind": "address" + } + } + ] + }, + { + "kind": "Struct", + "name": "MapWithPrefix", + "prefix": { + "prefixStr": "0b011", + "prefixLen": 3 + }, + "fields": [ + { + "name": "v", + "ty": { + "kind": "mapKV", + "k": { + "kind": "address" + }, + "v": { + "kind": "coins" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "CellWithPrefix", + "prefix": { + "prefixStr": "0b111", + "prefixLen": 3 + }, + "fields": [ + { + "name": "v", + "ty": { + "kind": "cell" + } + } + ] + } + ], + "incomingMessages": [], + "outgoingMessages": [], + "emittedEvents": [], + "storage": { + "storageTy": { + "kind": "nullLiteral" + } + }, + "getMethods": [], + "thrownErrors": [], + "compilerName": "tolk", + "compilerVersion": "1.2.0", + "codeBoc64": "te6ccgEBAgEAFQABFP8A9KQT9LzyyAsBAAzTMPiR8kA=" +} \ No newline at end of file diff --git a/tolk/testdata/custom_pack_unpack.json b/tolk/testdata/custom_pack_unpack.json new file mode 100644 index 00000000..be9bfcab --- /dev/null +++ b/tolk/testdata/custom_pack_unpack.json @@ -0,0 +1,40 @@ +{ + "contractName": "Test", + "declarations": [ + { + "kind": "Struct", + "name": "My", + "fields": [ + { + "name": "amount", + "ty": { + "kind": "coins" + } + } + ] + }, + { + "kind": "Alias", + "name": "MyAlias", + "targetTy": { + "kind": "StructRef", + "structName": "My" + }, + "customPackToBuilder": true, + "customUnpackFromSlice": true + } + ], + "incomingMessages": [], + "outgoingMessages": [], + "emittedEvents": [], + "storage": { + "storageTy": { + "kind": "nullLiteral" + } + }, + "getMethods": [], + "thrownErrors": [], + "compilerName": "tolk", + "compilerVersion": "1.2.0", + "codeBoc64": "te6ccgEBAgEAFQABFP8A9KQT9LzyyAsBAAzTMPiR8kA=" +} \ No newline at end of file diff --git a/tolk/testdata/default_values.json b/tolk/testdata/default_values.json new file mode 100644 index 00000000..02b781dc --- /dev/null +++ b/tolk/testdata/default_values.json @@ -0,0 +1,143 @@ +{ + "contractName": "Test", + "declarations": [ + { + "kind": "Alias", + "name": "Int8", + "targetTy": { + "kind": "intN", + "n": 8 + } + }, + { + "kind": "Alias", + "name": "Addr", + "targetTy": { + "kind": "address" + } + }, + { + "kind": "Struct", + "name": "DefaultTest", + "fields": [ + { + "name": "num1", + "ty": { + "kind": "nullable", + "inner": { + "kind": "uintN", + "n": 32 + } + }, + "defaultValue": { + "kind": "int", + "v": "4" + } + }, + { + "name": "num2", + "ty": { + "kind": "nullable", + "inner": { + "kind": "AliasRef", + "aliasName": "Int8" + } + }, + "defaultValue": { + "kind": "int", + "v": "5" + } + }, + { + "name": "addr4", + "ty": { + "kind": "addressOpt" + }, + "defaultValue": { + "kind": "null" + } + }, + { + "name": "addr5", + "ty": { + "kind": "addressOpt" + }, + "defaultValue": { + "kind": "address", + "addr": "0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe" + } + }, + { + "name": "tensor6", + "ty": { + "kind": "nullable", + "inner": { + "kind": "tensor", + "items": [ + { + "kind": "intN", + "n": 32 + }, + { + "kind": "bool" + } + ] + } + }, + "defaultValue": { + "kind": "tensor", + "items": [ + { + "kind": "int", + "v": "342" + }, + { + "kind": "bool", + "v": true + } + ] + } + }, + { + "name": "num7", + "ty": { + "kind": "nullable", + "inner": { + "kind": "intN", + "n": 128 + } + }, + "defaultValue": { + "kind": "null" + } + }, + { + "name": "slice3", + "ty": { + "kind": "nullable", + "inner": { + "kind": "remaining" + } + }, + "defaultValue": { + "kind": "slice", + "hex": "616263" + } + } + ] + } + ], + "incomingMessages": [], + "outgoingMessages": [], + "emittedEvents": [], + "storage": { + "storageTy": { + "kind": "nullLiteral" + } + }, + "getMethods": [], + "thrownErrors": [], + "compilerName": "tolk", + "compilerVersion": "1.2.0", + "codeBoc64": "te6ccgEBAgEAFQABFP8A9KQT9LzyyAsBAAzTMPiR8kA=" +} \ No newline at end of file diff --git a/tolk/testdata/generics.json b/tolk/testdata/generics.json new file mode 100644 index 00000000..deb2d0c3 --- /dev/null +++ b/tolk/testdata/generics.json @@ -0,0 +1,297 @@ +{ + "contractName": "Test", + "declarations": [ + { + "kind": "Alias", + "name": "Wrapper", + "targetTy": { + "kind": "genericT", + "nameT": "W" + }, + "typeParams": [ + "W" + ] + }, + { + "kind": "Struct", + "name": "EitherLeft", + "typeParams": [ + "R" + ], + "fields": [ + { + "name": "value", + "ty": { + "kind": "genericT", + "nameT": "R" + } + } + ] + }, + { + "kind": "Struct", + "name": "EitherRight", + "typeParams": [ + "T" + ], + "fields": [ + { + "name": "value", + "ty": { + "kind": "genericT", + "nameT": "T" + } + } + ] + }, + { + "kind": "Alias", + "name": "Either", + "targetTy": { + "kind": "union", + "variants": [ + { + "prefixStr": "0", + "prefixLen": 1, + "prefixEatInPlace": true, + "variantTy": { + "kind": "StructRef", + "structName": "EitherLeft", + "typeArgs": [ + { + "kind": "genericT", + "nameT": "L" + } + ] + } + }, + { + "prefixStr": "1", + "prefixLen": 1, + "prefixEatInPlace": true, + "variantTy": { + "kind": "StructRef", + "structName": "EitherRight", + "typeArgs": [ + { + "kind": "genericT", + "nameT": "R" + } + ] + } + } + ] + }, + "typeParams": [ + "L", + "R" + ] + }, + { + "kind": "Struct", + "name": "EitherRef", + "typeParams": [ + "I" + ], + "fields": [ + { + "name": "value", + "ty": { + "kind": "union", + "variants": [ + { + "prefixStr": "0", + "prefixLen": 1, + "prefixEatInPlace": true, + "variantTy": { + "kind": "genericT", + "nameT": "I" + } + }, + { + "prefixStr": "1", + "prefixLen": 1, + "prefixEatInPlace": true, + "variantTy": { + "kind": "cellOf", + "inner": { + "kind": "genericT", + "nameT": "I" + } + } + } + ] + } + } + ] + }, + { + "kind": "Alias", + "name": "EitherAlias", + "targetTy": { + "kind": "AliasRef", + "aliasName": "Either", + "typeArgs": [ + { + "kind": "genericT", + "nameT": "R" + }, + { + "kind": "genericT", + "nameT": "L" + } + ] + }, + "typeParams": [ + "R", + "L" + ] + }, + { + "kind": "Alias", + "name": "Doubler", + "targetTy": { + "kind": "tensor", + "items": [ + { + "kind": "genericT", + "nameT": "U" + }, + { + "kind": "genericT", + "nameT": "U" + } + ] + }, + "typeParams": [ + "U" + ] + }, + { + "kind": "Struct", + "name": "ManyRefsMsg", + "typeParams": [ + "T" + ], + "fields": [ + { + "name": "payload", + "ty": { + "kind": "StructRef", + "structName": "EitherRef", + "typeArgs": [ + { + "kind": "uintN", + "n": 32 + } + ] + } + }, + { + "name": "either", + "ty": { + "kind": "AliasRef", + "aliasName": "Either", + "typeArgs": [ + { + "kind": "address" + }, + { + "kind": "coins" + } + ] + } + }, + { + "name": "anotherEither", + "ty": { + "kind": "AliasRef", + "aliasName": "EitherAlias", + "typeArgs": [ + { + "kind": "bool" + }, + { + "kind": "tensor", + "items": [ + { + "kind": "bool" + }, + { + "kind": "coins" + } + ] + } + ] + } + }, + { + "name": "doubler", + "ty": { + "kind": "cellOf", + "inner": { + "kind": "AliasRef", + "aliasName": "Doubler", + "typeArgs": [ + { + "kind": "tensor", + "items": [ + { + "kind": "coins" + }, + { + "kind": "addressOpt" + } + ] + } + ] + } + } + }, + { + "name": "myVal", + "ty": { + "kind": "AliasRef", + "aliasName": "Wrapper", + "typeArgs": [ + { + "kind": "genericT", + "nameT": "T" + } + ] + } + } + ] + }, + { + "kind": "Alias", + "name": "GoodNamingForMsg", + "targetTy": { + "kind": "StructRef", + "structName": "ManyRefsMsg", + "typeArgs": [ + { + "kind": "genericT", + "nameT": "T" + } + ] + }, + "typeParams": [ + "T" + ] + } + ], + "incomingMessages": [], + "outgoingMessages": [], + "emittedEvents": [], + "storage": { + "storageTy": { + "kind": "nullLiteral" + } + }, + "getMethods": [], + "thrownErrors": [], + "compilerName": "tolk", + "compilerVersion": "1.2.0", + "codeBoc64": "te6ccgEBAgEAFQABFP8A9KQT9LzyyAsBAAzTMPiR8kA=" +} \ No newline at end of file diff --git a/tolk/testdata/hex_union.json b/tolk/testdata/hex_union.json new file mode 100644 index 00000000..7b7b4120 --- /dev/null +++ b/tolk/testdata/hex_union.json @@ -0,0 +1,69 @@ +{ + "contractName": "Test", + "declarations": [ + { + "kind": "Struct", + "name": "UInt66WithPrefix", + "prefix": { + "prefixStr": "0x12345678", + "prefixLen": 32 + }, + "fields": [ + { + "name": "v", + "ty": { + "kind": "uintN", + "n": 66 + } + } + ] + }, + { + "kind": "Struct", + "name": "UInt33WithPrefix", + "prefix": { + "prefixStr": "0xdeadbeef", + "prefixLen": 32 + }, + "fields": [ + { + "name": "v", + "ty": { + "kind": "uintN", + "n": 33 + } + } + ] + }, + { + "kind": "Struct", + "name": "UInt4WithPrefix", + "prefix": { + "prefixStr": "0x89abcdef", + "prefixLen": 32 + }, + "fields": [ + { + "name": "v", + "ty": { + "kind": "uintN", + "n": 4 + } + } + ] + } + ], + "incomingMessages": [], + "outgoingMessages": [], + "emittedEvents": [], + "storage": { + "storageTy": { + "kind": "nullLiteral" + } + }, + "getMethods": [], + "thrownErrors": [], + "compilerName": "tolk", + "compilerVersion": "1.2.0", + "codeBoc64": "te6ccgEBAgEAFQABFP8A9KQT9LzyyAsBAAzTMPiR8kA=" +} \ No newline at end of file diff --git a/tolk/testdata/jetton_transfer.json b/tolk/testdata/jetton_transfer.json new file mode 100644 index 00000000..56311fca --- /dev/null +++ b/tolk/testdata/jetton_transfer.json @@ -0,0 +1,75 @@ +{ + "contractName": "Jetton", + "declarations": [ + { + "kind": "Struct", + "name": "Transfer", + "prefix": { + "prefixStr": "0x0f8a7ea5", + "prefixLen": 32 + }, + "fields": [ + { + "name": "queryId", + "ty": { + "kind": "uintN", + "n": 64 + } + }, + { + "name": "amount", + "ty": { + "kind": "coins" + } + }, + { + "name": "destination", + "ty": { + "kind": "address" + } + }, + { + "name": "responseDestination", + "ty": { + "kind": "address" + } + }, + { + "name": "customPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "forwardAmount", + "ty": { + "kind": "coins" + } + }, + { + "name": "forwardPayload", + "isPayload": true, + "ty": { + "kind": "remaining" + } + } + ] + } + ], + "incomingMessages": [], + "outgoingMessages": [], + "emittedEvents": [], + "storage": { + "storageTy": { + "kind": "nullLiteral" + } + }, + "getMethods": [], + "thrownErrors": [], + "compilerName": "tolk", + "compilerVersion": "1.2.0", + "codeBoc64": "te6ccgEBAgEAFQABFP8A9KQT9LzyyAsBAAzTMPiR8kA=" +} \ No newline at end of file diff --git a/tolk/testdata/json/a_lot_generics_from_alias.json b/tolk/testdata/json/a_lot_generics_from_alias.json new file mode 100755 index 00000000..2d54a0fc --- /dev/null +++ b/tolk/testdata/json/a_lot_generics_from_alias.json @@ -0,0 +1,27 @@ +{ + "value": { + "payload": { + "value": 123 + }, + "either": { + "value": "100000000" + }, + "anotherEither": { + "value": [ + true, + "1000000000" + ] + }, + "doubler": [ + [ + "1000000000", + "" + ], + [ + "100000000", + "0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe" + ] + ], + "myVal": 16 + } +} \ No newline at end of file diff --git a/tolk/testdata/json/a_lot_generics_from_struct.json b/tolk/testdata/json/a_lot_generics_from_struct.json new file mode 100755 index 00000000..2d54a0fc --- /dev/null +++ b/tolk/testdata/json/a_lot_generics_from_struct.json @@ -0,0 +1,27 @@ +{ + "value": { + "payload": { + "value": 123 + }, + "either": { + "value": "100000000" + }, + "anotherEither": { + "value": [ + true, + "1000000000" + ] + }, + "doubler": [ + [ + "1000000000", + "" + ], + [ + "100000000", + "0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe" + ] + ], + "myVal": 16 + } +} \ No newline at end of file diff --git a/tolk/testdata/json/a_lot_generics_with_default_values.json b/tolk/testdata/json/a_lot_generics_with_default_values.json new file mode 100755 index 00000000..a54dc88b --- /dev/null +++ b/tolk/testdata/json/a_lot_generics_with_default_values.json @@ -0,0 +1,28 @@ +{ + "value": { + "num1": { + "isExists": true, + "value": 4 + }, + "num2": { + "isExists": true, + "value": 5 + }, + "addr4": "", + "addr5": "0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe", + "tensor6": { + "isExists": true, + "value": [ + 342, + true + ] + }, + "num7": { + "isExists": false + }, + "slice3": { + "isExists": true, + "value": "b5ee9c72010101010005000006616263" + } + } +} \ No newline at end of file diff --git a/tolk/testdata/json/a_lot_numbers.json b/tolk/testdata/json/a_lot_numbers.json new file mode 100755 index 00000000..af649dd6 --- /dev/null +++ b/tolk/testdata/json/a_lot_numbers.json @@ -0,0 +1,11 @@ +{ + "value": { + "num1": 0, + "num3": "241", + "num4": "3421", + "num5": true, + "num7": "3132", + "num8": 0, + "num9": "2342" + } +} \ No newline at end of file diff --git a/tolk/testdata/json/a_lot_random_fields.json b/tolk/testdata/json/a_lot_random_fields.json new file mode 100755 index 00000000..a8ee2b73 --- /dev/null +++ b/tolk/testdata/json/a_lot_random_fields.json @@ -0,0 +1,24 @@ +{ + "value": { + "dest_int": "0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8", + "amount": "500000123400000", + "dest_ext": "", + "intVector": [ + 342, + { + "isExists": true, + "value": "1000000000" + }, + 23443 + ], + "needs_more": true, + "some_payload": "b5ee9c7201010101002b000051000000000005120041efeaa9731b94da397e5e64622f5e63348b812ac5b4763a93f0dd201d0798d440", + "my_int": 432, + "some_union": 30000, + "default_1": 1, + "default_2": { + "isExists": true, + "value": 55 + } + } +} \ No newline at end of file diff --git a/tolk/testdata/json/a_lot_refs_from_alias.json b/tolk/testdata/json/a_lot_refs_from_alias.json new file mode 100755 index 00000000..00319064 --- /dev/null +++ b/tolk/testdata/json/a_lot_refs_from_alias.json @@ -0,0 +1,27 @@ +{ + "value": { + "user1": { + "addr": "0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8", + "balance": "1000000000" + }, + "user2": { + "isExists": true, + "value": { + "addr": "0:086fa2a675f74347b08dd4606a549b8fdb98829cb282bc1949d3b12fbaed9dcc", + "balance": "100000000" + } + }, + "user3": "b5ee9c720101010100240000438006e05909e22b2e5e6087533314ee56505f85212914bd5547941a2a658ac62fe101", + "user4": { + "isExists": false + }, + "user5": { + "addr": "0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe", + "balance": "10000000000000" + }, + "role": "Aboba", + "oper1": "Add", + "oper2": "TopUp", + "oper3": "Something" + } +} \ No newline at end of file diff --git a/tolk/testdata/json/a_lot_refs_from_struct.json b/tolk/testdata/json/a_lot_refs_from_struct.json new file mode 100755 index 00000000..00319064 --- /dev/null +++ b/tolk/testdata/json/a_lot_refs_from_struct.json @@ -0,0 +1,27 @@ +{ + "value": { + "user1": { + "addr": "0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8", + "balance": "1000000000" + }, + "user2": { + "isExists": true, + "value": { + "addr": "0:086fa2a675f74347b08dd4606a549b8fdb98829cb282bc1949d3b12fbaed9dcc", + "balance": "100000000" + } + }, + "user3": "b5ee9c720101010100240000438006e05909e22b2e5e6087533314ee56505f85212914bd5547941a2a658ac62fe101", + "user4": { + "isExists": false + }, + "user5": { + "addr": "0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe", + "balance": "10000000000000" + }, + "role": "Aboba", + "oper1": "Add", + "oper2": "TopUp", + "oper3": "Something" + } +} \ No newline at end of file diff --git a/tolk/testdata/json/address_key_map.json b/tolk/testdata/json/address_key_map.json new file mode 100755 index 00000000..4bf2f6df --- /dev/null +++ b/tolk/testdata/json/address_key_map.json @@ -0,0 +1,5 @@ +{ + "value": { + "0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe": "10000000000" + } +} \ No newline at end of file diff --git a/tolk/testdata/json/any_external_address.json b/tolk/testdata/json/any_external_address.json new file mode 100755 index 00000000..087c7896 --- /dev/null +++ b/tolk/testdata/json/any_external_address.json @@ -0,0 +1,3 @@ +{ + "value": "4142" +} \ No newline at end of file diff --git a/tolk/testdata/json/any_internal_address.json b/tolk/testdata/json/any_internal_address.json new file mode 100755 index 00000000..447ee205 --- /dev/null +++ b/tolk/testdata/json/any_internal_address.json @@ -0,0 +1,3 @@ +{ + "value": "0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8" +} \ No newline at end of file diff --git a/tolk/testdata/json/any_none_address.json b/tolk/testdata/json/any_none_address.json new file mode 100755 index 00000000..90bee0a3 --- /dev/null +++ b/tolk/testdata/json/any_none_address.json @@ -0,0 +1,3 @@ +{ + "value": "" +} \ No newline at end of file diff --git a/tolk/testdata/json/any_var_address.json b/tolk/testdata/json/any_var_address.json new file mode 100755 index 00000000..5b416318 --- /dev/null +++ b/tolk/testdata/json/any_var_address.json @@ -0,0 +1,3 @@ +{ + "value": "0:AB" +} \ No newline at end of file diff --git a/tolk/testdata/json/big_int.json b/tolk/testdata/json/big_int.json new file mode 100755 index 00000000..562c45b5 --- /dev/null +++ b/tolk/testdata/json/big_int.json @@ -0,0 +1,3 @@ +{ + "value": "-3513294376431" +} \ No newline at end of file diff --git a/tolk/testdata/json/big_int_key_map.json b/tolk/testdata/json/big_int_key_map.json new file mode 100755 index 00000000..4134f51e --- /dev/null +++ b/tolk/testdata/json/big_int_key_map.json @@ -0,0 +1,5 @@ +{ + "value": { + "2337412": "b5ee9c7201010101000800000b000000001ab0" + } +} \ No newline at end of file diff --git a/tolk/testdata/json/big_uint.json b/tolk/testdata/json/big_uint.json new file mode 100755 index 00000000..d2b3b1df --- /dev/null +++ b/tolk/testdata/json/big_uint.json @@ -0,0 +1,3 @@ +{ + "value": "351329437643124" +} \ No newline at end of file diff --git a/tolk/testdata/json/bits.json b/tolk/testdata/json/bits.json new file mode 100755 index 00000000..21020d3c --- /dev/null +++ b/tolk/testdata/json/bits.json @@ -0,0 +1,3 @@ +{ + "value": "313233" +} \ No newline at end of file diff --git a/tolk/testdata/json/bits_int_key_map.json b/tolk/testdata/json/bits_int_key_map.json new file mode 100755 index 00000000..2f21c450 --- /dev/null +++ b/tolk/testdata/json/bits_int_key_map.json @@ -0,0 +1,10 @@ +{ + "value": { + "4142": { + "124": [ + "0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe", + "1000000000" + ] + } + } +} \ No newline at end of file diff --git a/tolk/testdata/json/bool.json b/tolk/testdata/json/bool.json new file mode 100755 index 00000000..78211002 --- /dev/null +++ b/tolk/testdata/json/bool.json @@ -0,0 +1,3 @@ +{ + "value": false +} \ No newline at end of file diff --git a/tolk/testdata/json/cell.json b/tolk/testdata/json/cell.json new file mode 100755 index 00000000..4cb88fe4 --- /dev/null +++ b/tolk/testdata/json/cell.json @@ -0,0 +1,3 @@ +{ + "value": "b5ee9c720101010100060000080000007b" +} \ No newline at end of file diff --git a/tolk/testdata/json/coins.json b/tolk/testdata/json/coins.json new file mode 100755 index 00000000..0ccf8f03 --- /dev/null +++ b/tolk/testdata/json/coins.json @@ -0,0 +1,3 @@ +{ + "value": "921464321" +} \ No newline at end of file diff --git a/tolk/testdata/json/custom_pack_unpack.json b/tolk/testdata/json/custom_pack_unpack.json new file mode 100755 index 00000000..190bf906 --- /dev/null +++ b/tolk/testdata/json/custom_pack_unpack.json @@ -0,0 +1,5 @@ +{ + "value": { + "amount": "1000000000" + } +} \ No newline at end of file diff --git a/tolk/testdata/json/empty_tensor.json b/tolk/testdata/json/empty_tensor.json new file mode 100755 index 00000000..bcd37241 --- /dev/null +++ b/tolk/testdata/json/empty_tensor.json @@ -0,0 +1,3 @@ +{ + "value": [] +} \ No newline at end of file diff --git a/tolk/testdata/json/exists_nullable.json b/tolk/testdata/json/exists_nullable.json new file mode 100755 index 00000000..b6eef495 --- /dev/null +++ b/tolk/testdata/json/exists_nullable.json @@ -0,0 +1,6 @@ +{ + "value": { + "isExists": true, + "value": "b5ee9c7201010101000700000900000c0ae0" + } +} \ No newline at end of file diff --git a/tolk/testdata/json/exists_optional_address.json b/tolk/testdata/json/exists_optional_address.json new file mode 100755 index 00000000..447ee205 --- /dev/null +++ b/tolk/testdata/json/exists_optional_address.json @@ -0,0 +1,3 @@ +{ + "value": "0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8" +} \ No newline at end of file diff --git a/tolk/testdata/json/external_address.json b/tolk/testdata/json/external_address.json new file mode 100755 index 00000000..087c7896 --- /dev/null +++ b/tolk/testdata/json/external_address.json @@ -0,0 +1,3 @@ +{ + "value": "4142" +} \ No newline at end of file diff --git a/tolk/testdata/json/int_key_map.json b/tolk/testdata/json/int_key_map.json new file mode 100755 index 00000000..8d0d1c9b --- /dev/null +++ b/tolk/testdata/json/int_key_map.json @@ -0,0 +1,5 @@ +{ + "value": { + "123": true + } +} \ No newline at end of file diff --git a/tolk/testdata/json/internal_address.json b/tolk/testdata/json/internal_address.json new file mode 100755 index 00000000..447ee205 --- /dev/null +++ b/tolk/testdata/json/internal_address.json @@ -0,0 +1,3 @@ +{ + "value": "0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8" +} \ No newline at end of file diff --git a/tolk/testdata/json/not_exists_nullable.json b/tolk/testdata/json/not_exists_nullable.json new file mode 100755 index 00000000..dd9b57fc --- /dev/null +++ b/tolk/testdata/json/not_exists_nullable.json @@ -0,0 +1,5 @@ +{ + "value": { + "isExists": false + } +} \ No newline at end of file diff --git a/tolk/testdata/json/not_exists_optional_address.json b/tolk/testdata/json/not_exists_optional_address.json new file mode 100755 index 00000000..90bee0a3 --- /dev/null +++ b/tolk/testdata/json/not_exists_optional_address.json @@ -0,0 +1,3 @@ +{ + "value": "" +} \ No newline at end of file diff --git a/tolk/testdata/json/ref.json b/tolk/testdata/json/ref.json new file mode 100755 index 00000000..84623158 --- /dev/null +++ b/tolk/testdata/json/ref.json @@ -0,0 +1,3 @@ +{ + "value": "1233212" +} \ No newline at end of file diff --git a/tolk/testdata/json/remaining.json b/tolk/testdata/json/remaining.json new file mode 100755 index 00000000..8285abe4 --- /dev/null +++ b/tolk/testdata/json/remaining.json @@ -0,0 +1,3 @@ +{ + "value": "b5ee9c7201010101000900000dc0800000000ab8" +} \ No newline at end of file diff --git a/tolk/testdata/json/small_int.json b/tolk/testdata/json/small_int.json new file mode 100755 index 00000000..fc28d5c8 --- /dev/null +++ b/tolk/testdata/json/small_int.json @@ -0,0 +1,3 @@ +{ + "value": -35132 +} \ No newline at end of file diff --git a/tolk/testdata/json/small_uint.json b/tolk/testdata/json/small_uint.json new file mode 100755 index 00000000..a14d14d9 --- /dev/null +++ b/tolk/testdata/json/small_uint.json @@ -0,0 +1,3 @@ +{ + "value": 934 +} \ No newline at end of file diff --git a/tolk/testdata/json/tensor.json b/tolk/testdata/json/tensor.json new file mode 100755 index 00000000..e9ce69e8 --- /dev/null +++ b/tolk/testdata/json/tensor.json @@ -0,0 +1,15 @@ +{ + "value": [ + "4325", + true, + "1000000000", + [ + -342, + { + "isExists": true, + "value": 0 + } + ], + "-9304000000" + ] +} \ No newline at end of file diff --git a/tolk/testdata/json/uint_key_map.json b/tolk/testdata/json/uint_key_map.json new file mode 100755 index 00000000..d17c8b46 --- /dev/null +++ b/tolk/testdata/json/uint_key_map.json @@ -0,0 +1,6 @@ +{ + "value": { + "14": "0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe", + "23": "-1:5555555555555555555555555555555555555555555555555555555555555555" + } +} \ No newline at end of file diff --git a/tolk/testdata/json/union_with_bin_prefix.json b/tolk/testdata/json/union_with_bin_prefix.json new file mode 100755 index 00000000..8c54575c --- /dev/null +++ b/tolk/testdata/json/union_with_bin_prefix.json @@ -0,0 +1,7 @@ +{ + "value": { + "v": { + "0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe": "43213412" + } + } +} \ No newline at end of file diff --git a/tolk/testdata/json/union_with_dec_prefix.json b/tolk/testdata/json/union_with_dec_prefix.json new file mode 100755 index 00000000..c003e116 --- /dev/null +++ b/tolk/testdata/json/union_with_dec_prefix.json @@ -0,0 +1,3 @@ +{ + "value": "124432123" +} \ No newline at end of file diff --git a/tolk/testdata/json/union_with_hex_prefix.json b/tolk/testdata/json/union_with_hex_prefix.json new file mode 100755 index 00000000..f25445e2 --- /dev/null +++ b/tolk/testdata/json/union_with_hex_prefix.json @@ -0,0 +1,5 @@ +{ + "value": { + "v": 1 + } +} \ No newline at end of file diff --git a/tolk/testdata/json/var_int.json b/tolk/testdata/json/var_int.json new file mode 100755 index 00000000..3174bfa2 --- /dev/null +++ b/tolk/testdata/json/var_int.json @@ -0,0 +1,3 @@ +{ + "value": "825432" +} \ No newline at end of file diff --git a/tolk/testdata/json/var_uint.json b/tolk/testdata/json/var_uint.json new file mode 100755 index 00000000..4cedbbca --- /dev/null +++ b/tolk/testdata/json/var_uint.json @@ -0,0 +1,3 @@ +{ + "value": "9451236712" +} \ No newline at end of file diff --git a/tolk/testdata/numbers.json b/tolk/testdata/numbers.json new file mode 100644 index 00000000..5ba658f6 --- /dev/null +++ b/tolk/testdata/numbers.json @@ -0,0 +1,72 @@ +{ + "contractName": "Test", + "declarations": [ + { + "kind": "Struct", + "name": "Numbers", + "fields": [ + { + "name": "num1", + "ty": { + "kind": "uintN", + "n": 64 + } + }, + { + "name": "num3", + "ty": { + "kind": "uintN", + "n": 256 + } + }, + { + "name": "num4", + "ty": { + "kind": "varuintN", + "n": 32 + } + }, + { + "name": "num5", + "ty": { + "kind": "bool" + } + }, + { + "name": "num7", + "ty": { + "kind": "bitsN", + "n": 16 + } + }, + { + "name": "num8", + "ty": { + "kind": "intN", + "n": 14 + } + }, + { + "name": "num9", + "ty": { + "kind": "varintN", + "n": 16 + } + } + ] + } + ], + "incomingMessages": [], + "outgoingMessages": [], + "emittedEvents": [], + "storage": { + "storageTy": { + "kind": "nullLiteral" + } + }, + "getMethods": [], + "thrownErrors": [], + "compilerName": "tolk", + "compilerVersion": "1.2.0", + "codeBoc64": "te6ccgEBAgEAFQABFP8A9KQT9LzyyAsBAAzTMPiR8kA=" +} \ No newline at end of file diff --git a/tolk/testdata/payloads.json b/tolk/testdata/payloads.json new file mode 100644 index 00000000..7ed9eff4 --- /dev/null +++ b/tolk/testdata/payloads.json @@ -0,0 +1,2421 @@ +{ + "contractName": "Payloads", + "declarations": [ + { + "kind": "Struct", + "name": "CoffeeStakingLockJettonPayload", + "prefix": { + "prefixStr": "0x0c0ffede", + "prefixLen": 32 + }, + "fields": [ + { + "name": "periodID", + "ty": { + "kind": "uintN", + "n": 32 + } + } + ] + }, + { + "kind": "Struct", + "name": "TextCommentJettonPayload", + "prefix": { + "prefixStr": "0x00000000", + "prefixLen": 32 + }, + "fields": [ + { + "name": "text", + "ty": { + "kind": "slice" + } + } + ] + }, + { + "kind": "Struct", + "name": "TegroJettonSwapJettonPayload", + "prefix": { + "prefixStr": "0x01fb7a25", + "prefixLen": 32 + }, + "fields": [ + { + "name": "extract", + "ty": { + "kind": "bool" + } + }, + { + "name": "maxIn", + "ty": { + "kind": "coins" + } + }, + { + "name": "minOut", + "ty": { + "kind": "coins" + } + }, + { + "name": "destination", + "ty": { + "kind": "address" + } + }, + { + "name": "errorDestination", + "ty": { + "kind": "address" + } + }, + { + "name": "payload", + "ty": { + "kind": "cell" + } + } + ] + }, + { + "kind": "Struct", + "name": "EncryptedTextCommentJettonPayload", + "prefix": { + "prefixStr": "0x2167da4b", + "prefixLen": 32 + }, + "fields": [ + { + "name": "cipherText", + "ty": { + "kind": "slice" + } + } + ] + }, + { + "kind": "Struct", + "name": "StonfiSwapJettonPayload", + "prefix": { + "prefixStr": "0x25938561", + "prefixLen": 32 + }, + "fields": [ + { + "name": "tokenWallet", + "ty": { + "kind": "address" + } + }, + { + "name": "minOut", + "ty": { + "kind": "coins" + } + }, + { + "name": "toAddress", + "ty": { + "kind": "address" + } + }, + { + "name": "referralAddress", + "ty": { + "kind": "addressOpt" + } + } + ] + }, + { + "kind": "Struct", + "name": "TegroAddLiquidityJettonPayload", + "prefix": { + "prefixStr": "0x287e167a", + "prefixLen": 32 + }, + "fields": [ + { + "name": "amountA", + "ty": { + "kind": "coins" + } + }, + { + "name": "amountB", + "ty": { + "kind": "coins" + } + } + ] + }, + { + "kind": "Struct", + "name": "StonfiProvideLpV2JettonPayload", + "prefix": { + "prefixStr": "0x37c096df", + "prefixLen": 32 + }, + "fields": [ + { + "name": "tokenWallet1", + "ty": { + "kind": "address" + } + }, + { + "name": "refundAddress", + "ty": { + "kind": "address" + } + }, + { + "name": "excessesAddress", + "ty": { + "kind": "address" + } + }, + { + "name": "txDeadline", + "ty": { + "kind": "uintN", + "n": 64 + } + }, + { + "name": "crossProvideLpBody", + "ty": { + "kind": "cellOf", + "inner": { + "kind": "StructRef", + "structName": "CrossProvideLpBody" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "CrossProvideLpBody", + "fields": [ + { + "name": "minLpOut", + "ty": { + "kind": "coins" + } + }, + { + "name": "toAddress", + "ty": { + "kind": "address" + } + }, + { + "name": "bothPositive", + "ty": { + "kind": "bool" + } + }, + { + "name": "fwdAmount", + "ty": { + "kind": "coins" + } + }, + { + "name": "customPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "BidaskProvideBothJettonPayload", + "prefix": { + "prefixStr": "0x3ea0bafc", + "prefixLen": 32 + }, + "fields": [ + { + "name": "tonAmount", + "ty": { + "kind": "coins" + } + }, + { + "name": "depositType", + "ty": { + "kind": "uintN", + "n": 4 + } + }, + { + "name": "liquidityDict", + "ty": { + "kind": "mapKV", + "k": { + "kind": "uintN", + "n": 32 + }, + "v": { + "kind": "intN", + "n": 32 + } + } + }, + { + "name": "rejectPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "forwardPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "DedustVolatile", + "prefix": { + "prefixStr": "0b0", + "prefixLen": 1 + }, + "fields": [] + }, + { + "kind": "Struct", + "name": "DedustStable", + "prefix": { + "prefixStr": "0b1", + "prefixLen": 1 + }, + "fields": [] + }, + { + "kind": "Struct", + "name": "DedustAssetNative", + "prefix": { + "prefixStr": "0b0000", + "prefixLen": 4 + }, + "fields": [] + }, + { + "kind": "Struct", + "name": "DedustAssetJetton", + "prefix": { + "prefixStr": "0b0001", + "prefixLen": 4 + }, + "fields": [ + { + "name": "workchain", + "ty": { + "kind": "intN", + "n": 8 + } + }, + { + "name": "hash", + "ty": { + "kind": "bitsN", + "n": 256 + } + } + ] + }, + { + "kind": "Struct", + "name": "DedustAssetExtraCurrency", + "prefix": { + "prefixStr": "0b0010", + "prefixLen": 4 + }, + "fields": [ + { + "name": "currencyId", + "ty": { + "kind": "intN", + "n": 32 + } + } + ] + }, + { + "kind": "Alias", + "name": "DedustPoolType", + "targetTy": { + "kind": "union", + "variants": [ + { + "variantTy": { + "kind": "StructRef", + "structName": "DedustVolatile" + }, + "prefixStr": "0b0", + "prefixLen": 1 + }, + { + "variantTy": { + "kind": "StructRef", + "structName": "DedustStable" + }, + "prefixStr": "0b1", + "prefixLen": 1 + } + ] + } + }, + { + "kind": "Alias", + "name": "DedustAsset", + "targetTy": { + "kind": "union", + "variants": [ + { + "variantTy": { + "kind": "StructRef", + "structName": "DedustAssetNative" + }, + "prefixStr": "0b0000", + "prefixLen": 4 + }, + { + "variantTy": { + "kind": "StructRef", + "structName": "DedustAssetJetton" + }, + "prefixStr": "0b0001", + "prefixLen": 4 + }, + { + "variantTy": { + "kind": "StructRef", + "structName": "DedustAssetExtraCurrency" + }, + "prefixStr": "0b0010", + "prefixLen": 4 + } + ] + } + }, + { + "kind": "Struct", + "name": "DedustDepositLiquidityJettonPayload", + "prefix": { + "prefixStr": "0x40e108d6", + "prefixLen": 32 + }, + "fields": [ + { + "name": "poolType", + "ty": { + "kind": "AliasRef", + "aliasName": "DedustPoolType" + } + }, + { + "name": "asset0", + "ty": { + "kind": "AliasRef", + "aliasName": "DedustAsset" + } + }, + { + "name": "asset1", + "ty": { + "kind": "AliasRef", + "aliasName": "DedustAsset" + } + }, + { + "name": "asset0TargetBalance", + "ty": { + "kind": "coins" + } + }, + { + "name": "asset1TargetBalance", + "ty": { + "kind": "coins" + } + }, + { + "name": "fulfillPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "rejectPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "PoolFundAccountJettonPayload", + "prefix": { + "prefixStr": "0x4468de77", + "prefixLen": 32 + }, + "fields": [ + { + "name": "jettonTarget", + "ty": { + "kind": "address" + } + }, + { + "name": "enough0", + "ty": { + "kind": "coins" + } + }, + { + "name": "enough1", + "ty": { + "kind": "coins" + } + }, + { + "name": "liquidity", + "ty": { + "kind": "uintN", + "n": 128 + } + }, + { + "name": "tickLower", + "ty": { + "kind": "intN", + "n": 24 + } + }, + { + "name": "tickUpper", + "ty": { + "kind": "intN", + "n": 24 + } + } + ] + }, + { + "kind": "Struct", + "name": "StonfiSwapOkRefJettonPayload", + "prefix": { + "prefixStr": "0x45078540", + "prefixLen": 32 + }, + "fields": [] + }, + { + "kind": "Struct", + "name": "CoffeeCrossDexResendJettonPayload", + "prefix": { + "prefixStr": "0x4ee9b106", + "prefixLen": 32 + }, + "fields": [ + { + "name": "next", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "BidaskDammProvideJettonPayload", + "prefix": { + "prefixStr": "0x63ec24ae", + "prefixLen": 32 + }, + "fields": [ + { + "name": "receiver", + "ty": { + "kind": "address" + } + }, + { + "name": "lockLiquidity", + "ty": { + "kind": "bool" + } + }, + { + "name": "rejectPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "forwardPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "StonfiSwapV2JettonPayload", + "prefix": { + "prefixStr": "0x6664de2a", + "prefixLen": 32 + }, + "fields": [ + { + "name": "tokenWallet1", + "ty": { + "kind": "address" + } + }, + { + "name": "refundAddress", + "ty": { + "kind": "address" + } + }, + { + "name": "excessesAddress", + "ty": { + "kind": "address" + } + }, + { + "name": "txDeadline", + "ty": { + "kind": "uintN", + "n": 64 + } + }, + { + "name": "crossSwapBody", + "ty": { + "kind": "cellOf", + "inner": { + "kind": "StructRef", + "structName": "CrossSwapBody" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "CrossSwapBody", + "fields": [ + { + "name": "minOut", + "ty": { + "kind": "coins" + } + }, + { + "name": "receiver", + "ty": { + "kind": "address" + } + }, + { + "name": "fwdGas", + "ty": { + "kind": "coins" + } + }, + { + "name": "customPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "refundFwdGas", + "ty": { + "kind": "coins" + } + }, + { + "name": "refundPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "refFee", + "ty": { + "kind": "uintN", + "n": 16 + } + }, + { + "name": "refAddress", + "ty": { + "kind": "address" + } + } + ] + }, + { + "kind": "Struct", + "name": "BidaskDammProvideOneSideJettonPayload", + "prefix": { + "prefixStr": "0x729c04c8", + "prefixLen": 32 + }, + "fields": [ + { + "name": "receiver", + "ty": { + "kind": "address" + } + }, + { + "name": "lockLiquidity", + "ty": { + "kind": "bool" + } + }, + { + "name": "rejectPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "forwardPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "StormNoKeyInit", + "fields": [] + }, + { + "kind": "Struct", + "name": "StormNeedKeyInit", + "fields": [ + { + "name": "userPublicKeys", + "ty": { + "kind": "mapKV", + "k": { + "kind": "uintN", + "n": 256 + }, + "v": { + "kind": "cell" + } + } + } + ] + }, + { + "kind": "Alias", + "name": "InitializationRequest", + "targetTy": { + "kind": "union", + "variants": [ + { + "prefixStr": "0", + "prefixLen": 1, + "prefixEatInPlace": true, + "variantTy": { + "kind": "StructRef", + "structName": "StormNoKeyInit" + } + }, + { + "prefixStr": "1", + "prefixLen": 1, + "prefixEatInPlace": true, + "variantTy": { + "kind": "StructRef", + "structName": "StormNeedKeyInit" + } + } + ] + } + }, + { + "kind": "Struct", + "name": "StormDepositJettonJettonPayload", + "prefix": { + "prefixStr": "0x76840119", + "prefixLen": 32 + }, + "fields": [ + { + "name": "queryId", + "ty": { + "kind": "uintN", + "n": 64 + } + }, + { + "name": "receiverAddress", + "ty": { + "kind": "address" + } + }, + { + "name": "init", + "ty": { + "kind": "bool" + } + }, + { + "name": "keyInit", + "ty": { + "kind": "AliasRef", + "aliasName": "InitializationRequest" + } + } + ] + }, + { + "kind": "Struct", + "name": "InvoiceUrlNone", + "prefix": { + "prefixStr": "0x00", + "prefixLen": 8 + }, + "fields": [] + }, + { + "kind": "Struct", + "name": "InvoiceUrlTonsite", + "prefix": { + "prefixStr": "0x01", + "prefixLen": 8 + }, + "fields": [ + { + "name": "address", + "ty": { + "kind": "bitsN", + "n": 256 + } + } + ] + }, + { + "kind": "Alias", + "name": "InvoiceUrl", + "targetTy": { + "kind": "union", + "variants": [ + { + "variantTy": { + "kind": "StructRef", + "structName": "InvoiceUrlNone" + }, + "prefixStr": "0x00", + "prefixLen": 8 + }, + { + "variantTy": { + "kind": "StructRef", + "structName": "InvoiceUrlTonsite" + }, + "prefixStr": "0x01", + "prefixLen": 8 + } + ] + } + }, + { + "kind": "Struct", + "name": "InvoicePayloadJettonPayload", + "prefix": { + "prefixStr": "0x7aa23eb5", + "prefixLen": 32 + }, + "fields": [ + { + "name": "id", + "ty": { + "kind": "bitsN", + "n": 128 + } + }, + { + "name": "url", + "ty": { + "kind": "AliasRef", + "aliasName": "InvoiceUrl" + } + } + ] + }, + { + "kind": "Struct", + "name": "TonkeeperRelayerFeeJettonPayload", + "prefix": { + "prefixStr": "0x878da6e3", + "prefixLen": 32 + }, + "fields": [] + }, + { + "kind": "Struct", + "name": "AdditionalData", + "fields": [ + { + "name": "fromAddress", + "ty": { + "kind": "address" + } + }, + { + "name": "refAddress", + "ty": { + "kind": "address" + } + } + ] + }, + { + "kind": "Struct", + "name": "BidaskSwapV2JettonPayload", + "prefix": { + "prefixStr": "0x87d36990", + "prefixLen": 32 + }, + "fields": [ + { + "name": "toAddress", + "ty": { + "kind": "address" + } + }, + { + "name": "slippage", + "ty": { + "kind": "union", + "variants": [ + { + "prefixStr": "0", + "prefixLen": 1, + "prefixEatInPlace": true, + "variantTy": { + "kind": "coins" + } + }, + { + "prefixStr": "1", + "prefixLen": 1, + "prefixEatInPlace": true, + "variantTy": { + "kind": "uintN", + "n": 256 + } + } + ] + } + }, + { + "name": "exactOut", + "ty": { + "kind": "coins" + } + }, + { + "name": "additionalData", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cellOf", + "inner": { + "kind": "StructRef", + "structName": "AdditionalData" + } + } + } + }, + { + "name": "rejectPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "forwardPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "MoonBoostPoolJettonPayload", + "prefix": { + "prefixStr": "0x96aa1586", + "prefixLen": 32 + }, + "fields": [] + }, + { + "kind": "Struct", + "name": "BidaskProvideJettonPayload", + "prefix": { + "prefixStr": "0x96feef7b", + "prefixLen": 32 + }, + "fields": [ + { + "name": "depositType", + "ty": { + "kind": "uintN", + "n": 4 + } + }, + { + "name": "liquidityDict", + "ty": { + "kind": "mapKV", + "k": { + "kind": "uintN", + "n": 32 + }, + "v": { + "kind": "intN", + "n": 32 + } + } + }, + { + "name": "rejectPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "forwardPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "MoonFillOrderJettonPayload", + "prefix": { + "prefixStr": "0x99b49842", + "prefixLen": 32 + }, + "fields": [ + { + "name": "recipient", + "ty": { + "kind": "address" + } + }, + { + "name": "recipientPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "rejectAddress", + "ty": { + "kind": "address" + } + } + ] + }, + { + "kind": "Struct", + "name": "BidaskDammProvideBothJettonPayload", + "prefix": { + "prefixStr": "0xa8904134", + "prefixLen": 32 + }, + "fields": [ + { + "name": "nativeAmount", + "ty": { + "kind": "coins" + } + }, + { + "name": "reciever", + "ty": { + "kind": "address" + } + }, + { + "name": "lockLiquidity", + "ty": { + "kind": "bool" + } + }, + { + "name": "rejectPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "forwardPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "MoonDepositLiquidityJettonPayload", + "prefix": { + "prefixStr": "0xb31db781", + "prefixLen": 32 + }, + "fields": [ + { + "name": "minLpOut", + "ty": { + "kind": "coins" + } + } + ] + }, + { + "kind": "Struct", + "name": "MoonNextPayload", + "fields": [ + { + "name": "recipient", + "ty": { + "kind": "address" + } + }, + { + "name": "payload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "MoonSwapParams", + "fields": [ + { + "name": "minOut", + "ty": { + "kind": "coins" + } + }, + { + "name": "deadline", + "ty": { + "kind": "uintN", + "n": 64 + } + }, + { + "name": "excess", + "ty": { + "kind": "address" + } + }, + { + "name": "referral", + "ty": { + "kind": "address" + } + }, + { + "name": "nextFulfill", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cellOf", + "inner": { + "kind": "StructRef", + "structName": "MoonNextPayload" + } + } + } + }, + { + "name": "nextReject", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cellOf", + "inner": { + "kind": "StructRef", + "structName": "MoonNextPayload" + } + } + } + } + ] + }, + { + "kind": "Struct", + "name": "MoonSwapJettonPayload", + "prefix": { + "prefixStr": "0xb37a900b", + "prefixLen": 32 + }, + "fields": [ + { + "name": "moonSwap", + "ty": { + "kind": "StructRef", + "structName": "MoonSwapParams" + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeCrossDexFailureJettonPayload", + "prefix": { + "prefixStr": "0xb902e61a", + "prefixLen": 32 + }, + "fields": [ + { + "name": "queryId", + "ty": { + "kind": "uintN", + "n": 64 + } + }, + { + "name": "recipient", + "ty": { + "kind": "address" + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeSwapStepParams", + "fields": [ + { + "name": "poolAddressHash", + "ty": { + "kind": "uintN", + "n": 256 + } + }, + { + "name": "minOutAmount", + "ty": { + "kind": "coins" + } + }, + { + "name": "next", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cellOf", + "inner": { + "kind": "StructRef", + "structName": "CoffeeSwapStepParams" + } + } + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeNotificationDataSingle", + "fields": [ + { + "name": "reciever", + "ty": { + "kind": "address" + } + }, + { + "name": "fwdGas", + "ty": { + "kind": "coins" + } + }, + { + "name": "payload", + "ty": { + "kind": "cell" + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeNotificationData", + "fields": [ + { + "name": "onSuccess", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cellOf", + "inner": { + "kind": "StructRef", + "structName": "CoffeeNotificationDataSingle" + } + } + } + }, + { + "name": "onFailure", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cellOf", + "inner": { + "kind": "StructRef", + "structName": "CoffeeNotificationDataSingle" + } + } + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeSwapParams", + "fields": [ + { + "name": "deadline", + "ty": { + "kind": "uintN", + "n": 32 + } + }, + { + "name": "recipient", + "ty": { + "kind": "address" + } + }, + { + "name": "referral", + "ty": { + "kind": "address" + } + }, + { + "name": "notificationData", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cellOf", + "inner": { + "kind": "StructRef", + "structName": "CoffeeNotificationData" + } + } + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeSwapJettonPayload", + "prefix": { + "prefixStr": "0xc0ffee10", + "prefixLen": 32 + }, + "fields": [ + { + "name": "step", + "ty": { + "kind": "StructRef", + "structName": "CoffeeSwapStepParams" + } + }, + { + "name": "params", + "ty": { + "kind": "cellOf", + "inner": { + "kind": "StructRef", + "structName": "CoffeeSwapParams" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeAssetNative", + "prefix": { + "prefixStr": "0b00", + "prefixLen": 2 + }, + "fields": [] + }, + { + "kind": "Struct", + "name": "CoffeeAssetJetton", + "prefix": { + "prefixStr": "0b01", + "prefixLen": 2 + }, + "fields": [ + { + "name": "chain", + "ty": { + "kind": "uintN", + "n": 8 + } + }, + { + "name": "hash", + "ty": { + "kind": "uintN", + "n": 256 + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeAssetExtra", + "prefix": { + "prefixStr": "0b10", + "prefixLen": 2 + }, + "fields": [ + { + "name": "id", + "ty": { + "kind": "uintN", + "n": 32 + } + } + ] + }, + { + "kind": "Alias", + "name": "CoffeeAsset", + "targetTy": { + "kind": "union", + "variants": [ + { + "variantTy": { + "kind": "StructRef", + "structName": "CoffeeAssetNative" + }, + "prefixStr": "0b00", + "prefixLen": 2 + }, + { + "variantTy": { + "kind": "StructRef", + "structName": "CoffeeAssetJetton" + }, + "prefixStr": "0b01", + "prefixLen": 2 + }, + { + "variantTy": { + "kind": "StructRef", + "structName": "CoffeAssetExtra" + }, + "prefixStr": "0b10", + "prefixLen": 2 + } + ] + } + }, + { + "kind": "Struct", + "name": "CoffeeAmmConstProd", + "prefix": { + "prefixStr": "0b000", + "prefixLen": 3 + }, + "fields": [] + }, + { + "kind": "Struct", + "name": "CoffeeAmmCurveFiStable", + "prefix": { + "prefixStr": "0b001", + "prefixLen": 3 + }, + "fields": [] + }, + { + "kind": "Alias", + "name": "CoffeeAmm", + "targetTy": { + "kind": "union", + "variants": [ + { + "variantTy": { + "kind": "StructRef", + "structName": "CoffeeAmmConstProd" + }, + "prefixStr": "0b000", + "prefixLen": 3 + }, + { + "variantTy": { + "kind": "StructRef", + "structName": "CoffeeAmmCurveFiStable" + }, + "prefixStr": "0b001", + "prefixLen": 3 + } + ] + } + }, + { + "kind": "Struct", + "name": "CoffeePoolParams", + "fields": [ + { + "name": "first", + "ty": { + "kind": "AliasRef", + "aliasName": "CoffeeAsset" + } + }, + { + "name": "second", + "ty": { + "kind": "AliasRef", + "aliasName": "CoffeeAsset" + } + }, + { + "name": "amm", + "ty": { + "kind": "AliasRef", + "aliasName": "CoffeeAmm" + } + }, + { + "name": "ammSettings", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeePublicPoolCreationParams", + "fields": [ + { + "name": "recipient", + "ty": { + "kind": "address" + } + }, + { + "name": "useRecipientOnFailure", + "ty": { + "kind": "bool" + } + }, + { + "name": "notificationData", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeePrivatePoolCreationParams", + "fields": [ + { + "name": "isActive", + "ty": { + "kind": "bool" + } + }, + { + "name": "extraSettings", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeePoolCreationParams", + "fields": [ + { + "name": "public", + "ty": { + "kind": "StructRef", + "structName": "CoffeePublicPoolCreationParams" + } + }, + { + "name": "privateP", + "ty": { + "kind": "StructRef", + "structName": "CoffeePrivatePoolCreationParams" + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeCreatePoolJettonPayload", + "prefix": { + "prefixStr": "0xc0ffee11", + "prefixLen": 32 + }, + "fields": [ + { + "name": "params", + "ty": { + "kind": "StructRef", + "structName": "CoffeePoolParams" + } + }, + { + "name": "creationParams", + "ty": { + "kind": "StructRef", + "structName": "CoffeePoolCreationParams" + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeDepositLiquidityConditionNone", + "prefix": { + "prefixStr": "0b00", + "prefixLen": 2 + }, + "fields": [] + }, + { + "kind": "Struct", + "name": "CoffeeDepositLiquidityConditionLpQuantity", + "prefix": { + "prefixStr": "0b01", + "prefixLen": 2 + }, + "fields": [ + { + "name": "minLpAmount", + "ty": { + "kind": "coins" + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeDepositLiquidityConditionReservedRatio", + "prefix": { + "prefixStr": "0b10", + "prefixLen": 2 + }, + "fields": [ + { + "name": "denominator", + "ty": { + "kind": "coins" + } + }, + { + "name": "minNominator", + "ty": { + "kind": "coins" + } + }, + { + "name": "maxNominator", + "ty": { + "kind": "coins" + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeDepositLiquidityConditionComplex", + "prefix": { + "prefixStr": "0b11", + "prefixLen": 2 + }, + "fields": [ + { + "name": "minLpAmoint", + "ty": { + "kind": "coins" + } + }, + { + "name": "denominator", + "ty": { + "kind": "coins" + } + }, + { + "name": "minNominator", + "ty": { + "kind": "coins" + } + }, + { + "name": "maxNominator", + "ty": { + "kind": "coins" + } + } + ] + }, + { + "kind": "Alias", + "name": "CoffeeDepositLiquidityCondition", + "targetTy": { + "kind": "union", + "variants": [ + { + "variantTy": { + "kind": "StructRef", + "structName": "CoffeeDepositLiquidityConditionNone" + }, + "prefixStr": "0b00", + "prefixLen": 2 + }, + { + "variantTy": { + "kind": "StructRef", + "structName": "CoffeeDepositLiquidityConditionLpQuantity" + }, + "prefixStr": "0b01", + "prefixLen": 2 + }, + { + "variantTy": { + "kind": "StructRef", + "structName": "CoffeeDepositLiquidityConditionReservedRatio" + }, + "prefixStr": "0b10", + "prefixLen": 2 + }, + { + "variantTy": { + "kind": "StructRef", + "structName": "CoffeeDepositLiquidityConditionComplex" + }, + "prefixStr": "0b11", + "prefixLen": 2 + } + ] + } + }, + { + "kind": "Struct", + "name": "CoffeeDepositLiquidityParamsTrimmed", + "fields": [ + { + "name": "recipient", + "ty": { + "kind": "address" + } + }, + { + "name": "useRecipientOnFailure", + "ty": { + "kind": "bool" + } + }, + { + "name": "referral", + "ty": { + "kind": "addressOpt" + } + }, + { + "name": "deadline", + "ty": { + "kind": "uintN", + "n": 32 + } + }, + { + "name": "condition", + "ty": { + "kind": "AliasRef", + "aliasName": "CoffeeDepositLiquidityCondition" + } + }, + { + "name": "extraSettings", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "notificationData", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cellOf", + "inner": { + "kind": "StructRef", + "structName": "CoffeeNotificationData" + } + } + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeDepositLiquidityParams", + "fields": [ + { + "name": "params", + "ty": { + "kind": "StructRef", + "structName": "CoffeeDepositLiquidityParamsTrimmed" + } + }, + { + "name": "poolParmas", + "ty": { + "kind": "StructRef", + "structName": "CoffeePoolParams" + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeDepositLiquidityJettonPayload", + "prefix": { + "prefixStr": "0xc0ffee12", + "prefixLen": 32 + }, + "fields": [ + { + "name": "params", + "ty": { + "kind": "StructRef", + "structName": "CoffeeDepositLiquidityParams" + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeNotificationJettonPayload", + "prefix": { + "prefixStr": "0xc0ffee36", + "prefixLen": 32 + }, + "fields": [ + { + "name": "queryId", + "ty": { + "kind": "uintN", + "n": 64 + } + }, + { + "name": "body", + "ty": { + "kind": "cell" + } + } + ] + }, + { + "kind": "Struct", + "name": "MoonSwapFailedJettonPayload", + "prefix": { + "prefixStr": "0xc47c1f57", + "prefixLen": 32 + }, + "fields": [] + }, + { + "kind": "Struct", + "name": "StonfiSwapOkJettonPayload", + "prefix": { + "prefixStr": "0xc64370e5", + "prefixLen": 32 + }, + "fields": [] + }, + { + "kind": "Struct", + "name": "StormStakeJettonPayload", + "prefix": { + "prefixStr": "0xc89a3ee4", + "prefixLen": 32 + }, + "fields": [] + }, + { + "kind": "Struct", + "name": "WithdrawPayloadJettonPayload", + "prefix": { + "prefixStr": "0xcb03bfaf", + "prefixLen": 32 + }, + "fields": [ + { + "name": "assetAddress", + "ty": { + "kind": "address" + } + }, + { + "name": "oracleParams", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "forwardTonAmount", + "ty": { + "kind": "coins" + } + }, + { + "name": "forwardPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "MoonSwapSucceedJettonPayload", + "prefix": { + "prefixStr": "0xcb7f38d6", + "prefixLen": 32 + }, + "fields": [] + }, + { + "kind": "Struct", + "name": "MoonOrderParams", + "fields": [ + { + "name": "rate", + "ty": { + "kind": "uintN", + "n": 256 + } + }, + { + "name": "lock", + "ty": { + "kind": "uintN", + "n": 2 + } + }, + { + "name": "vestingTime", + "ty": { + "kind": "uintN", + "n": 64 + } + } + ] + }, + { + "kind": "Struct", + "name": "MoonCreateOrderJettonPayload", + "prefix": { + "prefixStr": "0xda067c19", + "prefixLen": 32 + }, + "fields": [ + { + "name": "asset1", + "ty": { + "kind": "address" + } + }, + { + "name": "asset2", + "ty": { + "kind": "address" + } + }, + { + "name": "orderData", + "ty": { + "kind": "StructRef", + "structName": "MoonOrderParams" + } + } + ] + }, + { + "kind": "Struct", + "name": "BidaskDammSwapJettonPayload", + "prefix": { + "prefixStr": "0xdd79732c", + "prefixLen": 32 + }, + "fields": [ + { + "name": "toAddress", + "ty": { + "kind": "address" + } + }, + { + "name": "slippage", + "ty": { + "kind": "coins" + } + }, + { + "name": "fromAddress", + "ty": { + "kind": "address" + } + }, + { + "name": "exactOut", + "ty": { + "kind": "coins" + } + }, + { + "name": "additionalData", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "rejectPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "forwardPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "DedustSwapStepParams", + "fields": [ + { + "name": "kindOut", + "ty": { + "kind": "bool" + } + }, + { + "name": "limit", + "ty": { + "kind": "coins" + } + }, + { + "name": "next", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cellOf", + "inner": { + "kind": "StructRef", + "structName": "DedustSwapStep" + } + } + } + } + ] + }, + { + "kind": "Struct", + "name": "DedustSwapStep", + "fields": [ + { + "name": "poolAddr", + "ty": { + "kind": "address" + } + }, + { + "name": "params", + "ty": { + "kind": "StructRef", + "structName": "DedustSwapStepParams" + } + } + ] + }, + { + "kind": "Struct", + "name": "DedustSwapParams", + "fields": [ + { + "name": "deadline", + "ty": { + "kind": "uintN", + "n": 32 + } + }, + { + "name": "recipientAddr", + "ty": { + "kind": "address" + } + }, + { + "name": "referralAddr", + "ty": { + "kind": "address" + } + }, + { + "name": "fulfillPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "rejectPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "DedustSwapJettonPayload", + "prefix": { + "prefixStr": "0xe3a0d482", + "prefixLen": 32 + }, + "fields": [ + { + "name": "step", + "ty": { + "kind": "StructRef", + "structName": "DedustSwapStep" + } + }, + { + "name": "swapParams", + "ty": { + "kind": "cellOf", + "inner": { + "kind": "StructRef", + "structName": "DedustSwapParams" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeMevProtectFailedSwapJettonPayload", + "prefix": { + "prefixStr": "0xee51ce51", + "prefixLen": 32 + }, + "fields": [ + { + "name": "queryId", + "ty": { + "kind": "uintN", + "n": 64 + } + }, + { + "name": "recipient", + "ty": { + "kind": "address" + } + } + ] + }, + { + "kind": "Struct", + "name": "BidaskSwapJettonPayload", + "prefix": { + "prefixStr": "0xf2ef6c1b", + "prefixLen": 32 + }, + "fields": [ + { + "name": "toAddress", + "ty": { + "kind": "address" + } + }, + { + "name": "slippage", + "ty": { + "kind": "union", + "variants": [ + { + "prefixStr": "0", + "prefixLen": 1, + "prefixEatInPlace": true, + "variantTy": { + "kind": "coins" + } + }, + { + "prefixStr": "1", + "prefixLen": 1, + "prefixEatInPlace": true, + "variantTy": { + "kind": "uintN", + "n": 256 + } + } + ] + } + }, + { + "name": "exactOut", + "ty": { + "kind": "coins" + } + }, + { + "name": "refAddress", + "ty": { + "kind": "address" + } + }, + { + "name": "additionalData", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "rejectPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "forwardPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "DepositPayloadJettonPayload", + "prefix": { + "prefixStr": "0xf9471134", + "prefixLen": 32 + }, + "fields": [ + { + "name": "oracleParams", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + }, + { + "name": "forwardTonAmount", + "ty": { + "kind": "coins" + } + }, + { + "name": "forwardPayload", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cell" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "StonfiProvideLiquidityJettonPayload", + "prefix": { + "prefixStr": "0xfcf9e58f", + "prefixLen": 32 + }, + "fields": [ + { + "name": "tokenWallet", + "ty": { + "kind": "address" + } + }, + { + "name": "minLpOut", + "ty": { + "kind": "coins" + } + } + ] + } + ], + "incomingMessages": [], + "outgoingMessages": [], + "emittedEvents": [], + "storage": { + "storageTy": { + "kind": "nullLiteral" + } + }, + "getMethods": [], + "thrownErrors": [], + "compilerName": "tolk", + "compilerVersion": "1.2.0", + "codeBoc64": "te6ccgEBAgEAFQABFP8A9KQT9LzyyAsBAAzTMPiR8kA=" +} \ No newline at end of file diff --git a/tolk/testdata/random_fields.json b/tolk/testdata/random_fields.json new file mode 100644 index 00000000..8dbea0cc --- /dev/null +++ b/tolk/testdata/random_fields.json @@ -0,0 +1,158 @@ +{ + "contractName": "Test", + "declarations": [ + { + "kind": "Alias", + "name": "MyInt32", + "targetTy": { + "kind": "intN", + "n": 32 + } + }, + { + "kind": "Struct", + "name": "RandomFields", + "prefix": { + "prefixStr": "0x794", + "prefixLen": 12 + }, + "fields": [ + { + "name": "dest_int", + "ty": { + "kind": "address" + } + }, + { + "name": "amount", + "ty": { + "kind": "coins" + } + }, + { + "name": "dest_ext", + "ty": { + "kind": "addressAny" + } + }, + { + "name": "intVector", + "ty": { + "kind": "tensor", + "items": [ + { + "kind": "intN", + "n": 32 + }, + { + "kind": "nullable", + "inner": { + "kind": "coins" + } + }, + { + "kind": "uintN", + "n": 64 + } + ] + } + }, + { + "name": "needs_more", + "ty": { + "kind": "cellOf", + "inner": { + "kind": "bool" + } + } + }, + { + "name": "some_payload", + "ty": { + "kind": "cell" + } + }, + { + "name": "my_int", + "ty": { + "kind": "AliasRef", + "aliasName": "MyInt32" + } + }, + { + "name": "some_union", + "ty": { + "kind": "union", + "variants": [ + { + "variantTy": { + "kind": "intN", + "n": 8 + }, + "prefixStr": "0b00", + "prefixLen": 2, + "prefixEatInPlace": true + }, + { + "variantTy": { + "kind": "intN", + "n": 16 + }, + "prefixStr": "0b01", + "prefixLen": 2, + "prefixEatInPlace": true + }, + { + "variantTy": { + "kind": "intN", + "n": 256 + }, + "prefixStr": "0b10", + "prefixLen": 2, + "prefixEatInPlace": true + } + ] + } + }, + { + "name": "default_1", + "ty": { + "kind": "intN", + "n": 16 + }, + "defaultValue": { + "kind": "int", + "v": "1" + } + }, + { + "name": "default_2", + "ty": { + "kind": "nullable", + "inner": { + "kind": "intN", + "n": 16 + } + }, + "defaultValue": { + "kind": "int", + "v": "55" + } + } + ] + } + ], + "incomingMessages": [], + "outgoingMessages": [], + "emittedEvents": [], + "storage": { + "storageTy": { + "kind": "nullLiteral" + } + }, + "getMethods": [], + "thrownErrors": [], + "compilerName": "tolk", + "compilerVersion": "1.2.0", + "codeBoc64": "te6ccgEBAgEAFQABFP8A9KQT9LzyyAsBAAzTMPiR8kA=" +} \ No newline at end of file diff --git a/tolk/testdata/refs.json b/tolk/testdata/refs.json new file mode 100644 index 00000000..33d0b9f4 --- /dev/null +++ b/tolk/testdata/refs.json @@ -0,0 +1,211 @@ +{ + "contractName": "Test", + "declarations": [ + { + "kind": "Struct", + "name": "AddressAndBalance", + "fields": [ + { + "name": "addr", + "ty": { + "kind": "address" + } + }, + { + "name": "balance", + "ty": { + "kind": "coins" + } + } + ] + }, + { + "kind": "Alias", + "name": "GoodNaming", + "targetTy": { + "kind": "StructRef", + "structName": "AddressAndBalance" + } + }, + { + "kind": "Enum", + "name": "Role", + "encodedAs": { + "kind": "uintN", + "n": 1 + }, + "members": [ + { + "name": "Admin", + "value": "0" + }, + { + "name": "Aboba", + "value": "1" + } + ] + }, + { + "kind": "Enum", + "name": "Operation", + "encodedAs": { + "kind": "varuintN", + "n": 16 + }, + "members": [ + { + "name": "Add", + "value": "0" + }, + { + "name": "Sub", + "value": "100000000000000" + } + ] + }, + { + "kind": "Enum", + "name": "Operation2", + "encodedAs": { + "kind": "intN", + "n": 128 + }, + "members": [ + { + "name": "TopUp", + "value": "-10000" + }, + { + "name": "Withdraw", + "value": "10000" + } + ] + }, + { + "kind": "Enum", + "name": "Operation3", + "encodedAs": { + "kind": "uintN", + "n": 8 + }, + "members": [ + { + "name": "Nothing", + "value": "10" + }, + { + "name": "Something", + "value": "1" + } + ] + }, + { + "kind": "Struct", + "name": "ManyRefsMsg", + "prefix": { + "prefixStr": "0xdeadbeef", + "prefixLen": 32 + }, + "fields": [ + { + "name": "user1", + "ty": { + "kind": "AliasRef", + "aliasName": "GoodNaming" + } + }, + { + "name": "user2", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cellOf", + "inner": { + "kind": "AliasRef", + "aliasName": "GoodNaming" + } + } + } + }, + { + "name": "user3", + "ty": { + "kind": "cell" + } + }, + { + "name": "user4", + "ty": { + "kind": "nullable", + "inner": { + "kind": "StructRef", + "structName": "AddressAndBalance" + } + }, + "defaultValue": { + "kind": "null" + } + }, + { + "name": "user5", + "ty": { + "kind": "cellOf", + "inner": { + "kind": "StructRef", + "structName": "AddressAndBalance" + } + } + }, + { + "name": "role", + "ty": { + "kind": "EnumRef", + "enumName": "Role" + } + }, + { + "name": "oper1", + "ty": { + "kind": "EnumRef", + "enumName": "Operation" + } + }, + { + "name": "oper2", + "ty": { + "kind": "EnumRef", + "enumName": "Operation2" + } + }, + { + "name": "oper3", + "ty": { + "kind": "EnumRef", + "enumName": "Operation3" + } + } + ] + }, + { + "kind": "Alias", + "name": "GoodNamingForMsg", + "targetTy": { + "kind": "StructRef", + "structName": "ManyRefsMsg" + } + } + ], + "incomingMessages": [], + "outgoingMessages": [], + "emittedEvents": [], + "storage": { + "storageTy": { + "kind": "nullLiteral" + } + }, + "getMethods": [], + "thrownErrors": [], + "compilerName": "tolk", + "compilerVersion": "1.2.0", + "codeBoc64": "te6ccgEBAgEAFQABFP8A9KQT9LzyyAsBAAzTMPiR8kA=" +} \ No newline at end of file diff --git a/tolk/testdata/simple.json b/tolk/testdata/simple.json new file mode 100644 index 00000000..cb07aa46 --- /dev/null +++ b/tolk/testdata/simple.json @@ -0,0 +1,17 @@ +{ + "contractName": "Test", + "declarations": [], + "incomingMessages": [], + "outgoingMessages": [], + "emittedEvents": [], + "storage": { + "storageTy": { + "kind": "nullLiteral" + } + }, + "getMethods": [], + "thrownErrors": [], + "compilerName": "tolk", + "compilerVersion": "1.2.0", + "codeBoc64": "te6ccgEBAgEAFQABFP8A9KQT9LzyyAsBAAzTMPiR8kA=" +} \ No newline at end of file diff --git a/tolk/testdata/swap_coffee.json b/tolk/testdata/swap_coffee.json new file mode 100644 index 00000000..337cc270 --- /dev/null +++ b/tolk/testdata/swap_coffee.json @@ -0,0 +1,156 @@ +{ + "contractName": "Test", + "declarations": [ + { + "kind": "Struct", + "name": "CoffeeCrossDexResend", + "prefix": { + "prefixStr": "0x4ee9b106", + "prefixLen": 32 + }, + "fields": [ + { + "name": "next", + "isPayload": true, + "ty": { + "kind": "cell" + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeSwap", + "prefix": { + "prefixStr": "0xc0ffee10", + "prefixLen": 32 + }, + "fields": [ + { + "name": "step", + "ty": { + "kind": "StructRef", + "structName": "CoffeeSwapStepParams" + } + }, + { + "name": "params", + "ty": { + "kind": "cellOf", + "inner": { + "kind": "StructRef", + "structName": "CoffeeSwapParams" + } + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeSwapStepParams", + "fields": [ + { + "name": "poolAddressHash", + "ty": { + "kind": "uintN", + "n": 256 + } + }, + { + "name": "minOutputAmount", + "ty": { + "kind": "coins" + } + }, + { + "name": "next", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cellOf", + "inner": { + "kind": "StructRef", + "structName": "CoffeeSwapStepParams" + } + } + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeSwapParams", + "fields": [ + { + "name": "deadline", + "ty": { + "kind": "uintN", + "n": 32 + } + }, + { + "name": "recipient", + "ty": { + "kind": "address" + } + }, + { + "name": "referral", + "ty": { + "kind": "addressOpt" + } + }, + { + "name": "notificationData", + "ty": { + "kind": "nullable", + "inner": { + "kind": "cellOf", + "inner": { + "kind": "StructRef", + "structName": "CoffeeNotificationData" + } + } + } + } + ] + }, + { + "kind": "Struct", + "name": "CoffeeNotificationData", + "fields": [ + { + "name": "reciever", + "ty": { + "kind": "address" + } + }, + { + "name": "fwdGas", + "ty": { + "kind": "coins" + } + }, + { + "name": "payload", + "ty": { + "kind": "cell" + } + } + ] + } + ], + "incomingMessages": [], + "outgoingMessages": [], + "emittedEvents": [], + "storage": { + "storageTy": { + "kind": "nullLiteral" + } + }, + "getMethods": [], + "thrownErrors": [], + "compilerName": "tolk", + "compilerVersion": "1.2.0", + "codeBoc64": "te6ccgEBAgEAFQABFP8A9KQT9LzyyAsBAAzTMPiR8kA=" +} \ No newline at end of file diff --git a/tolk/tuples.go b/tolk/tuples.go new file mode 100644 index 00000000..2fcd9d22 --- /dev/null +++ b/tolk/tuples.go @@ -0,0 +1,113 @@ +package tolk + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tolk/parser" +) + +type TupleValues []Value + +func (v *TupleValues) Unmarshal(cell *boc.Cell, ty parser.TupleWith, decoder *Decoder) error { + list := make(TupleValues, len(ty.Items)) + for i, item := range ty.Items { + inner := Value{} + err := inner.Unmarshal(cell, item, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal %v tuple's value: %w", i, err) + } + list[i] = inner + } + *v = list + return nil +} + +func (v *TupleValues) Marshal(cell *boc.Cell, ty parser.TupleWith, encoder *Encoder) error { + for i, item := range []Value(*v) { + err := item.Marshal(cell, ty.Items[i], encoder) + if err != nil { + return fmt.Errorf("failed to marshal %v tuple's value: %w", i, err) + } + } + return nil +} + +func (v *TupleValues) Equal(other any) bool { + otherTupleValues, ok := other.(TupleValues) + if !ok { + return false + } + wV := *v + if len(otherTupleValues) != len(wV) { + return false + } + for i := range wV { + if !wV[i].Equal(otherTupleValues[i]) { + return false + } + } + return true +} + +type TensorValues []Value + +func (v *TensorValues) Unmarshal(cell *boc.Cell, ty parser.Tensor, decoder *Decoder) error { + list := make(TensorValues, len(ty.Items)) + for i, item := range ty.Items { + inner := Value{} + err := inner.Unmarshal(cell, item, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal %v tensor's value: %w", i, err) + } + list[i] = inner + } + *v = list + return nil +} + +func (v *TensorValues) Marshal(cell *boc.Cell, ty parser.Tensor, encoder *Encoder) error { + for i, item := range []Value(*v) { + err := item.Marshal(cell, ty.Items[i], encoder) + if err != nil { + return fmt.Errorf("failed to marshal %v tensor's value: %w", i, err) + } + } + return nil +} + +func (v *TensorValues) Equal(other any) bool { + otherTensorValues, ok := other.(TensorValues) + if !ok { + return false + } + wV := *v + if len(otherTensorValues) != len(wV) { + return false + } + for i := range wV { + if !wV[i].Equal(otherTensorValues[i]) { + return false + } + } + return true +} + +func (v TensorValues) MarshalJSON() ([]byte, error) { + var s strings.Builder + s.WriteRune('[') + for i, item := range []Value(v) { + data, err := json.Marshal(item) + if err != nil { + return nil, fmt.Errorf("failed to marshal %v tensor's value: %w", i, err) + } + s.Write(data) + if i != len([]Value(v))-1 { + s.WriteRune(',') + } + } + s.WriteRune(']') + return []byte(s.String()), nil +} diff --git a/tolk/union.go b/tolk/union.go new file mode 100644 index 00000000..b7bbadc8 --- /dev/null +++ b/tolk/union.go @@ -0,0 +1,115 @@ +package tolk + +import ( + "fmt" + + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tolk/parser" +) + +type UnionValue struct { + Prefix Prefix + Val Value +} + +func (u *UnionValue) Unmarshal(cell *boc.Cell, ty parser.Union, decoder *Decoder) error { + unionV := UnionValue{} + if len(ty.Variants) < 2 { + return fmt.Errorf("union length must be at least 2") + } + prefixLen := ty.Variants[0].PrefixLen + eatPrefix := ty.Variants[0].PrefixEatInPlace + if prefixLen > 64 { + // todo: maybe prefix len can be bigger than 64? + return fmt.Errorf("union prefix length must be less than or equal to 64") + } + + var prefix uint64 + var err error + if !eatPrefix { + copyCell := cell.CopyRemaining() + prefix, err = copyCell.ReadUint(prefixLen) + } else { + prefix, err = cell.ReadUint(prefixLen) + } + if err != nil { + return fmt.Errorf("failed to read union's %v-length prefix: %w", prefixLen, err) + } + + for _, variant := range ty.Variants { + variantPrefix, err := PrefixToUint(variant.PrefixStr) + if err != nil { + return fmt.Errorf("failed to read union's variant prefi %v: %w", variant.PrefixStr, err) + } + + if prefix == variantPrefix { + unionV.Prefix = Prefix{ + Len: int16(variant.PrefixLen), + Prefix: prefix, + } + innerV := Value{} + err = innerV.Unmarshal(cell, variant.VariantTy, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal union value: %w", err) + } + unionV.Val = innerV + *u = unionV + + return nil + } + } + + return fmt.Errorf("none of union prefixes matched") +} + +func (u *UnionValue) Marshal(cell *boc.Cell, ty parser.Union, encoder *Encoder) error { + if len(ty.Variants) < 2 { + return fmt.Errorf("union length must be at least 2") + } + if u.Prefix.Len > 64 { + return fmt.Errorf("union prefix length must be less then or equals to 64") + } + + if ty.Variants[0].PrefixEatInPlace { + err := cell.WriteUint(u.Prefix.Prefix, int(u.Prefix.Len)) + if err != nil { + return fmt.Errorf("failed to write union's %v-length prefix: %w", u.Prefix.Len, err) + } + } + + for _, variant := range ty.Variants { + variantPrefix, err := PrefixToUint(variant.PrefixStr) + if err != nil { + return fmt.Errorf("failed to parse union's variant prefix %v: %w", variant.PrefixStr, err) + } + + if u.Prefix.Prefix == variantPrefix { + err = u.Val.Marshal(cell, variant.VariantTy, encoder) + if err != nil { + return fmt.Errorf("failed to marshal union value: %w", err) + } + return nil + } + } + + return fmt.Errorf("none of union prefixes matched") +} + +func (u *UnionValue) Equal(other any) bool { + otherUnionValue, ok := other.(UnionValue) + if !ok { + return false + } + if u.Prefix != otherUnionValue.Prefix { + return false + } + return u.Val.Equal(otherUnionValue.Val) +} + +func (u UnionValue) MarshalJSON() ([]byte, error) { + data, err := u.Val.MarshalJSON() + if err != nil { + return nil, fmt.Errorf("failed to marshal ref: %w", err) + } + return data, nil +} diff --git a/tolk/utils.go b/tolk/utils.go new file mode 100644 index 00000000..88df53e7 --- /dev/null +++ b/tolk/utils.go @@ -0,0 +1,66 @@ +package tolk + +import ( + "errors" + "fmt" + "strconv" +) + +func binHexToUint64(s string) (uint64, error) { + if len(s) <= 2 { + return 0, errors.New("number length must be greater than 2") + } + + if s[1] == 'b' { + val, err := strconv.ParseUint(s[2:], 2, 64) + if err != nil { + return 0, fmt.Errorf("invalid bin number: %v", err) + } + return val, nil + } else if s[1] == 'x' { + val, err := strconv.ParseUint(s[2:], 16, 64) + if err != nil { + return 0, fmt.Errorf("invalid hex number: %v", err) + } + return val, nil + } else { + return 0, fmt.Errorf("invalid number base, must be either bin or hex, got") + } +} + +func PrefixToUint(prefix string) (uint64, error) { + if prefix == "" { + return 0, errors.New("invalid prefix") + } + + if len(prefix) == 1 { + intPrefix, err := strconv.ParseUint(prefix, 10, 64) + if err != nil { + return 0, fmt.Errorf("invalid dec prefix: %v", err) + } + + return intPrefix, nil + } + + if len(prefix) == 2 { + return 0, fmt.Errorf("prefix tag len must be either 1 or >2") + } + + var intPrefix uint64 + var err error + if prefix[1] == 'b' { + intPrefix, err = strconv.ParseUint(prefix[2:], 2, 64) + if err != nil { + return 0, fmt.Errorf("invalid bin prefix: %v", err) + } + } else if prefix[1] == 'x' { + intPrefix, err = strconv.ParseUint(prefix[2:], 16, 64) + if err != nil { + return 0, fmt.Errorf("invalid hex prefix: %v", err) + } + } else { + return 0, fmt.Errorf("prefix tag must be either binary or hex format") + } + + return intPrefix, nil +} diff --git a/tolk/value.go b/tolk/value.go new file mode 100644 index 00000000..488796d0 --- /dev/null +++ b/tolk/value.go @@ -0,0 +1,1086 @@ +package tolk + +// todo: move this to some package or rename somehow. + +import ( + "encoding/json" + "fmt" + "math/big" + "strings" + + "github.com/tonkeeper/tongo/boc" + "github.com/tonkeeper/tongo/tolk/parser" + "github.com/tonkeeper/tongo/utils" +) + +type SumType string + +func (s SumType) MarshalJSON() ([]byte, error) { + return []byte(`"` + utils.ToCamelCasePrivate(string(s)) + `"`), nil +} + +func (s *SumType) UnmarshalJSON(bytes []byte) error { + if len(bytes) < 2 { + return fmt.Errorf("invalid SumType value: %s", string(bytes)) + } + *s = SumType(bytes[1 : len(bytes)-1]) + return nil +} + +type Value struct { + SumType SumType + Bool *BoolValue + SmallInt *Int64 + SmallUint *UInt64 + BigInt *BigInt + BigUint *BigUInt + VarInt *VarInt + VarUint *VarUInt + Coins *CoinsValue + Bits *Bits + Cell *Any + Remaining *RemainingValue + InternalAddress *InternalAddress + OptionalAddress *OptionalAddress + ExternalAddress *ExternalAddress + AnyAddress *AnyAddress + OptionalValue *OptValue + RefValue *RefValue + TupleWith *TupleValues + Tensor *TensorValues + Map *MapValue + Struct *Struct + Alias *AliasValue + Enum *EnumValue + Generic *GenericValue + Union *UnionValue + Null *NullValue + Void *VoidValue +} + +func (v *Value) GetBool() (bool, bool) { + if v.Bool == nil { + return false, false + } + return bool(*v.Bool), true +} + +func (v *Value) MustGetBool() bool { + if v.Bool == nil { + panic("value is not a bool") + } + return bool(*v.Bool) +} + +func (v *Value) GetSmallInt() (int64, bool) { + if v.SmallInt == nil { + return 0, false + } + return int64(*v.SmallInt), true +} + +func (v *Value) MustGetSmallInt() int64 { + if v.SmallInt == nil { + panic("value is not a small int") + } + return int64(*v.SmallInt) +} + +func (v *Value) GetBigInt() (big.Int, bool) { + if v.BigInt == nil { + return big.Int{}, false + } + return big.Int(*v.BigInt), true +} + +func (v *Value) MustGetBigInt() big.Int { + if v.BigInt == nil { + panic("value is not a big int") + } + return big.Int(*v.BigInt) +} + +func (v *Value) GetSmallUInt() (uint64, bool) { + if v.SmallUint == nil { + return 0, false + } + return uint64(*v.SmallUint), true +} + +func (v *Value) MustGetSmallUInt() uint64 { + if v.SmallUint == nil { + panic("value is not a small uint") + } + return uint64(*v.SmallUint) +} + +func (v *Value) GetBigUInt() (big.Int, bool) { + if v.BigUint == nil { + return big.Int{}, false + } + return big.Int(*v.BigUint), true +} + +func (v *Value) MustGetBigUInt() big.Int { + if v.BigUint == nil { + panic("value is not a big uint") + } + return big.Int(*v.BigUint) +} + +func (v *Value) GetVarInt() (big.Int, bool) { + if v.VarInt == nil { + return big.Int{}, false + } + return big.Int(*v.VarInt), true +} + +func (v *Value) MustGetVarInt() big.Int { + if v.VarInt == nil { + panic("value is not a var int") + } + return big.Int(*v.VarInt) +} + +func (v *Value) GetVarUInt() (big.Int, bool) { + if v.VarUint == nil { + return big.Int{}, false + } + return big.Int(*v.VarUint), true +} + +func (v *Value) MustGetVarUInt() big.Int { + if v.VarUint == nil { + panic("value is not a var uint") + } + return big.Int(*v.VarUint) +} + +func (v *Value) GetCoins() (big.Int, bool) { + if v.Coins == nil { + return big.Int{}, false + } + return big.Int(*v.Coins), true +} + +func (v *Value) MustGetCoins() big.Int { + if v.Coins == nil { + panic("value is not a coins") + } + return big.Int(*v.Coins) +} + +func (v *Value) GetBits() (boc.BitString, bool) { + if v.Bits == nil { + return boc.BitString{}, false + } + return boc.BitString(*v.Bits), true +} + +func (v *Value) MustGetBits() boc.BitString { + if v.Bits == nil { + panic("value is not a bits") + } + return boc.BitString(*v.Bits) +} + +func (v *Value) GetAddress() (InternalAddress, bool) { + if v.InternalAddress == nil { + return InternalAddress{}, false + } + return *v.InternalAddress, true +} + +func (v *Value) MustGetAddress() InternalAddress { + if v.InternalAddress == nil { + panic("value is not an internal address") + } + return *v.InternalAddress +} + +func (v *Value) GetOptionalAddress() (OptionalAddress, bool) { + if v.OptionalAddress == nil { + return OptionalAddress{}, false + } + return *v.OptionalAddress, true +} + +func (v *Value) MustGetOptionalAddress() OptionalAddress { + if v.OptionalAddress == nil { + panic("value is not an optional address") + } + return *v.OptionalAddress +} + +func (v *Value) GetExternalAddress() (ExternalAddress, bool) { + if v.ExternalAddress == nil { + return ExternalAddress{}, false + } + return *v.ExternalAddress, true +} + +func (v *Value) MustGetExternalAddress() ExternalAddress { + if v.ExternalAddress == nil { + panic("value is not an external address") + } + return *v.ExternalAddress +} + +func (v *Value) GetAnyAddress() (AnyAddress, bool) { + if v.AnyAddress == nil { + return AnyAddress{}, false + } + return *v.AnyAddress, true +} + +func (v *Value) MustGetAnyAddress() AnyAddress { + if v.AnyAddress == nil { + panic("value is not an any address") + } + return *v.AnyAddress +} + +func (v *Value) GetOptionalValue() (OptValue, bool) { + if v.OptionalValue == nil { + return OptValue{}, false + } + return *v.OptionalValue, true +} + +func (v *Value) MustGetOptionalValue() OptValue { + if v.OptionalValue == nil { + panic("value is not an optional value") + } + return *v.OptionalValue +} + +func (v *Value) GetRefValue() (Value, bool) { + if v.RefValue == nil { + return Value{}, false + } + return Value(*v.RefValue), true +} + +func (v *Value) MustGetRefValue() Value { + if v.RefValue == nil { + panic("value is not a reference") + } + return Value(*v.RefValue) +} + +func (v *Value) GetTensor() ([]Value, bool) { + if v.Tensor == nil { + return TensorValues{}, false + } + return *v.Tensor, true +} + +func (v *Value) MustGetTensor() []Value { + if v.Tensor == nil { + panic("value is not a tensor") + } + return *v.Tensor +} + +func (v *Value) GetMap() (MapValue, bool) { + if v.Map == nil { + return MapValue{}, false + } + return *v.Map, true +} + +func (v *Value) MustGetMap() MapValue { + if v.Map == nil { + panic("value is not a map") + } + return *v.Map +} + +func (v *Value) GetStruct() (Struct, bool) { + if v.Struct == nil { + return Struct{}, false + } + return *v.Struct, true +} + +func (v *Value) MustGetStruct() Struct { + if v.Struct == nil { + panic("value is not a struct") + } + return *v.Struct +} + +func (v *Value) GetAlias() (Value, bool) { + if v.Alias == nil { + return Value{}, false + } + return Value(*v.Alias), true +} + +func (v *Value) MustGetAlias() Value { + if v.Alias == nil { + panic("value is not an alias") + } + return Value(*v.Alias) +} + +func (v *Value) GetGeneric() (Value, bool) { + if v.Generic == nil { + return Value{}, false + } + return Value(*v.Generic), true +} + +func (v *Value) MustGetGeneric() Value { + if v.Generic == nil { + panic("value is not a generic") + } + return Value(*v.Generic) +} + +func (v *Value) GetEnum() (EnumValue, bool) { + if v.Enum == nil { + return EnumValue{}, false + } + return *v.Enum, true +} + +func (v *Value) MustGetEnum() EnumValue { + if v.Enum == nil { + panic("value is not an enum") + } + return *v.Enum +} + +func (v *Value) GetUnion() (UnionValue, bool) { + if v.Union == nil { + return UnionValue{}, false + } + return *v.Union, true +} + +func (v *Value) MustGetUnion() UnionValue { + if v.Union == nil { + panic("value is not an union") + } + return *v.Union +} + +func (v *Value) GetTupleValues() ([]Value, bool) { + if v.TupleWith == nil { + return TupleValues{}, false + } + return *v.TupleWith, true +} + +func (v *Value) MustGetTupleValues() []Value { + if v.TupleWith == nil { + panic("value is not a tuple") + } + return *v.TupleWith +} + +func (v *Value) GetCell() (boc.Cell, bool) { + if v.Cell == nil { + return boc.Cell{}, false + } + return boc.Cell(*v.Cell), true +} + +func (v *Value) MustGetCell() boc.Cell { + if v.Cell == nil { + panic("value is not a cell") + } + return boc.Cell(*v.Cell) +} + +func (v *Value) GetRemaining() (boc.Cell, bool) { + if v.Remaining == nil { + return boc.Cell{}, false + } + return boc.Cell(*v.Remaining), true +} + +func (v *Value) MustGetRemaining() boc.Cell { + if v.Remaining == nil { + panic("value is not a remaining") + } + return boc.Cell(*v.Remaining) +} + +func (v *Value) GetType() string { + return string(v.SumType) +} + +func (v *Value) Unmarshal(cell *boc.Cell, ty parser.Ty, decoder *Decoder) error { + var err error + switch ty.SumType { + case "IntN": + if ty.IntN.N <= 64 { + v.SumType = "SmallInt" + def := Int64(0) + v.SmallInt = &def + err = v.SmallInt.Unmarshal(cell, *ty.IntN, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal small int value: %w", err) + } + } else { + v.SumType = "BigInt" + v.BigInt = &BigInt{} + err = v.BigInt.Unmarshal(cell, *ty.IntN, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal big int value: %w", err) + } + } + case "UintN": + if ty.UintN.N <= 64 { + v.SumType = "SmallUint" + def := UInt64(0) + v.SmallUint = &def + err = v.SmallUint.Unmarshal(cell, *ty.UintN, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal small uint value: %w", err) + } + } else { + v.SumType = "BigUint" + v.BigUint = &BigUInt{} + err = v.BigUint.Unmarshal(cell, *ty.UintN, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal big uint value: %w", err) + } + } + case "VarIntN": + v.SumType = "VarInt" + v.VarInt = &VarInt{} + err = v.VarInt.Unmarshal(cell, *ty.VarIntN, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal var int value: %w", err) + } + case "VarUintN": + v.SumType = "VarUint" + v.VarUint = &VarUInt{} + err = v.VarUint.Unmarshal(cell, *ty.VarUintN, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal var uint value: %w", err) + } + case "BitsN": + v.SumType = "Bits" + v.Bits = &Bits{} + err = v.Bits.Unmarshal(cell, *ty.BitsN, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal bits value: %w", err) + } + case "Nullable": + v.SumType = "OptionalValue" + v.OptionalValue = &OptValue{} + err = v.OptionalValue.Unmarshal(cell, *ty.Nullable, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal nullable value: %w", err) + } + case "CellOf": + v.SumType = "RefValue" + v.RefValue = &RefValue{} + err = v.RefValue.Unmarshal(cell, *ty.CellOf, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal cell of: %w", err) + } + case "Tensor": + v.SumType = "Tensor" + v.Tensor = &TensorValues{} + err = v.Tensor.Unmarshal(cell, *ty.Tensor, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal tensor value: %w", err) + } + case "TupleWith": + v.SumType = "TupleWith" + v.TupleWith = &TupleValues{} + err = v.TupleWith.Unmarshal(cell, *ty.TupleWith, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal tuple value: %w", err) + } + case "Map": + v.SumType = "Map" + v.Map = &MapValue{} + err = v.Map.Unmarshal(cell, *ty.Map, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal map value: %w", err) + } + case "EnumRef": + v.SumType = "Enum" + v.Enum = &EnumValue{} + err = v.Enum.Unmarshal(cell, *ty.EnumRef, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal enum value: %w", err) + } + case "StructRef": + v.SumType = "Struct" + v.Struct = &Struct{} + err = v.Struct.Unmarshal(cell, *ty.StructRef, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal struct value: %w", err) + } + case "AliasRef": + v.SumType = "Alias" + v.Alias = &AliasValue{} + err = v.Alias.Unmarshal(cell, *ty.AliasRef, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal alias value: %w", err) + } + case "Generic": + v.SumType = "Generic" + v.Generic = &GenericValue{} + err = v.Generic.Unmarshal(cell, *ty.Generic, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal generic value: %w", err) + } + case "Union": + v.SumType = "Union" + v.Union = &UnionValue{} + err = v.Union.Unmarshal(cell, *ty.Union, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal union value: %w", err) + } + case "Int": + err = fmt.Errorf("failed to unmarshal int value: int is not supported") + case "Coins": + v.SumType = "Coins" + v.Coins = &CoinsValue{} + err = v.Coins.Unmarshal(cell, *ty.Coins, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal coins value: %w", err) + } + case "Bool": + v.SumType = "Bool" + def := BoolValue(false) + v.Bool = &def + err = v.Bool.Unmarshal(cell, *ty.Bool, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal bool value: %w", err) + } + case "Cell": + v.SumType = "Cell" + v.Cell = &Any{} + err = v.Cell.Unmarshal(cell, *ty.Cell, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal cell value: %w", err) + } + case "Slice": + err = fmt.Errorf("failed to unmarshal slice value: slice is not supported") + case "Builder": + err = fmt.Errorf("failed to unmarshal builder value: builder is not supported") + case "Callable": + err = fmt.Errorf("failed to unmarshal callable value: callable is not supported") + case "Remaining": + v.SumType = "Remaining" + v.Remaining = &RemainingValue{} + err = v.Remaining.Unmarshal(cell, *ty.Remaining, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal remaining value: %w", err) + } + case "Address": + v.SumType = "InternalAddress" + v.InternalAddress = &InternalAddress{} + err = v.InternalAddress.Unmarshal(cell, *ty.Address, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal internal address value: %w", err) + } + case "AddressOpt": + v.SumType = "OptionalAddress" + v.OptionalAddress = &OptionalAddress{} + err = v.OptionalAddress.Unmarshal(cell, *ty.AddressOpt, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal optional address value: %w", err) + } + case "AddressExt": + v.SumType = "ExternalAddress" + v.ExternalAddress = &ExternalAddress{} + err = v.ExternalAddress.Unmarshal(cell, *ty.AddressExt, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal external address value: %w", err) + } + case "AddressAny": + v.SumType = "AnyAddress" + v.AnyAddress = &AnyAddress{} + err = v.AnyAddress.Unmarshal(cell, *ty.AddressAny, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal any address value: %w", err) + } + case "TupleAny": + err = fmt.Errorf("failed to unmarshal tuple any value: tuple any is not supported") + case "NullLiteral": + v.SumType = "Null" + v.Null = &NullValue{} + err = v.Null.Unmarshal(cell, *ty.NullLiteral, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal null value: %w", err) + } + case "Void": + v.SumType = "Void" + v.Void = &VoidValue{} + err = v.Void.Unmarshal(cell, *ty.Void, decoder) + if err != nil { + return fmt.Errorf("failed to unmarshal void value: %w", err) + } + default: + return fmt.Errorf("unknown ty type %q", ty.SumType) + } + return nil +} + +func (v *Value) Marshal(cell *boc.Cell, ty parser.Ty, encoder *Encoder) error { + var err error + switch ty.SumType { + case "IntN": + if ty.IntN.N <= 64 { + if v.SumType != "SmallInt" { + return fmt.Errorf("expected SmallInt, but got %v", v.SumType) + } + err = v.SmallInt.Marshal(cell, *ty.IntN, encoder) + if err != nil { + return fmt.Errorf("failed to marshal small int value: %w", err) + } + } else { + if v.SumType != "BigInt" { + return fmt.Errorf("expected BigInt, but got %v", v.SumType) + } + err = v.BigInt.Marshal(cell, *ty.IntN, encoder) + if err != nil { + return fmt.Errorf("failed to marshal big int value: %w", err) + } + } + case "UintN": + if ty.UintN.N <= 64 { + if v.SumType != "SmallUint" { + return fmt.Errorf("expected SmallUint, but got %v", v.SumType) + } + err = v.SmallUint.Marshal(cell, *ty.UintN, encoder) + if err != nil { + return fmt.Errorf("failed to marshal small uint value: %w", err) + } + } else { + if v.SumType != "BigUint" { + return fmt.Errorf("expected BigUint, but got %v", v.SumType) + } + err = v.BigUint.Marshal(cell, *ty.UintN, encoder) + if err != nil { + return fmt.Errorf("failed to marshal big uint value: %w", err) + } + } + case "VarIntN": + if v.SumType != "VarInt" { + return fmt.Errorf("expected VarInt, but got %v", v.SumType) + } + err = v.VarInt.Marshal(cell, *ty.VarIntN, encoder) + if err != nil { + return fmt.Errorf("failed to marshal var int value: %w", err) + } + case "VarUintN": + if v.SumType != "VarUint" { + return fmt.Errorf("expected VarUint, but got %v", v.SumType) + } + err = v.VarUint.Marshal(cell, *ty.VarUintN, encoder) + if err != nil { + return fmt.Errorf("failed to marshal var uint value: %w", err) + } + case "BitsN": + if v.SumType != "Bits" { + return fmt.Errorf("expected Bits, but got %v", v.SumType) + } + err = v.Bits.Marshal(cell, *ty.BitsN, encoder) + if err != nil { + return fmt.Errorf("failed to marshal bits value: %w", err) + } + case "Nullable": + if v.SumType != "OptionalValue" { + return fmt.Errorf("expected OptionalValue, but got %v", v.SumType) + } + err = v.OptionalValue.Marshal(cell, *ty.Nullable, encoder) + if err != nil { + return fmt.Errorf("failed to marshal nullable value: %w", err) + } + case "CellOf": + if v.SumType != "RefValue" { + return fmt.Errorf("expected RefValue, but got %v", v.SumType) + } + err = v.RefValue.Marshal(cell, *ty.CellOf, encoder) + if err != nil { + return fmt.Errorf("failed to marshal cell of value: %w", err) + } + case "Tensor": + if v.SumType != "Tensor" { + return fmt.Errorf("expected Tensor, but got %v", v.SumType) + } + err = v.Tensor.Marshal(cell, *ty.Tensor, encoder) + if err != nil { + return fmt.Errorf("failed to marshal tensor value: %w", err) + } + case "TupleWith": + if v.SumType != "TupleWith" { + return fmt.Errorf("expected TupleWith, but got %v", v.SumType) + } + err = v.TupleWith.Marshal(cell, *ty.TupleWith, encoder) + if err != nil { + return fmt.Errorf("failed to marshal tuple value: %w", err) + } + case "Map": + if v.SumType != "Map" { + return fmt.Errorf("expected Map, but got %v", v.SumType) + } + err = v.Map.Marshal(cell, *ty.Map, encoder) + if err != nil { + return fmt.Errorf("failed to marshal map value: %w", err) + } + case "EnumRef": + if v.SumType != "Enum" { + return fmt.Errorf("expected Enum, but got %v", v.SumType) + } + err = v.Enum.Marshal(cell, *ty.EnumRef, encoder) + if err != nil { + return fmt.Errorf("failed to marshal enum value: %w", err) + } + case "StructRef": + if v.SumType != "Struct" { + return fmt.Errorf("expected Struct, but got %v", v.SumType) + } + err = v.Struct.Marshal(cell, *ty.StructRef, encoder) + if err != nil { + return fmt.Errorf("failed to marshal struct value: %w", err) + } + case "AliasRef": + if v.SumType != "Alias" { + return fmt.Errorf("expected Alias, but got %v", v.SumType) + } + err = v.Alias.Marshal(cell, *ty.AliasRef, encoder) + if err != nil { + return fmt.Errorf("failed to marshal alias value: %w", err) + } + case "Generic": + if v.SumType != "Generic" { + return fmt.Errorf("expected Generic, but got %v", v.SumType) + } + err = v.Generic.Marshal(cell, *ty.Generic, encoder) + if err != nil { + return fmt.Errorf("failed to marshal generic value: %w", err) + } + case "Union": + if v.SumType != "Union" { + return fmt.Errorf("expected Union, but got %v", v.SumType) + } + err = v.Union.Marshal(cell, *ty.Union, encoder) + if err != nil { + return fmt.Errorf("failed to marshal union value: %w", err) + } + case "Int": + err = fmt.Errorf("failed to marshal int value: int is not supported") + case "Coins": + if v.SumType != "Coins" { + return fmt.Errorf("expected Coins, but got %v", v.SumType) + } + err = v.Coins.Marshal(cell, *ty.Coins, encoder) + if err != nil { + return fmt.Errorf("failed to marshal coins value: %w", err) + } + case "Bool": + if v.SumType != "Bool" { + return fmt.Errorf("expected Bool, but got %v", v.SumType) + } + err = v.Bool.Marshal(cell, *ty.Bool, encoder) + if err != nil { + return fmt.Errorf("failed to marshal bool value: %w", err) + } + case "Cell": + if v.SumType != "Cell" { + return fmt.Errorf("expected Cell, but got %v", v.SumType) + } + err = v.Cell.Marshal(cell, *ty.Cell, encoder) + if err != nil { + return fmt.Errorf("failed to marshal cell value: %w", err) + } + case "Slice": + err = fmt.Errorf("failed to marshal slice value: slice is not supported") + case "Builder": + err = fmt.Errorf("failed to marshal builder value: builder is not supported") + case "Callable": + err = fmt.Errorf("failed to marshal int callable: callable is not supported") + case "Remaining": + if v.SumType != "Remaining" { + return fmt.Errorf("expected Remaining, but got %v", v.SumType) + } + err = v.Remaining.Marshal(cell, *ty.Remaining, encoder) + if err != nil { + return fmt.Errorf("failed to marshal remaining value: %w", err) + } + case "Address": + if v.SumType != "InternalAddress" { + return fmt.Errorf("expected InternalAddress, but got %v", v.SumType) + } + err = v.InternalAddress.Marshal(cell, *ty.Address, encoder) + if err != nil { + return fmt.Errorf("failed to marshal internal address value: %w", err) + } + case "AddressOpt": + if v.SumType != "OptionalAddress" { + return fmt.Errorf("expected OptionalAddress, but got %v", v.SumType) + } + err = v.OptionalAddress.Marshal(cell, *ty.AddressOpt, encoder) + if err != nil { + return fmt.Errorf("failed to marshal optional address value: %w", err) + } + case "AddressExt": + if v.SumType != "ExternalAddress" { + return fmt.Errorf("expected ExternalAddress, but got %v", v.SumType) + } + err = v.ExternalAddress.Marshal(cell, *ty.AddressExt, encoder) + if err != nil { + return fmt.Errorf("failed to marshal external address value: %w", err) + } + case "AddressAny": + if v.SumType != "AnyAddress" { + return fmt.Errorf("expected AnyAddress, but got %v", v.SumType) + } + err = v.AnyAddress.Marshal(cell, *ty.AddressAny, encoder) + if err != nil { + return fmt.Errorf("failed to marshal any address value: %w", err) + } + case "TupleAny": + err = fmt.Errorf("failed to marshal tuple any value: tuple any not supported") + case "NullLiteral": + if v.SumType != "Null" { + return fmt.Errorf("expected Null, but got %v", v.SumType) + } + err = v.Null.Marshal(cell, *ty.NullLiteral, encoder) + if err != nil { + return fmt.Errorf("failed to marshal null value: %w", err) + } + case "Void": + if v.SumType != "Void" { + return fmt.Errorf("expected Void, but got %v", v.SumType) + } + err = v.Void.Marshal(cell, *ty.Void, encoder) + if err != nil { + return fmt.Errorf("failed to marshal void value: %w", err) + } + default: + err = fmt.Errorf("unknown ty type %q", ty.SumType) + } + return nil +} + +func (v *Value) Equal(o any) bool { + otherValue, ok := o.(Value) + if !ok { + return false + } + + switch v.SumType { + case "Bool": + if otherValue.Bool == nil { + return false + } + return v.Bool.Equal(*otherValue.Bool) + case "SmallInt": + if otherValue.SmallInt == nil { + return false + } + return v.SmallInt.Equal(*otherValue.SmallInt) + case "SmallUint": + if otherValue.SmallUint == nil { + return false + } + return v.SmallUint.Equal(*otherValue.SmallUint) + case "BigInt": + if otherValue.BigInt == nil { + return false + } + return v.BigInt.Equal(*otherValue.BigInt) + case "BigUint": + if otherValue.BigUint == nil { + return false + } + return v.BigUint.Equal(*otherValue.BigUint) + case "VarInt": + if otherValue.VarInt == nil { + return false + } + return v.VarInt.Equal(*otherValue.VarInt) + case "VarUint": + if otherValue.VarUint == nil { + return false + } + return v.VarUint.Equal(*otherValue.VarUint) + case "Coins": + if otherValue.Coins == nil { + return false + } + return v.Coins.Equal(*otherValue.Coins) + case "Bits": + if otherValue.Bits == nil { + return false + } + return v.Bits.Equal(*otherValue.Bits) + case "Cell": + if otherValue.Cell == nil { + return false + } + return v.Cell.Equal(*otherValue.Cell) + case "Remaining": + if otherValue.Remaining == nil { + return false + } + return v.Remaining.Equal(*otherValue.Remaining) + case "InternalAddress": + if otherValue.InternalAddress == nil { + return false + } + return v.InternalAddress.Equal(*otherValue.InternalAddress) + case "OptionalAddress": + if otherValue.OptionalAddress == nil { + return false + } + return v.OptionalAddress.Equal(*otherValue.OptionalAddress) + case "ExternalAddress": + if otherValue.ExternalAddress == nil { + return false + } + return v.ExternalAddress.Equal(*otherValue.ExternalAddress) + case "AnyAddress": + if otherValue.AnyAddress == nil { + return false + } + return v.AnyAddress.Equal(*otherValue.AnyAddress) + case "OptionalValue": + if otherValue.OptionalValue == nil { + return false + } + return v.OptionalValue.Equal(*otherValue.OptionalValue) + case "RefValue": + if otherValue.RefValue == nil { + return false + } + return v.RefValue.Equal(*otherValue.RefValue) + case "TupleWith": + if otherValue.TupleWith == nil { + return false + } + return v.TupleWith.Equal(*otherValue.TupleWith) + case "Tensor": + if otherValue.Tensor == nil { + return false + } + return v.Tensor.Equal(*otherValue.Tensor) + case "Map": + if otherValue.Map == nil { + return false + } + return v.Map.Equal(*otherValue.Map) + case "Struct": + if otherValue.Struct == nil { + return false + } + return v.Struct.Equal(*otherValue.Struct) + case "Alias": + if otherValue.Alias == nil { + return false + } + return v.Alias.Equal(*otherValue.Alias) + case "Enum": + if otherValue.Enum == nil { + return false + } + return v.Enum.Equal(*otherValue.Enum) + case "Generic": + if otherValue.Generic == nil { + return false + } + return v.Generic.Equal(*otherValue.Generic) + case "Union": + if otherValue.Union == nil { + return false + } + return v.Union.Equal(*otherValue.Union) + case "Null": + if otherValue.Null == nil { + return false + } + return v.Null.Equal(*otherValue.Null) + case "Void": + if otherValue.Void == nil { + return false + } + return v.Void.Equal(*otherValue.Void) + default: + return false + } +} + +func (v Value) MarshalJSON() ([]byte, error) { + var s strings.Builder + var data []byte + var err error + switch v.SumType { + case "Bool": + data, err = json.Marshal(v.Bool) + case "SmallInt": + data, err = json.Marshal(v.SmallInt) + case "SmallUint": + data, err = json.Marshal(v.SmallUint) + case "BigInt": + data, err = json.Marshal(v.BigInt) + case "BigUint": + data, err = json.Marshal(v.BigUint) + case "VarInt": + data, err = json.Marshal(v.VarInt) + case "VarUint": + data, err = json.Marshal(v.VarUint) + case "Coins": + data, err = json.Marshal(v.Coins) + case "Bits": + data, err = json.Marshal(v.Bits) + case "Cell": + data, err = json.Marshal(v.Cell) + case "Remaining": + data, err = json.Marshal(v.Remaining) + case "InternalAddress": + data, err = json.Marshal(v.InternalAddress) + case "OptionalAddress": + data, err = json.Marshal(v.OptionalAddress) + case "ExternalAddress": + data, err = json.Marshal(v.ExternalAddress) + case "AnyAddress": + data, err = json.Marshal(v.AnyAddress) + case "OptionalValue": + data, err = json.Marshal(v.OptionalValue) + case "RefValue": + data, err = json.Marshal(v.RefValue) + case "TupleWith": + data, err = json.Marshal(v.TupleWith) + case "Tensor": + data, err = json.Marshal(v.Tensor) + case "Map": + data, err = json.Marshal(v.Map) + case "Struct": + data, err = json.Marshal(v.Struct) + case "Alias": + val := Value(*v.Alias) + data, err = json.Marshal(val) + case "Enum": + data, err = json.Marshal(v.Enum) + case "Generic": + val := Value(*v.Generic) + data, err = json.Marshal(val) + case "Union": + data, err = json.Marshal(v.Union) + case "Null": + data, err = json.Marshal(v.Null) + case "Void": + data, err = json.Marshal(v.Void) + default: + err = fmt.Errorf("unknown value type: %s", v.SumType) + } + if err != nil { + return nil, fmt.Errorf("failed to marshal value: %w", err) + } + + s.Write(data) + return []byte(s.String()), err +} diff --git a/utils/generator.go b/utils/generator.go index 36d88fe0..ff42509b 100644 --- a/utils/generator.go +++ b/utils/generator.go @@ -52,6 +52,35 @@ func ToCamelCasePrivate(s string) string { return res } +func ToSnakeCase(s string) string { + s = strings.TrimSpace(s) + if s == "" { + return s + } + + n := strings.Builder{} + n.Grow(len(s)) + for i, v := range []byte(s) { + vIsCap := v >= 'A' && v <= 'Z' + vIsLow := v >= 'a' && v <= 'z' + vIsNum := v >= '0' && v <= '9' + + if i != 0 && (vIsCap || v == '_' || v == ' ' || v == '-' || v == '.') { + n.WriteByte('_') + } + + if vIsCap { + v += 'a' + v -= 'A' + } + + if vIsLow || vIsCap || vIsNum { + n.WriteByte(v) + } + } + return n.String() +} + func GetOrderedKeys[M ~map[K]V, K constraints.Ordered, V any](m M) []K { keys := maps.Keys(m) slices.Sort(keys) diff --git a/utils/generator_test.go b/utils/generator_test.go new file mode 100644 index 00000000..729c8ecb --- /dev/null +++ b/utils/generator_test.go @@ -0,0 +1,46 @@ +package utils + +import "testing" + +func TestToSnakeCaseString(t *testing.T) { + tests := []struct { + data string + want string + }{ + { + data: "CocoonTest", + want: "cocoon_test", + }, + { + data: "ton.cocoon.test", + want: "ton_cocoon_test", + }, + { + data: "cocoonTest", + want: "cocoon_test", + }, + { + data: "cocoon123Test", + want: "cocoon123_test", + }, + { + data: "Cocoon123test", + want: "cocoon123test", + }, + { + data: "COcoon123Test", + want: "c_ocoon123_test", + }, + { + data: "FunC", + want: "fun_c", + }, + } + for _, tt := range tests { + t.Run(tt.data, func(t *testing.T) { + if got := ToSnakeCase(tt.data); got != tt.want { + t.Errorf("ToSnakeCase() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/utils/json.go b/utils/json.go new file mode 100644 index 00000000..66f87248 --- /dev/null +++ b/utils/json.go @@ -0,0 +1,16 @@ +package utils + +func ConcatPrefixAndSuffixIfExists(prefix, suffix []byte) []byte { + if len(suffix) == 0 { + return prefix + } + if len(prefix) == 0 { + return suffix + } + prefix = prefix[:len(prefix)-1] // remove '}' + suffix[0] = ',' // replace '{' with ',' + result := make([]byte, 0, len(prefix)+len(suffix)) + result = append(result, prefix...) + result = append(result, suffix...) + return result +}