// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package txs

import (
	"encoding/json"
	"errors"
	"math"
	"testing"

	"github.com/luxfi/mock/gomock"
	"github.com/stretchr/testify/require"

	"github.com/luxfi/runtime"
	"github.com/luxfi/constants"
	"github.com/luxfi/ids"
	lux "github.com/luxfi/utxo"
	luxmock "github.com/luxfi/utxo/luxmock"
	"github.com/luxfi/node/vms/platformvm/fx/fxmock"
	"github.com/luxfi/node/vms/platformvm/stakeable"
	"github.com/luxfi/utils"
	"github.com/luxfi/utxo/secp256k1fx"
	"github.com/luxfi/vm/types"

	safemath "github.com/luxfi/math"
)

var errCustom = errors.New("custom error")

func TestAddPermissionlessPrimaryDelegatorSerialization(t *testing.T) {
	require := require.New(t)

	// Use empty chain ID for serialization test to match expected bytes
	testChainID := ids.Empty

	addr := ids.ShortID{
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77,
	}

	utxoAssetID, err := ids.FromString("d1Rdokz7Vq8H5aczkwgkiPCCa6JME7yT2xpqgWTfFKWYVsGbG")
	require.NoError(err)

	customAssetID := ids.ID{
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
	}

	txID := ids.ID{
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
	}
	nodeID := ids.BuildTestNodeID([]byte{
		0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
		0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
		0x11, 0x22, 0x33, 0x44,
	})

	simpleAddPrimaryTx := &AddPermissionlessDelegatorTx{
		BaseTx: BaseTx{
			BaseTx: lux.BaseTx{
				NetworkID:    constants.MainnetID,
				BlockchainID: testChainID,
				Outs:         []*lux.TransferableOutput{},
				Ins: []*lux.TransferableInput{
					{
						UTXOID: lux.UTXOID{
							TxID:        txID,
							OutputIndex: 1,
						},
						Asset: lux.Asset{
							ID: utxoAssetID,
						},
						In: &secp256k1fx.TransferInput{
							Amt: 2 * constants.KiloLux,
							Input: secp256k1fx.Input{
								SigIndices: []uint32{1},
							},
						},
					},
				},
				Memo: types.JSONByteSlice{},
			},
		},
		Validator: Validator{
			NodeID: nodeID,
			Start:  12345,
			End:    12345 + 200*24*60*60,
			Wght:   2 * constants.KiloLux,
		},
		Chain: constants.PrimaryNetworkID,
		StakeOuts: []*lux.TransferableOutput{
			{
				Asset: lux.Asset{
					ID: utxoAssetID,
				},
				Out: &secp256k1fx.TransferOutput{
					Amt: 2 * constants.KiloLux,
					OutputOwners: secp256k1fx.OutputOwners{
						Locktime:  0,
						Threshold: 1,
						Addrs: []ids.ShortID{
							addr,
						},
					},
				},
			},
		},
		DelegationRewardsOwner: &secp256k1fx.OutputOwners{
			Locktime:  0,
			Threshold: 1,
			Addrs: []ids.ShortID{
				addr,
			},
		},
	}
	lux.SortTransferableOutputs(simpleAddPrimaryTx.Outs)
	lux.SortTransferableOutputs(simpleAddPrimaryTx.StakeOuts)
	utils.Sort(simpleAddPrimaryTx.Ins)
	rt := &runtime.Runtime{
		NetworkID: 1,

		ChainID:  testChainID,
		UTXOAssetID: utxoAssetID,
	}
	require.NoError(simpleAddPrimaryTx.SyntacticVerify(rt))

	expectedUnsignedSimpleAddPrimaryTxBytes := []byte{
		// Codec version
		0x00, 0x01,
		// AddPermissionlessDelegatorTx type ID (post codec-collapse: 30)
		0x00, 0x00, 0x00, 0x1e,
		// Mainnet network ID
		0x00, 0x00, 0x00, 0x01,
		// P-chain blockchain ID
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		// Number of immediate outputs
		0x00, 0x00, 0x00, 0x00,
		// Number of inputs
		0x00, 0x00, 0x00, 0x01,
		// inputs[0]
		// TxID
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		// Tx output index
		0x00, 0x00, 0x00, 0x01,
		// Mainnet LUX asset ID
		0x51, 0xc2, 0x4f, 0xe7, 0xee, 0x02, 0x01, 0xff,
		0x0f, 0x33, 0x5f, 0x51, 0x99, 0x28, 0xdb, 0x6e,
		0xef, 0x62, 0x24, 0x25, 0x45, 0x52, 0xc9, 0x6b,
		0x6b, 0x42, 0x5f, 0xbc, 0x18, 0xfa, 0x24, 0x3b,
		// secp256k1fx transfer input type ID
		0x00, 0x00, 0x00, 0x05,
		// Amount = 2k LUX
		0x00, 0x00, 0x00, 0x00, 0x77, 0x35, 0x94, 0x00,
		// Number of input signature indices
		0x00, 0x00, 0x00, 0x01,
		// signature index
		0x00, 0x00, 0x00, 0x01,
		// memo length
		0x00, 0x00, 0x00, 0x00,
		// NodeID
		0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
		0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
		0x11, 0x22, 0x33, 0x44,
		// Start time
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x39,
		// End time
		0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0xdc, 0x39,
		// Stake weight
		0x00, 0x00, 0x00, 0x00, 0x77, 0x35, 0x94, 0x00,
		// Primary network netID
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		// Number of locked outputs
		0x00, 0x00, 0x00, 0x01,
		// Mainnet LUX asset ID
		0x51, 0xc2, 0x4f, 0xe7, 0xee, 0x02, 0x01, 0xff,
		0x0f, 0x33, 0x5f, 0x51, 0x99, 0x28, 0xdb, 0x6e,
		0xef, 0x62, 0x24, 0x25, 0x45, 0x52, 0xc9, 0x6b,
		0x6b, 0x42, 0x5f, 0xbc, 0x18, 0xfa, 0x24, 0x3b,
		// secp256k1fx transferable output type ID
		0x00, 0x00, 0x00, 0x07,
		// amount
		0x00, 0x00, 0x00, 0x00, 0x77, 0x35, 0x94, 0x00,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		// threshold
		0x00, 0x00, 0x00, 0x01,
		// number of addresses
		0x00, 0x00, 0x00, 0x01,
		// addresses[0]
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77,
		// secp256k1fx owner type ID
		0x00, 0x00, 0x00, 0x0b,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		// threshold
		0x00, 0x00, 0x00, 0x01,
		// number of addresses
		0x00, 0x00, 0x00, 0x01,
		// addresses[0]
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77,
	}
	var unsignedSimpleAddPrimaryTx UnsignedTx = simpleAddPrimaryTx
	unsignedSimpleAddPrimaryTxBytes, err := Codec.Marshal(CodecVersion, &unsignedSimpleAddPrimaryTx)
	require.NoError(err)
	require.Equal(expectedUnsignedSimpleAddPrimaryTxBytes, unsignedSimpleAddPrimaryTxBytes)

	complexAddPrimaryTx := &AddPermissionlessDelegatorTx{
		BaseTx: BaseTx{
			BaseTx: lux.BaseTx{
				NetworkID:    constants.MainnetID,
				BlockchainID: constants.PlatformChainID,
				Outs: []*lux.TransferableOutput{
					{
						Asset: lux.Asset{
							ID: utxoAssetID,
						},
						Out: &secp256k1fx.TransferOutput{
							Amt: 1,
							OutputOwners: secp256k1fx.OutputOwners{
								Locktime:  0,
								Threshold: 1,
								Addrs: []ids.ShortID{
									addr,
								},
							},
						},
					},
					{
						Asset: lux.Asset{
							ID: utxoAssetID,
						},
						Out: &stakeable.LockOut{
							Locktime: 87654321,
							TransferableOut: &secp256k1fx.TransferOutput{
								Amt: 1,
								OutputOwners: secp256k1fx.OutputOwners{
									Locktime:  12345678,
									Threshold: 0,
									Addrs:     []ids.ShortID{},
								},
							},
						},
					},
					{
						Asset: lux.Asset{
							ID: customAssetID,
						},
						Out: &stakeable.LockOut{
							Locktime: 876543210,
							TransferableOut: &secp256k1fx.TransferOutput{
								Amt: 0xffffffffffffffff,
								OutputOwners: secp256k1fx.OutputOwners{
									Locktime:  0,
									Threshold: 1,
									Addrs: []ids.ShortID{
										addr,
									},
								},
							},
						},
					},
				},
				Ins: []*lux.TransferableInput{
					{
						UTXOID: lux.UTXOID{
							TxID:        txID,
							OutputIndex: 1,
						},
						Asset: lux.Asset{
							ID: utxoAssetID,
						},
						In: &secp256k1fx.TransferInput{
							Amt: constants.MegaLux,
							Input: secp256k1fx.Input{
								SigIndices: []uint32{2, 5},
							},
						},
					},
					{
						UTXOID: lux.UTXOID{
							TxID:        txID,
							OutputIndex: 2,
						},
						Asset: lux.Asset{
							ID: customAssetID,
						},
						In: &stakeable.LockIn{
							Locktime: 876543210,
							TransferableIn: &secp256k1fx.TransferInput{
								Amt: 0xefffffffffffffff,
								Input: secp256k1fx.Input{
									SigIndices: []uint32{0},
								},
							},
						},
					},
					{
						UTXOID: lux.UTXOID{
							TxID:        txID,
							OutputIndex: 3,
						},
						Asset: lux.Asset{
							ID: customAssetID,
						},
						In: &secp256k1fx.TransferInput{
							Amt: 0x1000000000000000,
							Input: secp256k1fx.Input{
								SigIndices: []uint32{},
							},
						},
					},
				},
				Memo: types.JSONByteSlice("😅\nwell that's\x01\x23\x45!"),
			},
		},
		Validator: Validator{
			NodeID: nodeID,
			Start:  12345,
			End:    12345 + 200*24*60*60,
			Wght:   5 * constants.KiloLux,
		},
		Chain: constants.PrimaryNetworkID,
		StakeOuts: []*lux.TransferableOutput{
			{
				Asset: lux.Asset{
					ID: utxoAssetID,
				},
				Out: &secp256k1fx.TransferOutput{
					Amt: 2 * constants.KiloLux,
					OutputOwners: secp256k1fx.OutputOwners{
						Locktime:  0,
						Threshold: 1,
						Addrs: []ids.ShortID{
							addr,
						},
					},
				},
			},
			{
				Asset: lux.Asset{
					ID: utxoAssetID,
				},
				Out: &stakeable.LockOut{
					Locktime: 987654321,
					TransferableOut: &secp256k1fx.TransferOutput{
						Amt: 3 * constants.KiloLux,
						OutputOwners: secp256k1fx.OutputOwners{
							Locktime:  87654321,
							Threshold: 0,
							Addrs:     []ids.ShortID{},
						},
					},
				},
			},
		},
		DelegationRewardsOwner: &secp256k1fx.OutputOwners{
			Locktime:  0,
			Threshold: 0,
			Addrs:     []ids.ShortID{},
		},
	}
	rt = &runtime.Runtime{
		NetworkID: 1,

		ChainID:  constants.PlatformChainID,
		UTXOAssetID: utxoAssetID,
	}
	require.NoError(complexAddPrimaryTx.SyntacticVerify(rt))

	expectedUnsignedComplexAddPrimaryTxBytes := []byte{
		// Codec version
		0x00, 0x01,
		// AddPermissionlessDelegatorTx type ID (post codec-collapse: 30)
		0x00, 0x00, 0x00, 0x1e,
		// Mainnet network ID
		0x00, 0x00, 0x00, 0x01,
		// P-chain blockchain ID (31 zeros + 'P')
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,
		// Number of immediate outputs
		0x00, 0x00, 0x00, 0x03,
		// outputs[0]
		// Mainnet LUX asset ID
		0x51, 0xc2, 0x4f, 0xe7, 0xee, 0x02, 0x01, 0xff,
		0x0f, 0x33, 0x5f, 0x51, 0x99, 0x28, 0xdb, 0x6e,
		0xef, 0x62, 0x24, 0x25, 0x45, 0x52, 0xc9, 0x6b,
		0x6b, 0x42, 0x5f, 0xbc, 0x18, 0xfa, 0x24, 0x3b,
		// secp256k1fx transfer output type ID
		0x00, 0x00, 0x00, 0x07,
		// amount
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		// threshold
		0x00, 0x00, 0x00, 0x01,
		// number of addresses
		0x00, 0x00, 0x00, 0x01,
		// addresses[0]
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77,
		// outputs[1]
		// Mainnet LUX asset ID
		0x51, 0xc2, 0x4f, 0xe7, 0xee, 0x02, 0x01, 0xff,
		0x0f, 0x33, 0x5f, 0x51, 0x99, 0x28, 0xdb, 0x6e,
		0xef, 0x62, 0x24, 0x25, 0x45, 0x52, 0xc9, 0x6b,
		0x6b, 0x42, 0x5f, 0xbc, 0x18, 0xfa, 0x24, 0x3b,
		// stakeable locked output type ID
		0x00, 0x00, 0x00, 0x16,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x05, 0x39, 0x7f, 0xb1,
		// secp256k1fx transfer output type ID
		0x00, 0x00, 0x00, 0x07,
		// amount
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x61, 0x4e,
		// threshold
		0x00, 0x00, 0x00, 0x00,
		// number of addresses
		0x00, 0x00, 0x00, 0x00,
		// outputs[2]
		// custom asset ID
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		// stakeable locked output type ID
		0x00, 0x00, 0x00, 0x16,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x34, 0x3e, 0xfc, 0xea,
		// secp256k1fx transfer output type ID
		0x00, 0x00, 0x00, 0x07,
		// amount
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		// threshold
		0x00, 0x00, 0x00, 0x01,
		// number of addresses
		0x00, 0x00, 0x00, 0x01,
		// address[0]
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77,
		// number of inputs
		0x00, 0x00, 0x00, 0x03,
		// inputs[0]
		// TxID
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		// Tx output index
		0x00, 0x00, 0x00, 0x01,
		// Mainnet LUX asset ID
		0x51, 0xc2, 0x4f, 0xe7, 0xee, 0x02, 0x01, 0xff,
		0x0f, 0x33, 0x5f, 0x51, 0x99, 0x28, 0xdb, 0x6e,
		0xef, 0x62, 0x24, 0x25, 0x45, 0x52, 0xc9, 0x6b,
		0x6b, 0x42, 0x5f, 0xbc, 0x18, 0xfa, 0x24, 0x3b,
		// secp256k1fx transfer input type ID
		0x00, 0x00, 0x00, 0x05,
		// amount
		0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x10, 0x00,
		// number of signature indices
		0x00, 0x00, 0x00, 0x02,
		// first signature index
		0x00, 0x00, 0x00, 0x02,
		// second signature index
		0x00, 0x00, 0x00, 0x05,
		// inputs[1]
		// TxID
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		// Tx output index
		0x00, 0x00, 0x00, 0x02,
		// custom asset ID
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		// stakeable locked input type ID
		0x00, 0x00, 0x00, 0x15,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x34, 0x3e, 0xfc, 0xea,
		// secp256k1fx transfer input type ID
		0x00, 0x00, 0x00, 0x05,
		// amount
		0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
		// number of signature indices
		0x00, 0x00, 0x00, 0x01,
		// signature index
		0x00, 0x00, 0x00, 0x00,
		// inputs[2]
		// TxID
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		// Tx output index
		0x00, 0x00, 0x00, 0x03,
		// custom asset ID
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		// secp256k1 transfer input type ID
		0x00, 0x00, 0x00, 0x05,
		// amount
		0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		// number of signature indices
		0x00, 0x00, 0x00, 0x00,
		// memo length
		0x00, 0x00, 0x00, 0x14,
		// memo
		0xf0, 0x9f, 0x98, 0x85, 0x0a, 0x77, 0x65, 0x6c,
		0x6c, 0x20, 0x74, 0x68, 0x61, 0x74, 0x27, 0x73,
		0x01, 0x23, 0x45, 0x21,
		// nodeID
		0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
		0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
		0x11, 0x22, 0x33, 0x44,
		// Start time
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x39,
		// End time
		0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0xdc, 0x39,
		// Stake weight
		0x00, 0x00, 0x00, 0x01, 0x2a, 0x05, 0xf2, 0x00,
		// Primary Network net ID
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		// number of locked outputs
		0x00, 0x00, 0x00, 0x02,
		// Mainnet LUX asset ID
		0x51, 0xc2, 0x4f, 0xe7, 0xee, 0x02, 0x01, 0xff,
		0x0f, 0x33, 0x5f, 0x51, 0x99, 0x28, 0xdb, 0x6e,
		0xef, 0x62, 0x24, 0x25, 0x45, 0x52, 0xc9, 0x6b,
		0x6b, 0x42, 0x5f, 0xbc, 0x18, 0xfa, 0x24, 0x3b,
		// secp256k1 transfer output type ID
		0x00, 0x00, 0x00, 0x07,
		// amount
		0x00, 0x00, 0x00, 0x00, 0x77, 0x35, 0x94, 0x00,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		// threshold
		0x00, 0x00, 0x00, 0x01,
		// number of addresses
		0x00, 0x00, 0x00, 0x01,
		// addresses[0]
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77,
		// Mainnet LUX asset ID
		0x51, 0xc2, 0x4f, 0xe7, 0xee, 0x02, 0x01, 0xff,
		0x0f, 0x33, 0x5f, 0x51, 0x99, 0x28, 0xdb, 0x6e,
		0xef, 0x62, 0x24, 0x25, 0x45, 0x52, 0xc9, 0x6b,
		0x6b, 0x42, 0x5f, 0xbc, 0x18, 0xfa, 0x24, 0x3b,
		// stakeable locked output type ID
		0x00, 0x00, 0x00, 0x16,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x3a, 0xde, 0x68, 0xb1,
		// secp256k1 transfer output type ID
		0x00, 0x00, 0x00, 0x07,
		// amount
		0x00, 0x00, 0x00, 0x00, 0xb2, 0xd0, 0x5e, 0x00,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x05, 0x39, 0x7f, 0xb1,
		// threshold
		0x00, 0x00, 0x00, 0x00,
		// number of addresses
		0x00, 0x00, 0x00, 0x00,
		// secp256k1 owner type ID
		0x00, 0x00, 0x00, 0x0b,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		// threshold
		0x00, 0x00, 0x00, 0x00,
		// number of addresses
		0x00, 0x00, 0x00, 0x00,
	}
	var unsignedComplexAddPrimaryTx UnsignedTx = complexAddPrimaryTx
	unsignedComplexAddPrimaryTxBytes, err := Codec.Marshal(CodecVersion, &unsignedComplexAddPrimaryTx)
	require.NoError(err)
	require.Equal(expectedUnsignedComplexAddPrimaryTxBytes, unsignedComplexAddPrimaryTxBytes)

	// Remove aliaser as BCLookup field doesn't exist in runtime.Runtime
	// This functionality is now handled differently

	rt2 := &runtime.Runtime{
		NetworkID: constants.MainnetID, // Must match tx.NetworkID for "P-lux1..." address encoding

		ChainID:  testChainID,
		UTXOAssetID: utxoAssetID,
	}
	unsignedComplexAddPrimaryTx.InitRuntime(rt2)

	unsignedComplexAddPrimaryTxJSONBytes, err := json.MarshalIndent(unsignedComplexAddPrimaryTx, "", "\t")
	require.NoError(err)
	require.JSONEq(`{
	"networkID": 1,
	"blockchainID": "11111111111111111111111111111111P",
	"outputs": [
		{
			"assetID": "d1Rdokz7Vq8H5aczkwgkiPCCa6JME7yT2xpqgWTfFKWYVsGbG",
			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
			"output": {
				"addresses": [
					"7EKFm18KvWqcxMCNgpBSN51pJnEr1cVUb"
				],
				"amount": 1,
				"locktime": 0,
				"threshold": 1
			}
		},
		{
			"assetID": "d1Rdokz7Vq8H5aczkwgkiPCCa6JME7yT2xpqgWTfFKWYVsGbG",
			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
			"output": {
				"locktime": 87654321,
				"output": {
					"addresses": [],
					"amount": 1,
					"locktime": 12345678,
					"threshold": 0
				}
			}
		},
		{
			"assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc",
			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
			"output": {
				"locktime": 876543210,
				"output": {
					"addresses": [
						"7EKFm18KvWqcxMCNgpBSN51pJnEr1cVUb"
					],
					"amount": 18446744073709551615,
					"locktime": 0,
					"threshold": 1
				}
			}
		}
	],
	"inputs": [
		{
			"txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN",
			"outputIndex": 1,
			"assetID": "d1Rdokz7Vq8H5aczkwgkiPCCa6JME7yT2xpqgWTfFKWYVsGbG",
			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
			"input": {
				"amount": 1000000000000,
				"signatureIndices": [
					2,
					5
				]
			}
		},
		{
			"txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN",
			"outputIndex": 2,
			"assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc",
			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
			"input": {
				"locktime": 876543210,
				"input": {
					"amount": 17293822569102704639,
					"signatureIndices": [
						0
					]
				}
			}
		},
		{
			"txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN",
			"outputIndex": 3,
			"assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc",
			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
			"input": {
				"amount": 1152921504606846976,
				"signatureIndices": []
			}
		}
	],
	"memo": "0xf09f98850a77656c6c2074686174277301234521",
	"validator": {
		"nodeID": "NodeID-2ZbTY9GatRTrfinAoYiYLcf6CvrPAUYgo",
		"start": 12345,
		"end": 17292345,
		"weight": 5000000000
	},
	"chainID": "11111111111111111111111111111111LpoYY",
	"stake": [
		{
			"assetID": "d1Rdokz7Vq8H5aczkwgkiPCCa6JME7yT2xpqgWTfFKWYVsGbG",
			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
			"output": {
				"addresses": [
					"7EKFm18KvWqcxMCNgpBSN51pJnEr1cVUb"
				],
				"amount": 2000000000,
				"locktime": 0,
				"threshold": 1
			}
		},
		{
			"assetID": "d1Rdokz7Vq8H5aczkwgkiPCCa6JME7yT2xpqgWTfFKWYVsGbG",
			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
			"output": {
				"locktime": 987654321,
				"output": {
					"addresses": [],
					"amount": 3000000000,
					"locktime": 87654321,
					"threshold": 0
				}
			}
		}
	],
	"rewardsOwner": {
		"addresses": [],
		"locktime": 0,
		"threshold": 0
	}
}`, string(unsignedComplexAddPrimaryTxJSONBytes))
}

