diff --git a/vms/avm/service_test.go b/vms/avm/service_test.go index 6f44e469a75e..c6e20c6ecb51 100644 --- a/vms/avm/service_test.go +++ b/vms/avm/service_test.go @@ -660,7 +660,7 @@ func TestServiceGetTxJSON_ExportTx(t *testing.T) { } } ], - "memo": "0x", + "memo": null, "destinationChain": "2mcwQKiD8VEspmMJpL1dc7okQQ5dDVAWeCBZ7FWBFAbxpv3t7w", "exportedOutputs": [ { @@ -800,7 +800,7 @@ func TestServiceGetTxJSON_CreateAssetTx(t *testing.T) { } } ], - "memo": "0x", + "memo": null, "name": "Team Rocket", "symbol": "TR", "denomination": 0, @@ -972,7 +972,7 @@ func TestServiceGetTxJSON_OperationTxWithNftxMintOp(t *testing.T) { } } ], - "memo": "0x", + "memo": null, "operations": [ { "assetID": %[4]q, @@ -1117,7 +1117,7 @@ func TestServiceGetTxJSON_OperationTxWithMultipleNftxMintOp(t *testing.T) { } } ], - "memo": "0x", + "memo": null, "operations": [ { "assetID": %[4]q, @@ -1292,7 +1292,7 @@ func TestServiceGetTxJSON_OperationTxWithSecpMintOp(t *testing.T) { } } ], - "memo": "0x", + "memo": null, "operations": [ { "assetID": %[4]q, @@ -1439,7 +1439,7 @@ func TestServiceGetTxJSON_OperationTxWithMultipleSecpMintOp(t *testing.T) { } } ], - "memo": "0x", + "memo": null, "operations": [ { "assetID": %[4]q, @@ -1617,7 +1617,7 @@ func TestServiceGetTxJSON_OperationTxWithPropertyFxMintOp(t *testing.T) { } } ], - "memo": "0x", + "memo": null, "operations": [ { "assetID": %[4]q, @@ -1759,7 +1759,7 @@ func TestServiceGetTxJSON_OperationTxWithPropertyFxMintOpMultiple(t *testing.T) } } ], - "memo": "0x", + "memo": null, "operations": [ { "assetID": %[4]q, diff --git a/vms/platformvm/service_test.go b/vms/platformvm/service_test.go index c896066142e6..7b3252c140e3 100644 --- a/vms/platformvm/service_test.go +++ b/vms/platformvm/service_test.go @@ -234,6 +234,7 @@ func TestGetTx(t *testing.T) { constants.AVMID, []ids.ID{}, "chain name", + common.WithMemo([]byte{}), ) require.NoError(t, err) return tx @@ -266,6 +267,7 @@ func TestGetTx(t *testing.T) { rewardsOwner, rewardsOwner, 0, + common.WithMemo([]byte{}), ) require.NoError(t, err) return tx @@ -289,6 +291,7 @@ func TestGetTx(t *testing.T) { }, }, }}, + common.WithMemo([]byte{}), ) require.NoError(t, err) return tx @@ -792,6 +795,7 @@ func TestGetBlock(t *testing.T) { constants.AVMID, []ids.ID{}, "chain name", + common.WithMemo([]byte{}), ) require.NoError(err) diff --git a/vms/types/blob_data.go b/vms/types/blob_data.go index cee4fe7ba4d8..155d6a29d7e0 100644 --- a/vms/types/blob_data.go +++ b/vms/types/blob_data.go @@ -9,13 +9,36 @@ import ( "github.com/ava-labs/avalanchego/utils/formatting" ) +const nullStr = "null" + // JSONByteSlice represents [[]byte] that is json marshalled to hex type JSONByteSlice []byte func (b JSONByteSlice) MarshalJSON() ([]byte, error) { + if b == nil { + return []byte(nullStr), nil + } + hexData, err := formatting.Encode(formatting.HexNC, b) if err != nil { return nil, err } return json.Marshal(hexData) } + +func (b *JSONByteSlice) UnmarshalJSON(jsonBytes []byte) error { + if string(jsonBytes) == nullStr { + return nil + } + + var hexData string + if err := json.Unmarshal(jsonBytes, &hexData); err != nil { + return err + } + v, err := formatting.Decode(formatting.HexNC, hexData) + if err != nil { + return err + } + *b = v + return nil +} diff --git a/vms/types/blob_data_test.go b/vms/types/blob_data_test.go new file mode 100644 index 000000000000..82c692a9468e --- /dev/null +++ b/vms/types/blob_data_test.go @@ -0,0 +1,56 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package types + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestJSON(t *testing.T) { + tests := []struct { + name string + value JSONByteSlice + expectedJSON string + }{ + { + name: "nil", + value: nil, + expectedJSON: nullStr, + }, + { + name: "empty", + value: []byte{}, + expectedJSON: `"0x"`, + }, + { + name: "not empty", + value: []byte{0, 1, 2, 3}, + expectedJSON: `"0x00010203"`, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + require := require.New(t) + + jsonBytes, err := json.Marshal(test.value) + require.NoError(err) + require.Equal(test.expectedJSON, string(jsonBytes)) + + var unmarshaled JSONByteSlice + require.NoError(json.Unmarshal(jsonBytes, &unmarshaled)) + require.Equal(test.value, unmarshaled) + }) + } +} + +func TestUnmarshalJSONNullKeepsInitialValue(t *testing.T) { + require := require.New(t) + + unmarshaled := JSONByteSlice{1, 2, 3} + require.NoError(json.Unmarshal([]byte(nullStr), &unmarshaled)) + require.Equal(JSONByteSlice{1, 2, 3}, unmarshaled) +}