code.vegaprotocol.io/vega@v0.79.0/core/banking/cancel_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 banking_test
    17  
    18  import (
    19  	"context"
    20  	"testing"
    21  
    22  	"code.vegaprotocol.io/vega/core/assets"
    23  	"code.vegaprotocol.io/vega/core/assets/common"
    24  	"code.vegaprotocol.io/vega/core/banking"
    25  	"code.vegaprotocol.io/vega/core/events"
    26  	"code.vegaprotocol.io/vega/core/types"
    27  	"code.vegaprotocol.io/vega/libs/num"
    28  	"code.vegaprotocol.io/vega/protos/vega"
    29  
    30  	"github.com/golang/mock/gomock"
    31  	"github.com/stretchr/testify/assert"
    32  	"github.com/stretchr/testify/require"
    33  )
    34  
    35  func TestCancelTransfer(t *testing.T) {
    36  	e := getTestEngine(t)
    37  
    38  	// let's do a massive fee, easy to test
    39  	e.OnTransferFeeFactorUpdate(context.Background(), num.NewDecimalFromFloat(0.5))
    40  	e.OnEpoch(context.Background(), types.Epoch{Seq: 7, Action: vega.EpochAction_EPOCH_ACTION_START})
    41  	e.OnEpoch(context.Background(), types.Epoch{Seq: 7, Action: vega.EpochAction_EPOCH_ACTION_END})
    42  
    43  	var endEpoch13 uint64 = 11
    44  	transferID := "TRANSFERID"
    45  	partyID := "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301"
    46  	ctx := context.Background()
    47  	transfer := &types.TransferFunds{
    48  		Kind: types.TransferCommandKindRecurring,
    49  		Recurring: &types.RecurringTransfer{
    50  			TransferBase: &types.TransferBase{
    51  				ID:   transferID,
    52  				From: partyID,
    53  
    54  				FromAccountType: types.AccountTypeGeneral,
    55  				To:              "0000000000000000000000000000000000000000000000000000000000000000",
    56  				ToAccountType:   types.AccountTypeGlobalReward,
    57  				Asset:           "eth",
    58  				Amount:          num.NewUint(100),
    59  				Reference:       "someref",
    60  			},
    61  			StartEpoch: 10,
    62  			EndEpoch:   &endEpoch13,
    63  			Factor:     num.MustDecimalFromString("0.9"),
    64  		},
    65  	}
    66  
    67  	asset := "ETH"
    68  
    69  	e.assets.EXPECT().Get(gomock.Any()).Times(2).Return(
    70  		assets.NewAsset(&mockAsset{quantum: num.DecimalFromFloat(100), name: asset}), nil)
    71  	e.broker.EXPECT().Send(gomock.Any()).Times(2)
    72  	assert.NoError(t, e.TransferFunds(ctx, transfer))
    73  
    74  	// now we try to cancel an non-existing transfer
    75  	assert.EqualError(t,
    76  		e.CancelTransferFunds(ctx, &types.CancelTransferFunds{TransferID: "NOPE"}),
    77  		banking.ErrRecurringTransferDoesNotExists.Error(),
    78  	)
    79  
    80  	// now we try to cancel the right transfer, but with the wrong party
    81  	assert.EqualError(t,
    82  		e.CancelTransferFunds(ctx, &types.CancelTransferFunds{
    83  			TransferID: transferID,
    84  			Party:      "NOPE",
    85  		}),
    86  		banking.ErrCannotCancelOtherPartiesRecurringTransfers.Error(),
    87  	)
    88  
    89  	// now we move in time just a bit so we get some transfer processed, but then cancel before
    90  	// then end of the transfer
    91  	fromAcc := types.Account{
    92  		Balance: num.NewUint(1000),
    93  		Asset:   asset,
    94  	}
    95  
    96  	// asset exists
    97  	e.col.EXPECT().GetPartyGeneralAccount(gomock.Any(), gomock.Any()).Times(1).Return(&fromAcc, nil)
    98  
    99  	// assert the calculation of fees and transfer request are correct
   100  	e.col.EXPECT().TransferFunds(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1).DoAndReturn(
   101  		func(ctx context.Context,
   102  			transfers []*types.Transfer,
   103  			accountTypes []types.AccountType,
   104  			references []string,
   105  			feeTransfers []*types.Transfer,
   106  			feeTransfersAccountTypes []types.AccountType,
   107  		) ([]*types.LedgerMovement, error,
   108  		) {
   109  			t.Run("ensure transfers are correct", func(t *testing.T) {
   110  				// transfer is done fully instantly, we should have 2 transfer
   111  				assert.Len(t, transfers, 2)
   112  				assert.Equal(t, transfers[0].Owner, "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301")
   113  				assert.Equal(t, transfers[0].Amount.Amount, num.NewUint(100))
   114  				assert.Equal(t, transfers[0].Amount.Asset, asset)
   115  
   116  				// 1 account types too
   117  				assert.Len(t, accountTypes, 2)
   118  				assert.Equal(t, accountTypes[0], types.AccountTypeGeneral)
   119  			})
   120  
   121  			t.Run("ensure fee transfers are correct", func(t *testing.T) {
   122  				assert.Len(t, feeTransfers, 1)
   123  				assert.Equal(t, feeTransfers[0].Owner, "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301")
   124  				assert.Equal(t, feeTransfers[0].Amount.Amount, num.NewUint(50))
   125  				assert.Equal(t, feeTransfers[0].Amount.Asset, asset)
   126  
   127  				// then the fees account types
   128  				assert.Len(t, feeTransfersAccountTypes, 1)
   129  				assert.Equal(t, accountTypes[0], types.AccountTypeGeneral)
   130  			})
   131  
   132  			return nil, nil
   133  		})
   134  
   135  	e.OnEpoch(context.Background(), types.Epoch{Seq: 10, Action: vega.EpochAction_EPOCH_ACTION_START})
   136  	e.OnEpoch(context.Background(), types.Epoch{Seq: 10, Action: vega.EpochAction_EPOCH_ACTION_END})
   137  
   138  	// now we cancel it, we should get no error and and event
   139  	e.broker.EXPECT().Send(gomock.Any()).DoAndReturn(func(evt events.Event) {
   140  		t.Run("ensure transfer is done", func(t *testing.T) {
   141  			e, ok := evt.(*events.TransferFunds)
   142  			assert.True(t, ok, "unexpected event from the bus")
   143  			assert.Equal(t, e.Proto().Status, types.TransferStatusCancelled)
   144  			assert.Equal(t, "transfer cancelled", *e.Proto().Reason)
   145  		})
   146  	})
   147  
   148  	key := (&types.PayloadBankingRecurringTransfers{}).Key()
   149  	_, _, err := e.GetState(key)
   150  	require.NoError(t, err)
   151  
   152  	assert.NoError(t,
   153  		e.CancelTransferFunds(ctx, &types.CancelTransferFunds{
   154  			TransferID: transferID,
   155  			Party:      partyID,
   156  		}),
   157  	)
   158  	// now we move in time, the recurring transfer was suppose to go
   159  	// 'til epoch 11, but it's not cancelled, and nothing should happen
   160  	e.OnEpoch(context.Background(), types.Epoch{Seq: 11, Action: vega.EpochAction_EPOCH_ACTION_START})
   161  	e.OnEpoch(context.Background(), types.Epoch{Seq: 11, Action: vega.EpochAction_EPOCH_ACTION_END})
   162  }
   163  
   164  type mockAsset struct {
   165  	quantum num.Decimal
   166  	name    string
   167  }
   168  
   169  func (m *mockAsset) Type() *types.Asset {
   170  	return &types.Asset{
   171  		ID: m.name,
   172  		Details: &types.AssetDetails{
   173  			Name:    m.name,
   174  			Symbol:  m.name,
   175  			Quantum: m.quantum,
   176  		},
   177  	}
   178  }
   179  
   180  func (m *mockAsset) SetPendingListing() {}
   181  func (m *mockAsset) SetRejected()       {}
   182  func (m *mockAsset) SetEnabled()        {}
   183  
   184  func (m *mockAsset) GetAssetClass() common.AssetClass { return common.ERC20 }
   185  func (m *mockAsset) IsValid() bool                    { return true }
   186  func (m *mockAsset) SetValid()                        {}
   187  func (m *mockAsset) String() string                   { return "" }