code.vegaprotocol.io/vega@v0.79.0/commands/proposal_submission_new_transfer_test.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package commands_test
    17  
    18  import (
    19  	"errors"
    20  	"testing"
    21  	"time"
    22  
    23  	"code.vegaprotocol.io/vega/commands"
    24  	"code.vegaprotocol.io/vega/libs/crypto"
    25  	types "code.vegaprotocol.io/vega/protos/vega"
    26  	commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1"
    27  
    28  	"github.com/stretchr/testify/require"
    29  )
    30  
    31  func TestCheckProposalSubmissionForNewTransfer(t *testing.T) {
    32  	t.Run("Submitting a new transfer change without a transfer market fails", testNewTransferChangeSubmissionWithoutTransferFails)
    33  	t.Run("Submitting a new transfer change without changes fails", testNewTransferChangeSubmissionWithoutChangesFails)
    34  	t.Run("Submitting a new transfer change without source account type fails", testNewTransferChangeSubmissionWithoutSourceTypeFails)
    35  	t.Run("Submitting a new transfer change with an invalid source account type fails", testNewTransferChangeSubmissionInvalidSourceTypeFails)
    36  	t.Run("Submitting a new transfer change without destination account type fails", testNewTransferChangeSubmissionWithoutDestinationTypeFails)
    37  	t.Run("Submitting a new transfer change with an invalid destination account type fails", testNewTransferChangeSubmissionInvalidDestinationTypeFails)
    38  	t.Run("Submitting a new transfer change with an invalid source fails", testNewTransferChangeSubmissionInvalidSourceFails)
    39  	t.Run("Submitting a new transfer change with an invalid destination fails", testNewTransferChangeSubmissionInvalidDestinationFails)
    40  	t.Run("Submitting a new transfer change with an invalid governance transfer type fails", testNewTransferChangeSubmissionInvalidTransferTypeFails)
    41  	t.Run("Submitting a new transfer change with an invalid amounts fails", testNewTransferChangeSubmissionInvalidAmountFails)
    42  	t.Run("Submitting a new transfer change with an invalid asset fails", testNewTransferChangeSubmissionInvalidAseetFails)
    43  	t.Run("Submitting a new transfer change with an invalid fraction fails", testNewTransferChangeSubmissionInvalidFractionFails)
    44  	t.Run("Submitting a new transfer change with neither one off nor recurring fails", testNewTransferWithNoKind)
    45  	t.Run("Submitting a new transfer change with recurring end epoch before the start epoch", testNewRecurringGovernanceTransferInvalidEndEpoch)
    46  	t.Run("Submitting a new transfer change with identical source/destination accounts", testNewTransferChangeSubmissionIneffectualTransferFails)
    47  	t.Run("Submitting a cancel transfer change with missing transfer id fails", testCancelTransferChangeSubmission)
    48  	t.Run("Submitting a new transfer change with an invalid destination type for one off transfer", testOneOffWithInvalidDestinationType)
    49  	t.Run("Submitting a new transfer change with an negative deliverOn", testOneOffWithNegativeDeliverOn)
    50  	t.Run("Submitting a new recurring transfer change with a dispatch strategy and destination party", testRecurringWithDestinationAndDispatch)
    51  	t.Run("Submitting a new recurring transfer change with a dispatch strategy and an invalid type", testRecurringWithDispatchInvalidTypes)
    52  	t.Run("Submitting a new recurring transfer change with a dispatch strategy and incompatible empty asset for metric", testInvalidAssetForMetric)
    53  	t.Run("Submitting a new recurring transfer change with a dispatch strategy and mismatching destination type for metric", testInvalidDestForMetric)
    54  	t.Run("Submitting a new transfer change with destination type general and an invalid vega public key", testInvalidGeneralPubKey)
    55  	t.Run("Submitting a new transfer change with destination type general and an invalid vega public key", testOnlyGeneralValid)
    56  	t.Run("Submitting a new transfer change with invalid decay factor", testInvalidDecayFactor)
    57  }
    58  
    59  func testInvalidDestForMetric(t *testing.T) {
    60  	metricsMismatches := map[types.AccountType][]types.DispatchMetric{
    61  		types.AccountType_ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES: {
    62  			types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID,
    63  			types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED,
    64  			types.DispatchMetric_DISPATCH_METRIC_MARKET_VALUE,
    65  			types.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL,
    66  			types.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN,
    67  			types.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY,
    68  			types.DispatchMetric_DISPATCH_METRIC_VALIDATOR_RANKING,
    69  			types.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN,
    70  			types.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES,
    71  		},
    72  		types.AccountType_ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES: {
    73  			types.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED,
    74  			types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED,
    75  			types.DispatchMetric_DISPATCH_METRIC_MARKET_VALUE,
    76  			types.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL,
    77  			types.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN,
    78  			types.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY,
    79  			types.DispatchMetric_DISPATCH_METRIC_VALIDATOR_RANKING,
    80  			types.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN,
    81  			types.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES,
    82  		},
    83  		types.AccountType_ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES: {
    84  			types.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED,
    85  			types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID,
    86  			types.DispatchMetric_DISPATCH_METRIC_MARKET_VALUE,
    87  			types.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL,
    88  			types.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN,
    89  			types.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY,
    90  			types.DispatchMetric_DISPATCH_METRIC_VALIDATOR_RANKING,
    91  			types.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN,
    92  			types.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES,
    93  		},
    94  		types.AccountType_ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS: {
    95  			types.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED,
    96  			types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID,
    97  			types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED,
    98  			types.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL,
    99  			types.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN,
   100  			types.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY,
   101  			types.DispatchMetric_DISPATCH_METRIC_VALIDATOR_RANKING,
   102  			types.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN,
   103  			types.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES,
   104  		},
   105  		types.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL: {
   106  			types.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED,
   107  			types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID,
   108  			types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED,
   109  			types.DispatchMetric_DISPATCH_METRIC_MARKET_VALUE,
   110  			types.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN,
   111  			types.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY,
   112  			types.DispatchMetric_DISPATCH_METRIC_VALIDATOR_RANKING,
   113  			types.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN,
   114  			types.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES,
   115  		},
   116  		types.AccountType_ACCOUNT_TYPE_REWARD_RELATIVE_RETURN: {
   117  			types.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED,
   118  			types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID,
   119  			types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED,
   120  			types.DispatchMetric_DISPATCH_METRIC_MARKET_VALUE,
   121  			types.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL,
   122  			types.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY,
   123  			types.DispatchMetric_DISPATCH_METRIC_VALIDATOR_RANKING,
   124  			types.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN,
   125  			types.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES,
   126  		},
   127  		types.AccountType_ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY: {
   128  			types.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED,
   129  			types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID,
   130  			types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED,
   131  			types.DispatchMetric_DISPATCH_METRIC_MARKET_VALUE,
   132  			types.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL,
   133  			types.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN,
   134  			types.DispatchMetric_DISPATCH_METRIC_VALIDATOR_RANKING,
   135  			types.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN,
   136  			types.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES,
   137  		},
   138  		types.AccountType_ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING: {
   139  			types.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED,
   140  			types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID,
   141  			types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED,
   142  			types.DispatchMetric_DISPATCH_METRIC_MARKET_VALUE,
   143  			types.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL,
   144  			types.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN,
   145  			types.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY,
   146  			types.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN,
   147  			types.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES,
   148  		},
   149  		types.AccountType_ACCOUNT_TYPE_REWARD_REALISED_RETURN: {
   150  			types.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED,
   151  			types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID,
   152  			types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED,
   153  			types.DispatchMetric_DISPATCH_METRIC_MARKET_VALUE,
   154  			types.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL,
   155  			types.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN,
   156  			types.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY,
   157  			types.DispatchMetric_DISPATCH_METRIC_VALIDATOR_RANKING,
   158  			types.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES,
   159  		},
   160  		types.AccountType_ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES: {
   161  			types.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED,
   162  			types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID,
   163  			types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED,
   164  			types.DispatchMetric_DISPATCH_METRIC_MARKET_VALUE,
   165  			types.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL,
   166  			types.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN,
   167  			types.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY,
   168  			types.DispatchMetric_DISPATCH_METRIC_VALIDATOR_RANKING,
   169  			types.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN,
   170  		},
   171  	}
   172  
   173  	for tp, metrics := range metricsMismatches {
   174  		for _, metric := range metrics {
   175  			err := checkProposalSubmission(&commandspb.ProposalSubmission{
   176  				Terms: &types.ProposalTerms{
   177  					Change: &types.ProposalTerms_NewTransfer{
   178  						NewTransfer: &types.NewTransfer{
   179  							Changes: &types.NewTransferConfiguration{
   180  								FractionOfBalance: "0.5",
   181  								Amount:            "1000",
   182  								SourceType:        types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   183  								DestinationType:   tp,
   184  								Destination:       "",
   185  								TransferType:      types.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   186  								Asset:             "abcde",
   187  								Kind: &types.NewTransferConfiguration_Recurring{
   188  									Recurring: &types.RecurringTransfer{
   189  										StartEpoch: 10,
   190  										DispatchStrategy: &types.DispatchStrategy{
   191  											Metric: metric,
   192  										},
   193  									},
   194  								},
   195  							},
   196  						},
   197  					},
   198  				},
   199  			})
   200  			require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.recurring.dispatch_strategy.dispatch_metric"), errors.New("cannot set toAccountType to "+tp.String()+" when dispatch metric is set to "+metric.String()))
   201  		}
   202  	}
   203  }
   204  
   205  func testInvalidAssetForMetric(t *testing.T) {
   206  	invalidTypes := []types.AccountType{
   207  		types.AccountType_ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
   208  		types.AccountType_ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
   209  		types.AccountType_ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
   210  		types.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL,
   211  		types.AccountType_ACCOUNT_TYPE_REWARD_RELATIVE_RETURN,
   212  		types.AccountType_ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY,
   213  		types.AccountType_ACCOUNT_TYPE_REWARD_REALISED_RETURN,
   214  	}
   215  
   216  	for _, inv := range invalidTypes {
   217  		err := checkProposalSubmission(&commandspb.ProposalSubmission{
   218  			Terms: &types.ProposalTerms{
   219  				Change: &types.ProposalTerms_NewTransfer{
   220  					NewTransfer: &types.NewTransfer{
   221  						Changes: &types.NewTransferConfiguration{
   222  							FractionOfBalance: "0.5",
   223  							Amount:            "1000",
   224  							SourceType:        types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   225  							DestinationType:   inv,
   226  							Destination:       "",
   227  							TransferType:      types.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   228  							Asset:             "abcde",
   229  							Kind: &types.NewTransferConfiguration_Recurring{
   230  								Recurring: &types.RecurringTransfer{
   231  									StartEpoch:       10,
   232  									DispatchStrategy: &types.DispatchStrategy{},
   233  								},
   234  							},
   235  						},
   236  					},
   237  				},
   238  			},
   239  		})
   240  
   241  		require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.recurring.dispatch_strategy.asset_for_metric"), commands.ErrIsRequired)
   242  	}
   243  
   244  	err := checkProposalSubmission(&commandspb.ProposalSubmission{
   245  		Terms: &types.ProposalTerms{
   246  			Change: &types.ProposalTerms_NewTransfer{
   247  				NewTransfer: &types.NewTransfer{
   248  					Changes: &types.NewTransferConfiguration{
   249  						FractionOfBalance: "0.5",
   250  						Amount:            "1000",
   251  						SourceType:        types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   252  						DestinationType:   types.AccountType_ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
   253  						Destination:       "",
   254  						TransferType:      types.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   255  						Asset:             "abcde",
   256  						Kind: &types.NewTransferConfiguration_Recurring{
   257  							Recurring: &types.RecurringTransfer{
   258  								StartEpoch: 10,
   259  								DispatchStrategy: &types.DispatchStrategy{
   260  									AssetForMetric: "zohar",
   261  								},
   262  							},
   263  						},
   264  					},
   265  				},
   266  			},
   267  		},
   268  	})
   269  
   270  	require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.recurring.dispatch_strategy.asset_for_metric"), commands.ErrShouldBeAValidVegaID, err.Error())
   271  }
   272  
   273  func testRecurringWithDispatchInvalidTypes(t *testing.T) {
   274  	invalidTypes := make(map[types.AccountType]struct{}, len(types.AccountType_name))
   275  	for k := range types.AccountType_name {
   276  		invalidTypes[types.AccountType(k)] = struct{}{}
   277  	}
   278  	delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES)
   279  	delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES)
   280  	delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES)
   281  	delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS)
   282  	delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING)
   283  	delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY)
   284  	delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_REWARD_RELATIVE_RETURN)
   285  	delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_REWARD_REALISED_RETURN)
   286  	delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL)
   287  	delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES)
   288  	delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_INSURANCE)
   289  	delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_GENERAL)
   290  
   291  	delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_UNSPECIFIED)
   292  
   293  	for inv := range invalidTypes {
   294  		err := checkProposalSubmission(&commandspb.ProposalSubmission{
   295  			Terms: &types.ProposalTerms{
   296  				Change: &types.ProposalTerms_NewTransfer{
   297  					NewTransfer: &types.NewTransfer{
   298  						Changes: &types.NewTransferConfiguration{
   299  							FractionOfBalance: "0.5",
   300  							Amount:            "1000",
   301  							SourceType:        types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   302  							DestinationType:   inv,
   303  							Destination:       "",
   304  							TransferType:      types.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   305  							Asset:             "abcde",
   306  							Kind: &types.NewTransferConfiguration_Recurring{
   307  								Recurring: &types.RecurringTransfer{
   308  									StartEpoch:       10,
   309  									DispatchStrategy: &types.DispatchStrategy{},
   310  								},
   311  							},
   312  						},
   313  					},
   314  				},
   315  			},
   316  		})
   317  		require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.destination_type"), commands.ErrIsNotValid, inv.String())
   318  	}
   319  }
   320  
   321  func testRecurringWithDestinationAndDispatch(t *testing.T) {
   322  	err := checkProposalSubmission(&commandspb.ProposalSubmission{
   323  		Terms: &types.ProposalTerms{
   324  			Change: &types.ProposalTerms_NewTransfer{
   325  				NewTransfer: &types.NewTransfer{
   326  					Changes: &types.NewTransferConfiguration{
   327  						FractionOfBalance: "0.5",
   328  						Amount:            "1000",
   329  						SourceType:        types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   330  						DestinationType:   types.AccountType_ACCOUNT_TYPE_GENERAL,
   331  						Destination:       "zohar",
   332  						TransferType:      types.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   333  						Asset:             "abcde",
   334  						Kind: &types.NewTransferConfiguration_Recurring{
   335  							Recurring: &types.RecurringTransfer{
   336  								StartEpoch:       10,
   337  								DispatchStrategy: &types.DispatchStrategy{},
   338  							},
   339  						},
   340  					},
   341  				},
   342  			},
   343  		},
   344  	})
   345  	require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.destination"), commands.ErrIsNotValid)
   346  }
   347  
   348  func testOneOffWithNegativeDeliverOn(t *testing.T) {
   349  	err := checkProposalSubmission(&commandspb.ProposalSubmission{
   350  		Terms: &types.ProposalTerms{
   351  			Change: &types.ProposalTerms_NewTransfer{
   352  				NewTransfer: &types.NewTransfer{
   353  					Changes: &types.NewTransferConfiguration{
   354  						FractionOfBalance: "0.5",
   355  						Amount:            "1000",
   356  						SourceType:        types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   357  						DestinationType:   types.AccountType_ACCOUNT_TYPE_GENERAL,
   358  						Destination:       "zohar",
   359  						TransferType:      types.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   360  						Asset:             "abcde",
   361  						Kind: &types.NewTransferConfiguration_OneOff{
   362  							OneOff: &types.OneOffTransfer{
   363  								DeliverOn: -100,
   364  							},
   365  						},
   366  					},
   367  				},
   368  			},
   369  		},
   370  	})
   371  	require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.oneoff.deliveron"), commands.ErrMustBePositiveOrZero)
   372  }
   373  
   374  func testInvalidDecayFactor(t *testing.T) {
   375  	decayFactors := []string{"", "-0.1", "bc", "0"}
   376  	errors := []string{
   377  		"proposal_submission.terms.change.new_transfer.changes.recurring.factor (not a valid float)",
   378  		"proposal_submission.terms.change.new_transfer.changes.recurring.factor (must be positive)",
   379  		"proposal_submission.terms.change.new_transfer.changes.recurring.factor (not a valid float)",
   380  		"proposal_submission.terms.change.new_transfer.changes.recurring.factor (must be positive)",
   381  	}
   382  	for i, decay := range decayFactors {
   383  		prop := &commandspb.ProposalSubmission{
   384  			Rationale: &types.ProposalRationale{
   385  				Description: "valid",
   386  				Title:       "test",
   387  			},
   388  			Terms: &types.ProposalTerms{
   389  				ClosingTimestamp:   time.Now().Unix() + 100,
   390  				EnactmentTimestamp: time.Now().Unix() + 200,
   391  				Change: &types.ProposalTerms_NewTransfer{
   392  					NewTransfer: &types.NewTransfer{
   393  						Changes: &types.NewTransferConfiguration{
   394  							FractionOfBalance: "0.5",
   395  							Amount:            "1000",
   396  							SourceType:        types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   397  							DestinationType:   types.AccountType_ACCOUNT_TYPE_GENERAL,
   398  							Destination:       crypto.RandomHash(),
   399  							TransferType:      types.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   400  							Asset:             "abcde",
   401  							Kind: &types.NewTransferConfiguration_Recurring{
   402  								Recurring: &types.RecurringTransfer{
   403  									StartEpoch: 0,
   404  									Factor:     decay,
   405  								},
   406  							},
   407  						},
   408  					},
   409  				},
   410  			},
   411  		}
   412  		err := checkProposalSubmission(prop)
   413  		require.Equal(t, errors[i], err.Error())
   414  	}
   415  	prop := &commandspb.ProposalSubmission{
   416  		Rationale: &types.ProposalRationale{
   417  			Description: "valid",
   418  			Title:       "test",
   419  		},
   420  		Terms: &types.ProposalTerms{
   421  			ClosingTimestamp:   time.Now().Unix() + 100,
   422  			EnactmentTimestamp: time.Now().Unix() + 200,
   423  			Change: &types.ProposalTerms_NewTransfer{
   424  				NewTransfer: &types.NewTransfer{
   425  					Changes: &types.NewTransferConfiguration{
   426  						FractionOfBalance: "0.5",
   427  						Amount:            "1000",
   428  						SourceType:        types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   429  						DestinationType:   types.AccountType_ACCOUNT_TYPE_GENERAL,
   430  						Destination:       crypto.RandomHash(),
   431  						TransferType:      types.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   432  						Asset:             "abcde",
   433  						Kind: &types.NewTransferConfiguration_Recurring{
   434  							Recurring: &types.RecurringTransfer{
   435  								StartEpoch: 0,
   436  								Factor:     "0.5",
   437  							},
   438  						},
   439  					},
   440  				},
   441  			},
   442  		},
   443  	}
   444  	err := checkProposalSubmission(prop)
   445  	require.True(t, err.Empty())
   446  }
   447  
   448  func testOnlyGeneralValid(t *testing.T) {
   449  	partyAccs := []types.AccountType{
   450  		types.AccountType_ACCOUNT_TYPE_MARGIN,
   451  		types.AccountType_ACCOUNT_TYPE_BOND,
   452  	}
   453  	// start with a valid transfer to the general account
   454  	prop := &commandspb.ProposalSubmission{
   455  		Rationale: &types.ProposalRationale{
   456  			Description: "valid",
   457  			Title:       "test",
   458  		},
   459  		Terms: &types.ProposalTerms{
   460  			ClosingTimestamp:   time.Now().Unix() + 100,
   461  			EnactmentTimestamp: time.Now().Unix() + 200,
   462  			Change: &types.ProposalTerms_NewTransfer{
   463  				NewTransfer: &types.NewTransfer{
   464  					Changes: &types.NewTransferConfiguration{
   465  						FractionOfBalance: "0.5",
   466  						Amount:            "1000",
   467  						SourceType:        types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   468  						DestinationType:   types.AccountType_ACCOUNT_TYPE_GENERAL,
   469  						Destination:       crypto.RandomHash(),
   470  						TransferType:      types.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   471  						Asset:             "abcde",
   472  						Kind: &types.NewTransferConfiguration_OneOff{
   473  							OneOff: &types.OneOffTransfer{
   474  								DeliverOn: 0,
   475  							},
   476  						},
   477  					},
   478  				},
   479  			},
   480  		},
   481  	}
   482  	err := checkProposalSubmission(prop)
   483  	require.True(t, err.Empty())
   484  	// none of the other accounts are valid destination types:
   485  	for _, at := range partyAccs {
   486  		prop := &commandspb.ProposalSubmission{
   487  			Rationale: &types.ProposalRationale{
   488  				Description: "invalid",
   489  				Title:       "test",
   490  			},
   491  			Terms: &types.ProposalTerms{
   492  				ClosingTimestamp:   time.Now().Unix() + 100,
   493  				EnactmentTimestamp: time.Now().Unix() + 200,
   494  				Change: &types.ProposalTerms_NewTransfer{
   495  					NewTransfer: &types.NewTransfer{
   496  						Changes: &types.NewTransferConfiguration{
   497  							FractionOfBalance: "0.5",
   498  							Amount:            "1000",
   499  							SourceType:        types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   500  							DestinationType:   at,
   501  							Destination:       crypto.RandomHash(), // ensure a valid hash
   502  							TransferType:      types.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   503  							Asset:             "abcde",
   504  							Kind: &types.NewTransferConfiguration_OneOff{
   505  								OneOff: &types.OneOffTransfer{
   506  									DeliverOn: 0,
   507  								},
   508  							},
   509  						},
   510  					},
   511  				},
   512  			},
   513  		}
   514  		err = checkProposalSubmission(prop)
   515  		require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.destination_type"), commands.ErrIsNotValid)
   516  	}
   517  }
   518  
   519  func testInvalidGeneralPubKey(t *testing.T) {
   520  	err := checkProposalSubmission(&commandspb.ProposalSubmission{
   521  		Terms: &types.ProposalTerms{
   522  			Change: &types.ProposalTerms_NewTransfer{
   523  				NewTransfer: &types.NewTransfer{
   524  					Changes: &types.NewTransferConfiguration{
   525  						FractionOfBalance: "0.5",
   526  						Amount:            "1000",
   527  						SourceType:        types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   528  						DestinationType:   types.AccountType_ACCOUNT_TYPE_GENERAL,
   529  						Destination:       "zohar",
   530  						TransferType:      types.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   531  						Asset:             "abcde",
   532  						Kind: &types.NewTransferConfiguration_OneOff{
   533  							OneOff: &types.OneOffTransfer{
   534  								DeliverOn: -100,
   535  							},
   536  						},
   537  					},
   538  				},
   539  			},
   540  		},
   541  	})
   542  	require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.destination"), commands.ErrShouldBeAValidVegaPublicKey)
   543  }
   544  
   545  func testOneOffWithInvalidDestinationType(t *testing.T) {
   546  	dests := []types.AccountType{
   547  		types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   548  		types.AccountType_ACCOUNT_TYPE_SETTLEMENT,
   549  		types.AccountType_ACCOUNT_TYPE_MARGIN,
   550  		types.AccountType_ACCOUNT_TYPE_BOND,
   551  		types.AccountType_ACCOUNT_TYPE_FEES_MAKER,
   552  		types.AccountType_ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
   553  		types.AccountType_ACCOUNT_TYPE_FEES_LIQUIDITY,
   554  	}
   555  
   556  	for _, dest := range dests {
   557  		err := checkProposalSubmission(&commandspb.ProposalSubmission{
   558  			Terms: &types.ProposalTerms{
   559  				Change: &types.ProposalTerms_NewTransfer{
   560  					NewTransfer: &types.NewTransfer{
   561  						Changes: &types.NewTransferConfiguration{
   562  							FractionOfBalance: "0.5",
   563  							Amount:            "1000",
   564  							SourceType:        types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   565  							DestinationType:   dest,
   566  							TransferType:      types.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   567  							Asset:             "abcde",
   568  							Kind: &types.NewTransferConfiguration_OneOff{
   569  								OneOff: &types.OneOffTransfer{},
   570  							},
   571  						},
   572  					},
   573  				},
   574  			},
   575  		})
   576  		require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.destination_type"), commands.ErrIsNotValid)
   577  	}
   578  }
   579  
   580  func testNewRecurringGovernanceTransferInvalidEndEpoch(t *testing.T) {
   581  	endEpoch := uint64(8)
   582  	err := checkProposalSubmission(&commandspb.ProposalSubmission{
   583  		Terms: &types.ProposalTerms{
   584  			Change: &types.ProposalTerms_NewTransfer{
   585  				NewTransfer: &types.NewTransfer{
   586  					Changes: &types.NewTransferConfiguration{
   587  						FractionOfBalance: "0.5",
   588  						Amount:            "1000",
   589  						SourceType:        types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   590  						DestinationType:   types.AccountType_ACCOUNT_TYPE_GENERAL,
   591  						Destination:       crypto.RandomHash(),
   592  						TransferType:      types.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   593  						Asset:             "abcde",
   594  						Kind: &types.NewTransferConfiguration_Recurring{
   595  							Recurring: &types.RecurringTransfer{
   596  								StartEpoch: 10,
   597  								EndEpoch:   &endEpoch,
   598  							},
   599  						},
   600  					},
   601  				},
   602  			},
   603  		},
   604  	})
   605  	require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.recurring.end_epoch"), commands.ErrIsNotValid)
   606  }
   607  
   608  func testNewTransferWithNoKind(t *testing.T) {
   609  	err := checkProposalSubmission(&commandspb.ProposalSubmission{
   610  		Terms: &types.ProposalTerms{
   611  			Change: &types.ProposalTerms_NewTransfer{
   612  				NewTransfer: &types.NewTransfer{
   613  					Changes: &types.NewTransferConfiguration{
   614  						FractionOfBalance: "0.5",
   615  						Amount:            "1000",
   616  						SourceType:        types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   617  						DestinationType:   types.AccountType_ACCOUNT_TYPE_GENERAL,
   618  						Destination:       crypto.RandomHash(),
   619  						TransferType:      types.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   620  						Asset:             "abcde",
   621  					},
   622  				},
   623  			},
   624  		},
   625  	})
   626  	require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.kind"), commands.ErrIsRequired)
   627  }
   628  
   629  func testNewTransferChangeSubmissionWithoutTransferFails(t *testing.T) {
   630  	err := checkProposalSubmission(&commandspb.ProposalSubmission{
   631  		Terms: &types.ProposalTerms{
   632  			Change: &types.ProposalTerms_NewTransfer{},
   633  		},
   634  	})
   635  
   636  	require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer"), commands.ErrIsRequired)
   637  }
   638  
   639  func testNewTransferChangeSubmissionWithoutChangesFails(t *testing.T) {
   640  	err := checkProposalSubmission(&commandspb.ProposalSubmission{
   641  		Terms: &types.ProposalTerms{
   642  			Change: &types.ProposalTerms_NewTransfer{
   643  				NewTransfer: &types.NewTransfer{},
   644  			},
   645  		},
   646  	})
   647  
   648  	require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes"), commands.ErrIsRequired)
   649  }
   650  
   651  func testNewTransferChangeSubmissionWithoutSourceTypeFails(t *testing.T) {
   652  	err := checkProposalSubmission(&commandspb.ProposalSubmission{
   653  		Terms: &types.ProposalTerms{
   654  			Change: &types.ProposalTerms_NewTransfer{
   655  				NewTransfer: &types.NewTransfer{
   656  					Changes: &types.NewTransferConfiguration{},
   657  				},
   658  			},
   659  		},
   660  	})
   661  
   662  	require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.source_type"), commands.ErrIsRequired)
   663  }
   664  
   665  func testNewTransferChangeSubmissionWithoutDestinationTypeFails(t *testing.T) {
   666  	err := checkProposalSubmission(&commandspb.ProposalSubmission{
   667  		Terms: &types.ProposalTerms{
   668  			Change: &types.ProposalTerms_NewTransfer{
   669  				NewTransfer: &types.NewTransfer{
   670  					Changes: &types.NewTransferConfiguration{
   671  						SourceType: types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   672  					},
   673  				},
   674  			},
   675  		},
   676  	})
   677  
   678  	require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.destination_type"), commands.ErrIsRequired)
   679  }
   680  
   681  func testNewTransferChangeSubmissionInvalidDestinationTypeFails(t *testing.T) {
   682  	allAccountTypes := make(map[int32]struct{}, len(types.AccountType_name))
   683  	for k := range types.AccountType_name {
   684  		allAccountTypes[k] = struct{}{}
   685  	}
   686  	delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_GENERAL))
   687  	delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_GLOBAL_REWARD))
   688  	delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_INSURANCE))
   689  	delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY))
   690  	delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_GLOBAL_INSURANCE))
   691  	delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES))
   692  	delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES))
   693  	delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES))
   694  	delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS))
   695  	delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL))
   696  	delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_REWARD_RELATIVE_RETURN))
   697  	delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY))
   698  	delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING))
   699  	delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_REWARD_REALISED_RETURN))
   700  	delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES))
   701  	delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_UNSPECIFIED))
   702  
   703  	for at := range allAccountTypes {
   704  		err := checkProposalSubmission(&commandspb.ProposalSubmission{
   705  			Terms: &types.ProposalTerms{
   706  				Change: &types.ProposalTerms_NewTransfer{
   707  					NewTransfer: &types.NewTransfer{
   708  						Changes: &types.NewTransferConfiguration{
   709  							SourceType:      types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   710  							DestinationType: types.AccountType(at),
   711  						},
   712  					},
   713  				},
   714  			},
   715  		})
   716  		require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.destination_type"), commands.ErrIsNotValid)
   717  	}
   718  	validDestinationAccountTypes := []types.AccountType{types.AccountType_ACCOUNT_TYPE_GENERAL, types.AccountType_ACCOUNT_TYPE_INSURANCE}
   719  	for _, at := range validDestinationAccountTypes {
   720  		err := checkProposalSubmission(&commandspb.ProposalSubmission{
   721  			Terms: &types.ProposalTerms{
   722  				Change: &types.ProposalTerms_NewTransfer{
   723  					NewTransfer: &types.NewTransfer{
   724  						Changes: &types.NewTransferConfiguration{
   725  							SourceType:      types.AccountType_ACCOUNT_TYPE_GLOBAL_REWARD,
   726  							DestinationType: at,
   727  						},
   728  					},
   729  				},
   730  			},
   731  		})
   732  		require.NotContains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.destination_type"), commands.ErrIsNotValid)
   733  	}
   734  }
   735  
   736  func testNewTransferChangeSubmissionInvalidSourceTypeFails(t *testing.T) {
   737  	allAccountTypes := make(map[int32]struct{}, len(types.AccountType_name))
   738  	for k := range types.AccountType_name {
   739  		allAccountTypes[k] = struct{}{}
   740  	}
   741  	delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY))
   742  	delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_GLOBAL_INSURANCE))
   743  	delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_INSURANCE))
   744  	delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_UNSPECIFIED))
   745  
   746  	for at := range allAccountTypes {
   747  		err := checkProposalSubmission(&commandspb.ProposalSubmission{
   748  			Terms: &types.ProposalTerms{
   749  				Change: &types.ProposalTerms_NewTransfer{
   750  					NewTransfer: &types.NewTransfer{
   751  						Changes: &types.NewTransferConfiguration{
   752  							SourceType: types.AccountType(at),
   753  						},
   754  					},
   755  				},
   756  			},
   757  		})
   758  		require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.source_type"), commands.ErrIsNotValid, types.AccountType(at).String())
   759  	}
   760  
   761  	validSourceAccountTypes := []types.AccountType{types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY, types.AccountType_ACCOUNT_TYPE_INSURANCE}
   762  	for _, at := range validSourceAccountTypes {
   763  		err := checkProposalSubmission(&commandspb.ProposalSubmission{
   764  			Terms: &types.ProposalTerms{
   765  				Change: &types.ProposalTerms_NewTransfer{
   766  					NewTransfer: &types.NewTransfer{
   767  						Changes: &types.NewTransferConfiguration{
   768  							SourceType: at,
   769  						},
   770  					},
   771  				},
   772  			},
   773  		})
   774  		require.NotContains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.source_type"), commands.ErrIsNotValid)
   775  	}
   776  }
   777  
   778  func testNewTransferChangeSubmissionInvalidSourceFails(t *testing.T) {
   779  	err := checkProposalSubmission(&commandspb.ProposalSubmission{
   780  		Terms: &types.ProposalTerms{
   781  			Change: &types.ProposalTerms_NewTransfer{
   782  				NewTransfer: &types.NewTransfer{
   783  					Changes: &types.NewTransferConfiguration{
   784  						SourceType:      types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   785  						Source:          "some source",
   786  						DestinationType: types.AccountType_ACCOUNT_TYPE_GENERAL,
   787  					},
   788  				},
   789  			},
   790  		},
   791  	})
   792  	require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.source"), commands.ErrIsNotValid)
   793  }
   794  
   795  func testNewTransferChangeSubmissionInvalidDestinationFails(t *testing.T) {
   796  	err := checkProposalSubmission(&commandspb.ProposalSubmission{
   797  		Terms: &types.ProposalTerms{
   798  			Change: &types.ProposalTerms_NewTransfer{
   799  				NewTransfer: &types.NewTransfer{
   800  					Changes: &types.NewTransferConfiguration{
   801  						SourceType:      types.AccountType_ACCOUNT_TYPE_INSURANCE,
   802  						Source:          "some market",
   803  						DestinationType: types.AccountType_ACCOUNT_TYPE_INSURANCE,
   804  						Destination:     "",
   805  					},
   806  				},
   807  			},
   808  		},
   809  	})
   810  	require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.destination"), commands.ErrIsNotValid, err.Error())
   811  }
   812  
   813  func testCancelTransferChangeSubmission(t *testing.T) {
   814  	err := checkProposalSubmission(&commandspb.ProposalSubmission{
   815  		Terms: &types.ProposalTerms{
   816  			Change: &types.ProposalTerms_CancelTransfer{},
   817  		},
   818  	})
   819  	require.Contains(t, err.Get("proposal_submission.terms.change.cancel_transfer"), commands.ErrIsRequired)
   820  
   821  	err = checkProposalSubmission(&commandspb.ProposalSubmission{
   822  		Terms: &types.ProposalTerms{
   823  			Change: &types.ProposalTerms_CancelTransfer{
   824  				CancelTransfer: &types.CancelTransfer{},
   825  			},
   826  		},
   827  	})
   828  	require.Contains(t, err.Get("proposal_submission.terms.change.cancel_transfer.changes"), commands.ErrIsRequired)
   829  
   830  	err = checkProposalSubmission(&commandspb.ProposalSubmission{
   831  		Terms: &types.ProposalTerms{
   832  			Change: &types.ProposalTerms_CancelTransfer{
   833  				CancelTransfer: &types.CancelTransfer{
   834  					Changes: &types.CancelTransferConfiguration{},
   835  				},
   836  			},
   837  		},
   838  	})
   839  	require.Contains(t, err.Get("proposal_submission.terms.change.cancel_transfer.changes.transferId"), commands.ErrIsRequired)
   840  }
   841  
   842  func testNewTransferChangeSubmissionIneffectualTransferFails(t *testing.T) {
   843  	err := checkProposalSubmission(&commandspb.ProposalSubmission{
   844  		Terms: &types.ProposalTerms{
   845  			Change: &types.ProposalTerms_NewTransfer{
   846  				NewTransfer: &types.NewTransfer{
   847  					Changes: &types.NewTransferConfiguration{
   848  						SourceType:      types.AccountType_ACCOUNT_TYPE_INSURANCE,
   849  						DestinationType: types.AccountType_ACCOUNT_TYPE_INSURANCE,
   850  						Source:          "some destination",
   851  						Destination:     "some destination",
   852  					},
   853  				},
   854  			},
   855  		},
   856  	})
   857  	require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.destination"), commands.ErrIsNotValid)
   858  }
   859  
   860  func testNewTransferChangeSubmissionInvalidTransferTypeFails(t *testing.T) {
   861  	expectation := map[types.GovernanceTransferType]bool{
   862  		types.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_UNSPECIFIED:    true,
   863  		types.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING: false,
   864  		types.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_BEST_EFFORT:    false,
   865  	}
   866  	for tp, expectedError := range expectation {
   867  		err := checkProposalSubmission(&commandspb.ProposalSubmission{
   868  			Terms: &types.ProposalTerms{
   869  				Change: &types.ProposalTerms_NewTransfer{
   870  					NewTransfer: &types.NewTransfer{
   871  						Changes: &types.NewTransferConfiguration{
   872  							SourceType:      types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   873  							DestinationType: types.AccountType_ACCOUNT_TYPE_GENERAL,
   874  							Destination:     crypto.RandomHash(),
   875  							TransferType:    tp,
   876  						},
   877  					},
   878  				},
   879  			},
   880  		})
   881  		if expectedError {
   882  			require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.transfer_type"), commands.ErrIsRequired)
   883  		} else {
   884  			require.NotContains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.transfer_type"), commands.ErrIsRequired)
   885  		}
   886  	}
   887  }
   888  
   889  func testNewTransferChangeSubmissionInvalidAmountFails(t *testing.T) {
   890  	transfer := &types.NewTransferConfiguration{
   891  		SourceType:      types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   892  		DestinationType: types.AccountType_ACCOUNT_TYPE_GENERAL,
   893  		Destination:     crypto.RandomHash(),
   894  		TransferType:    types.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   895  	}
   896  	err := checkProposalSubmission(&commandspb.ProposalSubmission{
   897  		Terms: &types.ProposalTerms{
   898  			Change: &types.ProposalTerms_NewTransfer{
   899  				NewTransfer: &types.NewTransfer{
   900  					Changes: transfer,
   901  				},
   902  			},
   903  		},
   904  	})
   905  	require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.amount"), commands.ErrIsRequired)
   906  	transfer.Amount = "abc"
   907  	err = checkProposalSubmission(&commandspb.ProposalSubmission{
   908  		Terms: &types.ProposalTerms{
   909  			Change: &types.ProposalTerms_NewTransfer{
   910  				NewTransfer: &types.NewTransfer{
   911  					Changes: transfer,
   912  				},
   913  			},
   914  		},
   915  	})
   916  	require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.amount"), commands.ErrIsNotValid)
   917  	transfer.Amount = "500.1234"
   918  	err = checkProposalSubmission(&commandspb.ProposalSubmission{
   919  		Terms: &types.ProposalTerms{
   920  			Change: &types.ProposalTerms_NewTransfer{
   921  				NewTransfer: &types.NewTransfer{
   922  					Changes: transfer,
   923  				},
   924  			},
   925  		},
   926  	})
   927  	require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.amount"), commands.ErrIsNotValid)
   928  
   929  	transfer.Amount = "-500"
   930  	err = checkProposalSubmission(&commandspb.ProposalSubmission{
   931  		Terms: &types.ProposalTerms{
   932  			Change: &types.ProposalTerms_NewTransfer{
   933  				NewTransfer: &types.NewTransfer{
   934  					Changes: transfer,
   935  				},
   936  			},
   937  		},
   938  	})
   939  	require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.amount"), commands.ErrIsNotValid)
   940  
   941  	transfer.Amount = "500"
   942  	err = checkProposalSubmission(&commandspb.ProposalSubmission{
   943  		Terms: &types.ProposalTerms{
   944  			Change: &types.ProposalTerms_NewTransfer{
   945  				NewTransfer: &types.NewTransfer{
   946  					Changes: transfer,
   947  				},
   948  			},
   949  		},
   950  	})
   951  	require.NotContains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.amount"), commands.ErrIsNotValid)
   952  }
   953  
   954  func testNewTransferChangeSubmissionInvalidAseetFails(t *testing.T) {
   955  	transfer := &types.NewTransferConfiguration{
   956  		SourceType:      types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   957  		DestinationType: types.AccountType_ACCOUNT_TYPE_GENERAL,
   958  		Destination:     crypto.RandomHash(),
   959  		TransferType:    types.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   960  		Amount:          "500",
   961  	}
   962  	err := checkProposalSubmission(&commandspb.ProposalSubmission{
   963  		Terms: &types.ProposalTerms{
   964  			Change: &types.ProposalTerms_NewTransfer{
   965  				NewTransfer: &types.NewTransfer{
   966  					Changes: transfer,
   967  				},
   968  			},
   969  		},
   970  	})
   971  	require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.asset"), commands.ErrIsRequired)
   972  	transfer.Asset = "abcde"
   973  	err = checkProposalSubmission(&commandspb.ProposalSubmission{
   974  		Terms: &types.ProposalTerms{
   975  			Change: &types.ProposalTerms_NewTransfer{
   976  				NewTransfer: &types.NewTransfer{
   977  					Changes: transfer,
   978  				},
   979  			},
   980  		},
   981  	})
   982  	require.NotContains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.asset"), commands.ErrIsRequired)
   983  }
   984  
   985  func testNewTransferChangeSubmissionInvalidFractionFails(t *testing.T) {
   986  	expectation := map[string]error{
   987  		"":     commands.ErrIsRequired,
   988  		"abc":  commands.ErrIsNotValid,
   989  		"-1":   commands.ErrMustBePositive,
   990  		"1.01": commands.ErrMustBeLTE1,
   991  	}
   992  
   993  	transfer := &types.NewTransferConfiguration{
   994  		SourceType:      types.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY,
   995  		DestinationType: types.AccountType_ACCOUNT_TYPE_GENERAL,
   996  		Destination:     crypto.RandomHash(),
   997  		TransferType:    types.GovernanceTransferType_GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
   998  		Amount:          "500",
   999  		Asset:           "abcde",
  1000  	}
  1001  	for fraction, expectedErr := range expectation {
  1002  		transfer.FractionOfBalance = fraction
  1003  		err := checkProposalSubmission(&commandspb.ProposalSubmission{
  1004  			Terms: &types.ProposalTerms{
  1005  				Change: &types.ProposalTerms_NewTransfer{
  1006  					NewTransfer: &types.NewTransfer{
  1007  						Changes: transfer,
  1008  					},
  1009  				},
  1010  			},
  1011  		})
  1012  		require.Contains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.fraction_of_balance"), expectedErr)
  1013  	}
  1014  	transfer.FractionOfBalance = "0.5"
  1015  	err := checkProposalSubmission(&commandspb.ProposalSubmission{
  1016  		Terms: &types.ProposalTerms{
  1017  			Change: &types.ProposalTerms_NewTransfer{
  1018  				NewTransfer: &types.NewTransfer{
  1019  					Changes: transfer,
  1020  				},
  1021  			},
  1022  		},
  1023  	})
  1024  	require.NotContains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.fraction_of_balance"), commands.ErrMustBePositive)
  1025  	require.NotContains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.fraction_of_balance"), commands.ErrIsNotValid)
  1026  	require.NotContains(t, err.Get("proposal_submission.terms.change.new_transfer.changes.fraction_of_balance"), commands.ErrIsRequired)
  1027  }