Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: remove fee and minimal amount on transfer from vested account #9984

Merged
merged 1 commit into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
### 🛠 Improvements

- [9930](https://github.com/vegaprotocol/vega/issues/9930) - `LiquidityFeeSettings` can now be used in market proposals to choose how liquidity fees are calculated.
- [9982](https://github.com/vegaprotocol/vega/issues/9982) - Remove fees and minimal transfer amount from vested account

### 🐛 Fixes

Expand Down
2 changes: 1 addition & 1 deletion core/banking/oneoff_transfers.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func (e *Engine) oneOffTransfer(
return err
}

if err := e.ensureMinimalTransferAmount(a, transfer.Amount); err != nil {
if err := e.ensureMinimalTransferAmount(a, transfer.Amount, transfer.FromAccountType, transfer.From); err != nil {
transfer.Status = types.TransferStatusRejected
return err
}
Expand Down
6 changes: 3 additions & 3 deletions core/banking/recurring_transfers.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (e *Engine) recurringTransfer(
return err
}

if err := e.ensureMinimalTransferAmount(a, transfer.Amount); err != nil {
if err := e.ensureMinimalTransferAmount(a, transfer.Amount, transfer.FromAccountType, transfer.From); err != nil {
transfer.Status = types.TransferStatusRejected
return err
}
Expand Down Expand Up @@ -238,7 +238,7 @@ func (e *Engine) distributeRecurringTransfers(ctx context.Context, newEpoch uint
e.log.Panic("this should never happen", logging.Error(err))
}

if err = e.ensureMinimalTransferAmount(a, amount); err != nil {
if err = e.ensureMinimalTransferAmount(a, amount, v.FromAccountType, v.From); err != nil {
v.Status = types.TransferStatusStopped
transfersDone = append(transfersDone,
events.NewRecurringTransferFundsEventWithReason(ctx, v, err.Error()))
Expand All @@ -256,7 +256,7 @@ func (e *Engine) distributeRecurringTransfers(ctx context.Context, newEpoch uint
)
} else {
// check if the amount + fees can be covered by the party issuing the transfer
if _, err = e.ensureFeeForTransferFunds(amount, v.From, v.Asset, v.FromAccountType); err == nil {
if _, err = e.ensureFeeForTransferFunds(amount, v.From, v.Asset, v.FromAccountType, v.To); err == nil {
// NB: if the metric is market value we're going to transfer the bonus if any directly
// to the market account of the asset/reward type - this is similar to previous behaviour and
// different to how all other metric based rewards behave. The reason is that we need the context of the funder
Expand Down
46 changes: 46 additions & 0 deletions core/banking/transfer_common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,49 @@ func TestCheckTransfer(t *testing.T) {
transfer.Amount = num.NewUint(200)
require.EqualError(t, e.CheckTransfer(transfer), "could not transfer funds, not enough funds to transfer")
}

func TestCheckTransferWithVestedAccount(t *testing.T) {
e := getTestEngine(t)

transfer := &types.TransferBase{
From: "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301",
FromAccountType: types.AccountTypeVestedRewards,
To: "03ae90688632c649c4beab6040ff5bd04dbde8efbf737d8673bbda792a110301",
ToAccountType: types.AccountTypeGeneral,
Asset: "eth",
Amount: num.NewUint(10),
Reference: "someref",
}

e.OnMinTransferQuantumMultiple(context.Background(), num.DecimalFromFloat(1))

// balance is under the min amount
e.col.EXPECT().GetPartyVestedRewardAccount(gomock.Any(), gomock.Any()).Return(&types.Account{Balance: num.NewUint(90)}, nil).Times(1)

// asset exists
e.assets.EXPECT().Get(gomock.Any()).Times(1).Return(assets.NewAsset(&mockAsset{num.DecimalFromFloat(100)}), nil)
// try to transfer a small balance, but not the whole balance
require.EqualError(t,
e.CheckTransfer(transfer),
"transfer from vested account under minimal transfer amount must be the full balance",
)

// now we try to transfre the full amount
e.col.EXPECT().GetPartyVestedRewardAccount(gomock.Any(), gomock.Any()).Return(&types.Account{Balance: num.NewUint(90)}, nil).Times(2)
transfer.Amount = num.NewUint(90)
e.assets.EXPECT().Get(gomock.Any()).Times(1).Return(assets.NewAsset(&mockAsset{num.DecimalFromFloat(100)}), nil)
require.NoError(t,
e.CheckTransfer(transfer),
)

// now we try again, with a balance above the min amount, but not the whole balance

e.col.EXPECT().GetPartyVestedRewardAccount(gomock.Any(), gomock.Any()).Return(&types.Account{Balance: num.NewUint(300)}, nil).Times(1)
e.assets.EXPECT().Get(gomock.Any()).Times(1).Return(assets.NewAsset(&mockAsset{num.DecimalFromFloat(100)}), nil)

transfer.Amount = num.NewUint(110)
// try to transfer a small balance, but not the whole balance
require.NoError(t,
e.CheckTransfer(transfer),
)
}
44 changes: 38 additions & 6 deletions core/banking/transfers_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,24 +66,33 @@ func (e *Engine) CheckTransfer(t *types.TransferBase) error {
return fmt.Errorf("could not transfer funds, %w", err)
}

if err := e.ensureMinimalTransferAmount(a, t.Amount); err != nil {
if err := e.ensureMinimalTransferAmount(a, t.Amount, t.FromAccountType, t.From); err != nil {
return err
}

_, err = e.ensureFeeForTransferFunds(t.Amount, t.From, t.Asset, t.FromAccountType)
_, err = e.ensureFeeForTransferFunds(t.Amount, t.From, t.Asset, t.FromAccountType, t.To)
if err != nil {
return fmt.Errorf("could not transfer funds, %w", err)
}
return nil
}

func (e *Engine) ensureMinimalTransferAmount(a *assets.Asset, amount *num.Uint) error {
func (e *Engine) ensureMinimalTransferAmount(
a *assets.Asset,
amount *num.Uint,
fromAccType types.AccountType,
from string,
) error {
quantum := a.Type().Details.Quantum
// no reason this would produce an error
minAmount, _ := num.UintFromDecimal(quantum.Mul(e.minTransferQuantumMultiple))

// no verify amount
if amount.LT(minAmount) {
if fromAccType == types.AccountTypeVestedRewards {
return e.ensureMinimalTransferAmountFromVested(amount, from, a.Type().ID)
}

e.log.Debug("cannot transfer funds, less than minimal amount requested to transfer",
logging.BigUint("min-amount", minAmount),
logging.BigUint("requested-amount", amount),
Expand All @@ -94,6 +103,22 @@ func (e *Engine) ensureMinimalTransferAmount(a *assets.Asset, amount *num.Uint)
return nil
}

func (e *Engine) ensureMinimalTransferAmountFromVested(
transferAmount *num.Uint,
from, asset string,
) error {
account, err := e.col.GetPartyVestedRewardAccount(from, asset)
if err != nil {
return err
}

if transferAmount.EQ(account.Balance) {
return nil
}

return fmt.Errorf("transfer from vested account under minimal transfer amount must be the full balance")
}

func (e *Engine) processTransfer(
ctx context.Context,
from, to, asset, toMarket string,
Expand All @@ -108,7 +133,7 @@ func (e *Engine) processTransfer(
) ([]*types.LedgerMovement, error) {
// ensure the party have enough funds for both the
// amount and the fee for the transfer
feeTransfer, err := e.ensureFeeForTransferFunds(amount, from, asset, fromAcc)
feeTransfer, err := e.ensureFeeForTransferFunds(amount, from, asset, fromAcc, to)
if err != nil {
return nil, fmt.Errorf("could not pay the fee for transfer: %w", err)
}
Expand Down Expand Up @@ -179,9 +204,15 @@ func (e *Engine) makeFeeTransferForTransferFunds(
amount *num.Uint,
from, asset string,
fromAccountType types.AccountType,
to string,
) *types.Transfer {
// no fee for Vested account
feeAmount := num.UintZero()

// first we calculate the fee
feeAmount, _ := num.UintFromDecimal(amount.ToDecimal().Mul(e.transferFeeFactor))
if !(fromAccountType == types.AccountTypeVestedRewards && from == to) {
feeAmount, _ = num.UintFromDecimal(amount.ToDecimal().Mul(e.transferFeeFactor))
}

switch fromAccountType {
case types.AccountTypeGeneral, types.AccountTypeVestedRewards:
Expand All @@ -208,9 +239,10 @@ func (e *Engine) ensureFeeForTransferFunds(
amount *num.Uint,
from, asset string,
fromAccountType types.AccountType,
to string,
) (*types.Transfer, error) {
transfer := e.makeFeeTransferForTransferFunds(
amount, from, asset, fromAccountType,
amount, from, asset, fromAccountType, to,
)

var (
Expand Down
Loading