func TestAddPermissionlessNetDelegatorSerialization(t *testing.T) {
	require := require.New(t)
	// Use empty chain ID for serialization test to match expected bytes
	testChainID := ids.Empty

	addr := ids.ShortID{
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77,
	}

	utxoAssetID, err := ids.FromString("d1Rdokz7Vq8H5aczkwgkiPCCa6JME7yT2xpqgWTfFKWYVsGbG")
	require.NoError(err)

	customAssetID := ids.ID{
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
	}

	txID := ids.ID{
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
	}
	nodeID := ids.BuildTestNodeID([]byte{
		0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
		0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
		0x11, 0x22, 0x33, 0x44,
	})
	netID := ids.ID{
		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
		0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
		0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
		0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
	}

	simpleAddNetTx := &AddPermissionlessDelegatorTx{
		BaseTx: BaseTx{
			BaseTx: lux.BaseTx{
				NetworkID:    constants.MainnetID,
				BlockchainID: constants.PlatformChainID,
				Outs:         []*lux.TransferableOutput{},
				Ins: []*lux.TransferableInput{
					{
						UTXOID: lux.UTXOID{
							TxID:        txID,
							OutputIndex: 1,
						},
						Asset: lux.Asset{
							ID: utxoAssetID,
						},
						In: &secp256k1fx.TransferInput{
							Amt: constants.MilliLux,
							Input: secp256k1fx.Input{
								SigIndices: []uint32{1},
							},
						},
					},
					{
						UTXOID: lux.UTXOID{
							TxID:        txID,
							OutputIndex: 2,
						},
						Asset: lux.Asset{
							ID: customAssetID,
						},
						In: &secp256k1fx.TransferInput{
							Amt: 1,
							Input: secp256k1fx.Input{
								SigIndices: []uint32{1},
							},
						},
					},
				},
				Memo: types.JSONByteSlice{},
			},
		},
		Validator: Validator{
			NodeID: nodeID,
			Start:  12345,
			End:    12346,
			Wght:   1,
		},
		Chain: netID,
		StakeOuts: []*lux.TransferableOutput{
			{
				Asset: lux.Asset{
					ID: customAssetID,
				},
				Out: &secp256k1fx.TransferOutput{
					Amt: 1,
					OutputOwners: secp256k1fx.OutputOwners{
						Locktime:  0,
						Threshold: 1,
						Addrs: []ids.ShortID{
							addr,
						},
					},
				},
			},
		},
		DelegationRewardsOwner: &secp256k1fx.OutputOwners{
			Locktime:  0,
			Threshold: 1,
			Addrs: []ids.ShortID{
				addr,
			},
		},
	}
	lux.SortTransferableOutputs(simpleAddNetTx.Outs)
	lux.SortTransferableOutputs(simpleAddNetTx.StakeOuts)
	utils.Sort(simpleAddNetTx.Ins)
	rt := &runtime.Runtime{
		NetworkID: constants.UnitTestID,

		ChainID: ids.GenerateTestID(),
	}
	rt = &runtime.Runtime{
		NetworkID: 1,

		ChainID:  constants.PlatformChainID,
		UTXOAssetID: utxoAssetID,
	}
	require.NoError(simpleAddNetTx.SyntacticVerify(rt))

	expectedUnsignedSimpleAddNetTxBytes := []byte{
		// Codec version
		0x00, 0x01,
		// AddPermissionlessDelegationTx type ID (post codec-collapse: 30)
		0x00, 0x00, 0x00, 0x1e,
		// Mainnet network ID
		0x00, 0x00, 0x00, 0x01,
		// P-chain blockchain ID (31 zeros + 'P')
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,
		// Number of immediate outputs
		0x00, 0x00, 0x00, 0x00,
		// Number of inputs
		0x00, 0x00, 0x00, 0x02,
		// inputs[0]
		// TxID
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		// Tx output index
		0x00, 0x00, 0x00, 0x01,
		// Mainnet LUX asset ID
		0x51, 0xc2, 0x4f, 0xe7, 0xee, 0x02, 0x01, 0xff,
		0x0f, 0x33, 0x5f, 0x51, 0x99, 0x28, 0xdb, 0x6e,
		0xef, 0x62, 0x24, 0x25, 0x45, 0x52, 0xc9, 0x6b,
		0x6b, 0x42, 0x5f, 0xbc, 0x18, 0xfa, 0x24, 0x3b,
		// secp256k1fx transfer input type ID
		0x00, 0x00, 0x00, 0x05,
		// Amount = 1 MilliLUX
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8,
		// Number of input signature indices
		0x00, 0x00, 0x00, 0x01,
		// signature index
		0x00, 0x00, 0x00, 0x01,
		// inputs[1]
		// TxID
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		// Tx output index
		0x00, 0x00, 0x00, 0x02,
		// custom asset ID
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		// secp256k1fx transfer input type ID
		0x00, 0x00, 0x00, 0x05,
		// Amount
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
		// Number of input signature indices
		0x00, 0x00, 0x00, 0x01,
		// signature index
		0x00, 0x00, 0x00, 0x01,
		// memo length
		0x00, 0x00, 0x00, 0x00,
		// NodeID
		0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
		0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
		0x11, 0x22, 0x33, 0x44,
		// Start time
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x39,
		// End time
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x3a,
		// Stake weight
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
		// ChainID
		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
		0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
		0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
		0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
		// Number of locked outputs
		0x00, 0x00, 0x00, 0x01,
		// custom asset ID
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		// secp256k1fx transferable output type ID
		0x00, 0x00, 0x00, 0x07,
		// amount
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		// threshold
		0x00, 0x00, 0x00, 0x01,
		// number of addresses
		0x00, 0x00, 0x00, 0x01,
		// addresses[0]
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77,
		// secp256k1fx owner type ID
		0x00, 0x00, 0x00, 0x0b,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		// threshold
		0x00, 0x00, 0x00, 0x01,
		// number of addresses
		0x00, 0x00, 0x00, 0x01,
		// addresses[0]
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77,
	}
	var unsignedSimpleAddNetTx UnsignedTx = simpleAddNetTx
	unsignedSimpleAddNetTxBytes, err := Codec.Marshal(CodecVersion, &unsignedSimpleAddNetTx)
	require.NoError(err)
	require.Equal(expectedUnsignedSimpleAddNetTxBytes, unsignedSimpleAddNetTxBytes)

	complexAddNetTx := &AddPermissionlessDelegatorTx{
		BaseTx: BaseTx{
			BaseTx: lux.BaseTx{
				NetworkID:    constants.MainnetID,
				BlockchainID: constants.PlatformChainID,
				Outs: []*lux.TransferableOutput{
					{
						Asset: lux.Asset{
							ID: utxoAssetID,
						},
						Out: &secp256k1fx.TransferOutput{
							Amt: 1,
							OutputOwners: secp256k1fx.OutputOwners{
								Locktime:  0,
								Threshold: 1,
								Addrs: []ids.ShortID{
									addr,
								},
							},
						},
					},
					{
						Asset: lux.Asset{
							ID: utxoAssetID,
						},
						Out: &stakeable.LockOut{
							Locktime: 87654321,
							TransferableOut: &secp256k1fx.TransferOutput{
								Amt: 1,
								OutputOwners: secp256k1fx.OutputOwners{
									Locktime:  12345678,
									Threshold: 0,
									Addrs:     []ids.ShortID{},
								},
							},
						},
					},
					{
						Asset: lux.Asset{
							ID: customAssetID,
						},
						Out: &stakeable.LockOut{
							Locktime: 876543210,
							TransferableOut: &secp256k1fx.TransferOutput{
								Amt: 0xfffffffffffffff0,
								OutputOwners: secp256k1fx.OutputOwners{
									Locktime:  0,
									Threshold: 1,
									Addrs: []ids.ShortID{
										addr,
									},
								},
							},
						},
					},
				},
				Ins: []*lux.TransferableInput{
					{
						UTXOID: lux.UTXOID{
							TxID:        txID,
							OutputIndex: 1,
						},
						Asset: lux.Asset{
							ID: utxoAssetID,
						},
						In: &secp256k1fx.TransferInput{
							Amt: constants.MegaLux,
							Input: secp256k1fx.Input{
								SigIndices: []uint32{2, 5},
							},
						},
					},
					{
						UTXOID: lux.UTXOID{
							TxID:        txID,
							OutputIndex: 2,
						},
						Asset: lux.Asset{
							ID: customAssetID,
						},
						In: &stakeable.LockIn{
							Locktime: 876543210,
							TransferableIn: &secp256k1fx.TransferInput{
								Amt: 0xefffffffffffffff,
								Input: secp256k1fx.Input{
									SigIndices: []uint32{0},
								},
							},
						},
					},
					{
						UTXOID: lux.UTXOID{
							TxID:        txID,
							OutputIndex: 3,
						},
						Asset: lux.Asset{
							ID: customAssetID,
						},
						In: &secp256k1fx.TransferInput{
							Amt: 0x1000000000000000,
							Input: secp256k1fx.Input{
								SigIndices: []uint32{},
							},
						},
					},
				},
				Memo: types.JSONByteSlice("😅\nwell that's\x01\x23\x45!"),
			},
		},
		Validator: Validator{
			NodeID: nodeID,
			Start:  12345,
			End:    12345 + 1,
			Wght:   9,
		},
		Chain: netID,
		StakeOuts: []*lux.TransferableOutput{
			{
				Asset: lux.Asset{
					ID: customAssetID,
				},
				Out: &secp256k1fx.TransferOutput{
					Amt: 2,
					OutputOwners: secp256k1fx.OutputOwners{
						Locktime:  0,
						Threshold: 1,
						Addrs: []ids.ShortID{
							addr,
						},
					},
				},
			},
			{
				Asset: lux.Asset{
					ID: customAssetID,
				},
				Out: &stakeable.LockOut{
					Locktime: 987654321,
					TransferableOut: &secp256k1fx.TransferOutput{
						Amt: 7,
						OutputOwners: secp256k1fx.OutputOwners{
							Locktime:  87654321,
							Threshold: 0,
							Addrs:     []ids.ShortID{},
						},
					},
				},
			},
		},
		DelegationRewardsOwner: &secp256k1fx.OutputOwners{
			Locktime:  0,
			Threshold: 0,
			Addrs:     []ids.ShortID{},
		},
	}
	rt = &runtime.Runtime{
		NetworkID: 1,

		ChainID:  constants.PlatformChainID,
		UTXOAssetID: utxoAssetID,
	}
	require.NoError(complexAddNetTx.SyntacticVerify(rt))

	expectedUnsignedComplexAddNetTxBytes := []byte{
		// Codec version
		0x00, 0x01,
		// AddPermissionlessDelegatorTx type ID (post codec-collapse: 30)
		0x00, 0x00, 0x00, 0x1e,
		// Mainnet network ID
		0x00, 0x00, 0x00, 0x01,
		// P-chain blockchain ID (31 zeros + 'P')
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,
		// Number of immediate outputs
		0x00, 0x00, 0x00, 0x03,
		// outputs[0]
		// Mainnet LUX asset ID
		0x51, 0xc2, 0x4f, 0xe7, 0xee, 0x02, 0x01, 0xff,
		0x0f, 0x33, 0x5f, 0x51, 0x99, 0x28, 0xdb, 0x6e,
		0xef, 0x62, 0x24, 0x25, 0x45, 0x52, 0xc9, 0x6b,
		0x6b, 0x42, 0x5f, 0xbc, 0x18, 0xfa, 0x24, 0x3b,
		// secp256k1fx transfer output type ID
		0x00, 0x00, 0x00, 0x07,
		// amount
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		// threshold
		0x00, 0x00, 0x00, 0x01,
		// number of addresses
		0x00, 0x00, 0x00, 0x01,
		// addresses[0]
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77,
		// outputs[1]
		// Mainnet LUX asset ID
		0x51, 0xc2, 0x4f, 0xe7, 0xee, 0x02, 0x01, 0xff,
		0x0f, 0x33, 0x5f, 0x51, 0x99, 0x28, 0xdb, 0x6e,
		0xef, 0x62, 0x24, 0x25, 0x45, 0x52, 0xc9, 0x6b,
		0x6b, 0x42, 0x5f, 0xbc, 0x18, 0xfa, 0x24, 0x3b,
		// stakeable locked output type ID
		0x00, 0x00, 0x00, 0x16,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x05, 0x39, 0x7f, 0xb1,
		// secp256k1fx transfer output type ID
		0x00, 0x00, 0x00, 0x07,
		// amount
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x61, 0x4e,
		// threshold
		0x00, 0x00, 0x00, 0x00,
		// number of addresses
		0x00, 0x00, 0x00, 0x00,
		// outputs[2]
		// custom asset ID
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		// stakeable locked output type ID
		0x00, 0x00, 0x00, 0x16,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x34, 0x3e, 0xfc, 0xea,
		// secp256k1fx transfer output type ID
		0x00, 0x00, 0x00, 0x07,
		// amount
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		// threshold
		0x00, 0x00, 0x00, 0x01,
		// number of addresses
		0x00, 0x00, 0x00, 0x01,
		// address[0]
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77,
		// number of inputs
		0x00, 0x00, 0x00, 0x03,
		// inputs[0]
		// TxID
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		// Tx output index
		0x00, 0x00, 0x00, 0x01,
		// Mainnet LUX asset ID
		0x51, 0xc2, 0x4f, 0xe7, 0xee, 0x02, 0x01, 0xff,
		0x0f, 0x33, 0x5f, 0x51, 0x99, 0x28, 0xdb, 0x6e,
		0xef, 0x62, 0x24, 0x25, 0x45, 0x52, 0xc9, 0x6b,
		0x6b, 0x42, 0x5f, 0xbc, 0x18, 0xfa, 0x24, 0x3b,
		// secp256k1fx transfer input type ID
		0x00, 0x00, 0x00, 0x05,
		// amount
		0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x10, 0x00,
		// number of signature indices
		0x00, 0x00, 0x00, 0x02,
		// first signature index
		0x00, 0x00, 0x00, 0x02,
		// second signature index
		0x00, 0x00, 0x00, 0x05,
		// inputs[1]
		// TxID
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		// Tx output index
		0x00, 0x00, 0x00, 0x02,
		// custom asset ID
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		// stakeable locked input type ID
		0x00, 0x00, 0x00, 0x15,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x34, 0x3e, 0xfc, 0xea,
		// secp256k1fx transfer input type ID
		0x00, 0x00, 0x00, 0x05,
		// amount
		0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
		// number of signature indices
		0x00, 0x00, 0x00, 0x01,
		// signature index
		0x00, 0x00, 0x00, 0x00,
		// inputs[2]
		// TxID
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
		// Tx output index
		0x00, 0x00, 0x00, 0x03,
		// custom asset ID
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		// secp256k1 transfer input type ID
		0x00, 0x00, 0x00, 0x05,
		// amount
		0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		// number of signature indices
		0x00, 0x00, 0x00, 0x00,
		// memo length
		0x00, 0x00, 0x00, 0x14,
		// memo
		0xf0, 0x9f, 0x98, 0x85, 0x0a, 0x77, 0x65, 0x6c,
		0x6c, 0x20, 0x74, 0x68, 0x61, 0x74, 0x27, 0x73,
		0x01, 0x23, 0x45, 0x21,
		// nodeID
		0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
		0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
		0x11, 0x22, 0x33, 0x44,
		// Start time
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x39,
		// End time
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x3a,
		// Stake weight
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
		// netID
		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
		0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
		0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
		0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
		// number of locked outputs
		0x00, 0x00, 0x00, 0x02,
		// custom asset ID
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		// secp256k1 transfer output type ID
		0x00, 0x00, 0x00, 0x07,
		// amount
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		// threshold
		0x00, 0x00, 0x00, 0x01,
		// number of addresses
		0x00, 0x00, 0x00, 0x01,
		// addresses[0]
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
		0x44, 0x55, 0x66, 0x77,
		// custom asset ID
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
		// stakeable locked output type ID
		0x00, 0x00, 0x00, 0x16,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x3a, 0xde, 0x68, 0xb1,
		// secp256k1 transfer output type ID
		0x00, 0x00, 0x00, 0x07,
		// amount
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x05, 0x39, 0x7f, 0xb1,
		// threshold
		0x00, 0x00, 0x00, 0x00,
		// number of addresses
		0x00, 0x00, 0x00, 0x00,
		// secp256k1 owner type ID
		0x00, 0x00, 0x00, 0x0b,
		// locktime
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		// threshold
		0x00, 0x00, 0x00, 0x00,
		// number of addresses
		0x00, 0x00, 0x00, 0x00,
	}
	var unsignedComplexAddNetTx UnsignedTx = complexAddNetTx
	unsignedComplexAddNetTxBytes, err := Codec.Marshal(CodecVersion, &unsignedComplexAddNetTx)
	require.NoError(err)
	require.Equal(expectedUnsignedComplexAddNetTxBytes, unsignedComplexAddNetTxBytes)

	// Remove aliaser as BCLookup field doesn't exist in runtime.Runtime
	// This functionality is now handled differently

	rt3 := &runtime.Runtime{
		NetworkID: constants.MainnetID, // Must match tx.NetworkID for "P-lux1..." address encoding

		ChainID:  testChainID,
		UTXOAssetID: utxoAssetID,
	}
	unsignedComplexAddNetTx.InitRuntime(rt3)

	unsignedComplexAddNetTxJSONBytes, err := json.MarshalIndent(unsignedComplexAddNetTx, "", "\t")
	require.NoError(err)
	require.JSONEq(`{
	"networkID": 1,
	"blockchainID": "11111111111111111111111111111111P",
	"outputs": [
		{
			"assetID": "d1Rdokz7Vq8H5aczkwgkiPCCa6JME7yT2xpqgWTfFKWYVsGbG",
			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
			"output": {
				"addresses": [
					"7EKFm18KvWqcxMCNgpBSN51pJnEr1cVUb"
				],
				"amount": 1,
				"locktime": 0,
				"threshold": 1
			}
		},
		{
			"assetID": "d1Rdokz7Vq8H5aczkwgkiPCCa6JME7yT2xpqgWTfFKWYVsGbG",
			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
			"output": {
				"locktime": 87654321,
				"output": {
					"addresses": [],
					"amount": 1,
					"locktime": 12345678,
					"threshold": 0
				}
			}
		},
		{
			"assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc",
			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
			"output": {
				"locktime": 876543210,
				"output": {
					"addresses": [
						"7EKFm18KvWqcxMCNgpBSN51pJnEr1cVUb"
					],
					"amount": 18446744073709551600,
					"locktime": 0,
					"threshold": 1
				}
			}
		}
	],
	"inputs": [
		{
			"txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN",
			"outputIndex": 1,
			"assetID": "d1Rdokz7Vq8H5aczkwgkiPCCa6JME7yT2xpqgWTfFKWYVsGbG",
			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
			"input": {
				"amount": 1000000000000,
				"signatureIndices": [
					2,
					5
				]
			}
		},
		{
			"txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN",
			"outputIndex": 2,
			"assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc",
			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
			"input": {
				"locktime": 876543210,
				"input": {
					"amount": 17293822569102704639,
					"signatureIndices": [
						0
					]
				}
			}
		},
		{
			"txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN",
			"outputIndex": 3,
			"assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc",
			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
			"input": {
				"amount": 1152921504606846976,
				"signatureIndices": []
			}
		}
	],
	"memo": "0xf09f98850a77656c6c2074686174277301234521",
	"validator": {
		"nodeID": "NodeID-2ZbTY9GatRTrfinAoYiYLcf6CvrPAUYgo",
		"start": 12345,
		"end": 12346,
		"weight": 9
	},
	"chainID": "SkB92YpWm4UpburLz9tEKZw2i67H3FF6YkjaU4BkFUDTG9Xm",
	"stake": [
		{
			"assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc",
			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
			"output": {
				"addresses": [
					"7EKFm18KvWqcxMCNgpBSN51pJnEr1cVUb"
				],
				"amount": 2,
				"locktime": 0,
				"threshold": 1
			}
		},
		{
			"assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc",
			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
			"output": {
				"locktime": 987654321,
				"output": {
					"addresses": [],
					"amount": 7,
					"locktime": 87654321,
					"threshold": 0
				}
			}
		}
	],
	"rewardsOwner": {
		"addresses": [],
		"locktime": 0,
		"threshold": 0
	}
}`, string(unsignedComplexAddNetTxJSONBytes))
}

