code.vegaprotocol.io/vega@v0.79.0/core/execution/spot/fees_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 spot_test
    17  
    18  import (
    19  	"context"
    20  	"testing"
    21  	"time"
    22  
    23  	"code.vegaprotocol.io/vega/core/types"
    24  	vegacontext "code.vegaprotocol.io/vega/libs/context"
    25  	"code.vegaprotocol.io/vega/libs/crypto"
    26  
    27  	"github.com/stretchr/testify/require"
    28  )
    29  
    30  func setupToLeaveOpeningAuction(t *testing.T) (*testMarket, context.Context) {
    31  	t.Helper()
    32  	now := time.Now()
    33  	ctx := context.Background()
    34  	ctx = vegacontext.WithTraceID(ctx, crypto.RandomHash())
    35  	tm := newTestMarket(t, defaultPriceMonitorSettings, &types.AuctionDuration{Duration: 1}, now)
    36  
    37  	addAccountWithAmount(tm, "party1", 100000, "ETH")
    38  	addAccountWithAmount(tm, "party2", 5, "BTC")
    39  
    40  	tm.market.StartOpeningAuction(ctx)
    41  
    42  	order1 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideBuy, "party1", 2, 30000)
    43  	_, err := tm.market.SubmitOrder(ctx, order1.IntoSubmission(), order1.Party, crypto.RandomHash())
    44  	require.NoError(t, err)
    45  
    46  	order2 := getGTCLimitOrder(tm, now, crypto.RandomHash(), types.SideSell, "party2", 1, 30000)
    47  	_, err = tm.market.SubmitOrder(ctx, order2.IntoSubmission(), order2.Party, crypto.RandomHash())
    48  	require.NoError(t, err)
    49  
    50  	tm.now = now.Add(2 * time.Second)
    51  	tm.market.OnTick(ctx, tm.now)
    52  	md := tm.market.GetMarketData()
    53  	require.Equal(t, types.MarketTradingModeContinuous, md.MarketTradingMode)
    54  	return tm, ctx
    55  }
    56  
    57  func TestNoFeesInOpeningAuction(t *testing.T) {
    58  	tm, _ := setupToLeaveOpeningAuction(t)
    59  
    60  	// at this point party 1 bought 1 BTC and has one outstanding order at price 30k as we've left opening auction
    61  	gaBalance1, err := tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset)
    62  	require.NoError(t, err)
    63  	require.Equal(t, "40000", gaBalance1.Balance.String())
    64  
    65  	gaBalance2, err := tm.collateralEngine.GetPartyGeneralAccount("party2", tm.quoteAsset)
    66  	require.NoError(t, err)
    67  	require.Equal(t, "30000", gaBalance2.Balance.String())
    68  }
    69  
    70  func TestSellerAggressorInContinuousMode(t *testing.T) {
    71  	tm, ctx := setupToLeaveOpeningAuction(t)
    72  
    73  	// there's already an open buy order for 30k in the book from the setup so we only need to setup the
    74  	// sell aggressive order
    75  	// setup the aggressive sell order
    76  
    77  	// lets check first what is the balance of party2 in the quote asset before the trade
    78  	gaBalance2, err := tm.collateralEngine.GetPartyGeneralAccount("party2", tm.quoteAsset)
    79  	require.NoError(t, err)
    80  	require.Equal(t, "30000", gaBalance2.Balance.String()) // the 40k they had from before + the maker fee received
    81  
    82  	sellOrder := getGTCLimitOrder(tm, tm.now, crypto.RandomHash(), types.SideSell, "party2", 1, 30000)
    83  	conf, err := tm.market.SubmitOrder(ctx, sellOrder.IntoSubmission(), sellOrder.Party, crypto.RandomHash())
    84  	require.NoError(t, err)
    85  	require.Equal(t, 1, len(conf.Trades))
    86  
    87  	// the trade price is 30000, however the aggressor, i.e. the seller pays the fee, instead of getting 30000 they get 30000 - fee
    88  	gaBalance1, err := tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset)
    89  	require.NoError(t, err)
    90  	require.Equal(t, "40120", gaBalance1.Balance.String()) // the 40k they had from before + the maker fee received
    91  
    92  	// they had 30k, they get 30k - fees added to their general account in the quote asset
    93  	gaBalance2, err = tm.collateralEngine.GetPartyGeneralAccount("party2", tm.quoteAsset)
    94  	require.NoError(t, err)
    95  	require.Equal(t, "59850", gaBalance2.Balance.String())
    96  }
    97  
    98  func TestBuyAggressorInContinuousMode(t *testing.T) {
    99  	tm, ctx := setupToLeaveOpeningAuction(t)
   100  	// first lets close the open buy trade in the book
   101  
   102  	sellOrder := getGTCLimitOrder(tm, tm.now, crypto.RandomHash(), types.SideSell, "party2", 2, 30000)
   103  	conf, err := tm.market.SubmitOrder(ctx, sellOrder.IntoSubmission(), sellOrder.Party, crypto.RandomHash())
   104  	require.NoError(t, err)
   105  	require.Equal(t, 1, len(conf.Trades))
   106  
   107  	// confirm the quote asset balance of the seller after the trade
   108  	gaBalance2, err := tm.collateralEngine.GetPartyGeneralAccount("party2", tm.quoteAsset)
   109  	require.NoError(t, err)
   110  	require.Equal(t, "59850", gaBalance2.Balance.String())
   111  
   112  	// now place a buy order to trade against the remaining size of party2, but first let's confirm the quote balance of party1
   113  	gaBalance1, err := tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset)
   114  	require.NoError(t, err)
   115  	require.Equal(t, "40120", gaBalance1.Balance.String()) // the 40k they had from before + the maker fee received
   116  
   117  	// this should be enough to cover the trade + fees
   118  	buyOrder := getGTCLimitOrder(tm, tm.now, crypto.RandomHash(), types.SideBuy, "party1", 1, 30000)
   119  	conf, err = tm.market.SubmitOrder(ctx, buyOrder.IntoSubmission(), buyOrder.Party, crypto.RandomHash())
   120  	require.NoError(t, err)
   121  	require.Equal(t, 1, len(conf.Trades))
   122  
   123  	// expect the buyer to have paid the 30k + fees
   124  	gaBalance1, err = tm.collateralEngine.GetPartyGeneralAccount("party1", tm.quoteAsset)
   125  	require.NoError(t, err)
   126  	require.Equal(t, "9970", gaBalance1.Balance.String())
   127  
   128  	// confirm the quote asset balance of the seller increased by 30k after the trade + 120 from maker fees
   129  	gaBalance2, err = tm.collateralEngine.GetPartyGeneralAccount("party2", tm.quoteAsset)
   130  	require.NoError(t, err)
   131  	require.Equal(t, "89970", gaBalance2.Balance.String())
   132  }