func TestAddPermissionlessDelegatorTxSyntacticVerify(t *testing.T) {
	type test struct {
		name   string
		txFunc func(*gomock.Controller) *AddPermissionlessDelegatorTx
		err    error
	}

	var (
		networkID = uint32(1337)
		chainID   = ids.GenerateTestID()
	)

	_ = &runtime.Runtime{
		NetworkID: constants.UnitTestID,

		ChainID: chainID,
	}

	// A BaseTx that already passed syntactic verification.
	verifiedBaseTx := BaseTx{
		SyntacticallyVerified: true,
	}

	// A BaseTx that passes syntactic verification.
	validBaseTx := BaseTx{
		BaseTx: lux.BaseTx{
			NetworkID:    networkID,
			BlockchainID: chainID,
		},
	}

	// A BaseTx that fails syntactic verification.
	invalidBaseTx := BaseTx{}

	tests := []test{
		{
			name: "nil tx",
			txFunc: func(*gomock.Controller) *AddPermissionlessDelegatorTx {
				return nil
			},
			err: ErrNilTx,
		},
		{
			name: "already verified",
			txFunc: func(*gomock.Controller) *AddPermissionlessDelegatorTx {
				return &AddPermissionlessDelegatorTx{
					BaseTx: verifiedBaseTx,
				}
			},
			err: nil,
		},
		{
			name: "no provided stake",
			txFunc: func(*gomock.Controller) *AddPermissionlessDelegatorTx {
				return &AddPermissionlessDelegatorTx{
					BaseTx:    validBaseTx,
					StakeOuts: nil,
				}
			},
			err: errNoStake,
		},
		{
			name: "invalid BaseTx",
			txFunc: func(*gomock.Controller) *AddPermissionlessDelegatorTx {
				return &AddPermissionlessDelegatorTx{
					BaseTx: invalidBaseTx,
					Validator: Validator{
						NodeID: ids.GenerateTestNodeID(),
					},
					StakeOuts: []*lux.TransferableOutput{
						{
							Asset: lux.Asset{
								ID: ids.GenerateTestID(),
							},
							Out: &secp256k1fx.TransferOutput{
								Amt: 1,
							},
						},
					},
				}
			},
			err: lux.ErrWrongNetworkID,
		},
		{
			name: "invalid rewards owner",
			txFunc: func(ctrl *gomock.Controller) *AddPermissionlessDelegatorTx {
				rewardsOwner := fxmock.NewOwner(ctrl)
				rewardsOwner.EXPECT().Verify().Return(errCustom)
				return &AddPermissionlessDelegatorTx{
					BaseTx: validBaseTx,
					Validator: Validator{
						Wght: 1,
					},
					Chain: ids.GenerateTestID(),
					StakeOuts: []*lux.TransferableOutput{
						{
							Asset: lux.Asset{
								ID: ids.GenerateTestID(),
							},
							Out: &secp256k1fx.TransferOutput{
								Amt: 1,
							},
						},
					},
					DelegationRewardsOwner: rewardsOwner,
				}
			},
			err: errCustom,
		},
		{
			name: "invalid stake output",
			txFunc: func(ctrl *gomock.Controller) *AddPermissionlessDelegatorTx {
				rewardsOwner := fxmock.NewOwner(ctrl)
				rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes()

				stakeOut := luxmock.NewMockTransferableOut(ctrl)
				stakeOut.EXPECT().Verify().Return(errCustom)
				return &AddPermissionlessDelegatorTx{
					BaseTx: validBaseTx,
					Validator: Validator{
						Wght: 1,
					},
					Chain: ids.GenerateTestID(),
					StakeOuts: []*lux.TransferableOutput{
						{
							Asset: lux.Asset{
								ID: ids.GenerateTestID(),
							},
							Out: stakeOut,
						},
					},
					DelegationRewardsOwner: rewardsOwner,
				}
			},
			err: errCustom,
		},
		{
			name: "multiple staked assets",
			txFunc: func(ctrl *gomock.Controller) *AddPermissionlessDelegatorTx {
				rewardsOwner := fxmock.NewOwner(ctrl)
				rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes()
				return &AddPermissionlessDelegatorTx{
					BaseTx: validBaseTx,
					Validator: Validator{
						Wght: 1,
					},
					Chain: ids.GenerateTestID(),
					StakeOuts: []*lux.TransferableOutput{
						{
							Asset: lux.Asset{
								ID: ids.GenerateTestID(),
							},
							Out: &secp256k1fx.TransferOutput{
								Amt: 1,
							},
						},
						{
							Asset: lux.Asset{
								ID: ids.GenerateTestID(),
							},
							Out: &secp256k1fx.TransferOutput{
								Amt: 1,
							},
						},
					},
					DelegationRewardsOwner: rewardsOwner,
				}
			},
			err: errMultipleStakedAssets,
		},
		{
			name: "stake not sorted",
			txFunc: func(ctrl *gomock.Controller) *AddPermissionlessDelegatorTx {
				rewardsOwner := fxmock.NewOwner(ctrl)
				rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes()
				assetID := ids.GenerateTestID()
				return &AddPermissionlessDelegatorTx{
					BaseTx: validBaseTx,
					Validator: Validator{
						Wght: 1,
					},
					Chain: ids.GenerateTestID(),
					StakeOuts: []*lux.TransferableOutput{
						{
							Asset: lux.Asset{
								ID: assetID,
							},
							Out: &secp256k1fx.TransferOutput{
								Amt: 2,
							},
						},
						{
							Asset: lux.Asset{
								ID: assetID,
							},
							Out: &secp256k1fx.TransferOutput{
								Amt: 1,
							},
						},
					},
					DelegationRewardsOwner: rewardsOwner,
				}
			},
			err: errOutputsNotSorted,
		},
		{
			name: "stake overflow",
			txFunc: func(ctrl *gomock.Controller) *AddPermissionlessDelegatorTx {
				rewardsOwner := fxmock.NewOwner(ctrl)
				rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes()
				assetID := ids.GenerateTestID()
				return &AddPermissionlessDelegatorTx{
					BaseTx: validBaseTx,
					Validator: Validator{
						NodeID: ids.GenerateTestNodeID(),
						Wght:   1,
					},
					Chain: ids.GenerateTestID(),
					StakeOuts: []*lux.TransferableOutput{
						{
							Asset: lux.Asset{
								ID: assetID,
							},
							Out: &secp256k1fx.TransferOutput{
								Amt: math.MaxUint64,
							},
						},
						{
							Asset: lux.Asset{
								ID: assetID,
							},
							Out: &secp256k1fx.TransferOutput{
								Amt: 2,
							},
						},
					},
					DelegationRewardsOwner: rewardsOwner,
				}
			},
			err: safemath.ErrOverflow,
		},
		{
			name: "weight mismatch",
			txFunc: func(ctrl *gomock.Controller) *AddPermissionlessDelegatorTx {
				rewardsOwner := fxmock.NewOwner(ctrl)
				rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes()
				assetID := ids.GenerateTestID()
				return &AddPermissionlessDelegatorTx{
					BaseTx: validBaseTx,
					Validator: Validator{
						Wght: 1,
					},
					Chain: ids.GenerateTestID(),
					StakeOuts: []*lux.TransferableOutput{
						{
							Asset: lux.Asset{
								ID: assetID,
							},
							Out: &secp256k1fx.TransferOutput{
								Amt: 1,
							},
						},
						{
							Asset: lux.Asset{
								ID: assetID,
							},
							Out: &secp256k1fx.TransferOutput{
								Amt: 1,
							},
						},
					},
					DelegationRewardsOwner: rewardsOwner,
				}
			},
			err: errDelegatorWeightMismatch,
		},
		{
			name: "valid net validator",
			txFunc: func(ctrl *gomock.Controller) *AddPermissionlessDelegatorTx {
				rewardsOwner := fxmock.NewOwner(ctrl)
				rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes()
				assetID := ids.GenerateTestID()
				return &AddPermissionlessDelegatorTx{
					BaseTx: validBaseTx,
					Validator: Validator{
						Wght: 2,
					},
					Chain: ids.GenerateTestID(),
					StakeOuts: []*lux.TransferableOutput{
						{
							Asset: lux.Asset{
								ID: assetID,
							},
							Out: &secp256k1fx.TransferOutput{
								Amt: 1,
							},
						},
						{
							Asset: lux.Asset{
								ID: assetID,
							},
							Out: &secp256k1fx.TransferOutput{
								Amt: 1,
							},
						},
					},
					DelegationRewardsOwner: rewardsOwner,
				}
			},
			err: nil,
		},
		{
			name: "valid primary network validator",
			txFunc: func(ctrl *gomock.Controller) *AddPermissionlessDelegatorTx {
				rewardsOwner := fxmock.NewOwner(ctrl)
				rewardsOwner.EXPECT().Verify().Return(nil).AnyTimes()
				assetID := ids.GenerateTestID()
				return &AddPermissionlessDelegatorTx{
					BaseTx: validBaseTx,
					Validator: Validator{
						Wght: 2,
					},
					Chain: constants.PrimaryNetworkID,
					StakeOuts: []*lux.TransferableOutput{
						{
							Asset: lux.Asset{
								ID: assetID,
							},
							Out: &secp256k1fx.TransferOutput{
								Amt: 1,
							},
						},
						{
							Asset: lux.Asset{
								ID: assetID,
							},
							Out: &secp256k1fx.TransferOutput{
								Amt: 1,
							},
						},
					},
					DelegationRewardsOwner: rewardsOwner,
				}
			},
			err: nil,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctrl := gomock.NewController(t)

			tx := tt.txFunc(ctrl)
			testCtx := &runtime.Runtime{
				NetworkID: networkID,

				ChainID: chainID,
			}
			err := tx.SyntacticVerify(testCtx)
			require.ErrorIs(t, err, tt.err)
		})
	}
}

func TestAddPermissionlessDelegatorTxNotValidatorTx(t *testing.T) {
	txIntf := any((*AddPermissionlessDelegatorTx)(nil))
	_, ok := txIntf.(ValidatorTx)
	require.False(t, ok)
}
