Skip to content

Commit

Permalink
feat: buy back auctions
Browse files Browse the repository at this point in the history
  • Loading branch information
ze97286 committed Sep 26, 2024
1 parent 62066e8 commit 2fda1e3
Show file tree
Hide file tree
Showing 95 changed files with 16,302 additions and 8,715 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- [11644](https://github.com/vegaprotocol/vega/issues/11644) - `liveOnly` flag has been added to the `AMM` API to show only active `AMMs`.
- [11519](https://github.com/vegaprotocol/vega/issues/11519) - Add fees to position API types.
- [11642](https://github.com/vegaprotocol/vega/issues/11642) - `AMMs` with empty price levels are now allowed.
- [11685](https://github.com/vegaprotocol/vega/issues/11685) - Automated purchase support added.
- [11711](https://github.com/vegaprotocol/vega/issues/11711) - Manage closed team membership by updating the allow list.

### 🐛 Fixes
Expand Down
450 changes: 287 additions & 163 deletions commands/proposal_submission.go

Large diffs are not rendered by default.

588 changes: 588 additions & 0 deletions commands/proposal_submission_pap_test.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion core/collateral/checkpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ func TestCheckPointWithUndistributedLPFees(t *testing.T) {
},
Type: types.TransferTypeLiquidityFeeUnpaidCollect,
}
_, err = e.TransferSpotFees(context.Background(), "market1", "MYASSET1", &feesTransfer{transfers: []*types.Transfer{lpSpotTransfers}})
_, err = e.TransferSpotFees(context.Background(), "market1", "MYASSET1", &feesTransfer{transfers: []*types.Transfer{lpSpotTransfers}}, types.AccountTypeGeneral)
require.NoError(t, err)

// setup some balance on the LP fee pay account for MYASSET1/market2
Expand Down
175 changes: 142 additions & 33 deletions core/collateral/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ type Engine struct {
timeService TimeService
broker Broker

earmarkedBalance map[string]*num.Uint

partiesAccsBalanceCache map[string]*num.Uint
partiesAccsBalanceCacheLock sync.RWMutex

Expand Down Expand Up @@ -151,6 +153,7 @@ func New(log *logging.Logger, conf Config, ts TimeService, broker Broker) *Engin
nextBalancesSnapshot: time.Time{},
partyAssetCache: map[string]map[string]*num.Uint{},
partyMarketCache: map[string]map[string]*num.Uint{},
earmarkedBalance: map[string]*num.Uint{},
}
}

Expand Down Expand Up @@ -731,14 +734,14 @@ func (e *Engine) TransferSpotFeesContinuousTrading(ctx context.Context, marketID
}
}

return e.transferSpotFees(ctx, marketID, assetID, ft)
return e.transferSpotFees(ctx, marketID, assetID, ft, types.AccountTypeGeneral)
}

func (e *Engine) TransferSpotFees(ctx context.Context, marketID string, assetID string, ft events.FeesTransfer) ([]*types.LedgerMovement, error) {
return e.transferSpotFees(ctx, marketID, assetID, ft)
func (e *Engine) TransferSpotFees(ctx context.Context, marketID string, assetID string, ft events.FeesTransfer, fromAccountType types.AccountType) ([]*types.LedgerMovement, error) {
return e.transferSpotFees(ctx, marketID, assetID, ft, fromAccountType)
}

func (e *Engine) transferSpotFees(ctx context.Context, marketID string, assetID string, ft events.FeesTransfer) ([]*types.LedgerMovement, error) {
func (e *Engine) transferSpotFees(ctx context.Context, marketID string, assetID string, ft events.FeesTransfer, fromAccountType types.AccountType) ([]*types.LedgerMovement, error) {
makerFee, infraFee, liquiFee, err := e.getFeesAccounts(marketID, assetID)
if err != nil {
return nil, err
Expand All @@ -749,7 +752,7 @@ func (e *Engine) transferSpotFees(ctx context.Context, marketID string, assetID

for _, transfer := range transfers {
req, err := e.getSpotFeeTransferRequest(
ctx, transfer, makerFee, infraFee, liquiFee, marketID, assetID)
ctx, transfer, makerFee, infraFee, liquiFee, marketID, assetID, fromAccountType)
if err != nil {
e.log.Error("Failed to build transfer request for event",
logging.Error(err))
Expand Down Expand Up @@ -784,6 +787,7 @@ func (e *Engine) getSpotFeeTransferRequest(
t *types.Transfer,
makerFee, infraFee, liquiFee *types.Account,
marketID, assetID string,
sourceAccountType types.AccountType,
) (*types.TransferRequest, error) {
getAccount := func(marketID, owner string, accountType vega.AccountType) (*types.Account, error) {
acc, err := e.GetAccountByID(e.accountID(marketID, owner, assetID, accountType))
Expand All @@ -808,7 +812,13 @@ func (e *Engine) getSpotFeeTransferRequest(
return getAccount(marketID, systemOwner, types.AccountTypeLiquidityFeesBonusDistribution)
}

general, err := getAccount(noMarket, t.Owner, types.AccountTypeGeneral)
accTypeForGeneral := types.AccountTypeGeneral
owner := t.Owner
if t.Owner == types.NetworkParty {
owner = systemOwner
accTypeForGeneral = sourceAccountType
}
general, err := getAccount(noMarket, owner, accTypeForGeneral)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -2736,7 +2746,7 @@ func (e *Engine) getGovernanceTransferFundsTransferRequest(ctx context.Context,
case types.AccountTypeGlobalReward, types.AccountTypeLPFeeReward, types.AccountTypeMakerReceivedFeeReward,
types.AccountTypeMakerPaidFeeReward, types.AccountTypeMarketProposerReward, types.AccountTypeAverageNotionalReward,
types.AccountTypeRelativeReturnReward, types.AccountTypeReturnVolatilityReward, types.AccountTypeRealisedReturnReward,
types.AccountTypeValidatorRankingReward, types.AccountTypeEligibleEntitiesReward:
types.AccountTypeValidatorRankingReward, types.AccountTypeEligibleEntitiesReward, types.AccountTypeBuyBackFees:
market := noMarket
if len(t.Market) > 0 {
market = t.Market
Expand Down Expand Up @@ -2843,7 +2853,7 @@ func (e *Engine) getTransferFundsTransferRequest(ctx context.Context, t *types.T
case types.AccountTypeGlobalReward, types.AccountTypeLPFeeReward, types.AccountTypeMakerReceivedFeeReward, types.AccountTypeNetworkTreasury,
types.AccountTypeMakerPaidFeeReward, types.AccountTypeMarketProposerReward, types.AccountTypeAverageNotionalReward,
types.AccountTypeRelativeReturnReward, types.AccountTypeReturnVolatilityReward, types.AccountTypeRealisedReturnReward,
types.AccountTypeValidatorRankingReward, types.AccountTypeEligibleEntitiesReward:
types.AccountTypeValidatorRankingReward, types.AccountTypeEligibleEntitiesReward, types.AccountTypeBuyBackFees:
market := noMarket
if len(t.Market) > 0 {
market = t.Market
Expand Down Expand Up @@ -4688,6 +4698,10 @@ func (e *Engine) GetNetworkTreasuryAccount(asset string) (*types.Account, error)
return e.GetAccountByID(e.accountID(noMarket, systemOwner, asset, types.AccountTypeNetworkTreasury))
}

func (e *Engine) GetOrCreateBuyBackFeesAccountID(ctx context.Context, asset string) string {
return e.getOrCreateBuyBackFeesAccount(ctx, asset).ID
}

func (e *Engine) getOrCreateBuyBackFeesAccount(ctx context.Context, asset string) *types.Account {
accID := e.accountID(noMarket, systemOwner, asset, types.AccountTypeBuyBackFees)
acc, err := e.GetAccountByID(accID)
Expand Down Expand Up @@ -4728,6 +4742,26 @@ func (e *Engine) GetOrCreateNetworkTreasuryAccount(ctx context.Context, asset st
return ntAcc
}

func (e *Engine) getOrCreateNetworkAccount(ctx context.Context, asset string, accountType types.AccountType) *types.Account {
accID := e.accountID(noMarket, systemOwner, asset, accountType)
acc, err := e.GetAccountByID(accID)
if err == nil {
return acc
}
ntAcc := &types.Account{
ID: accID,
Asset: asset,
Owner: systemOwner,
Balance: num.UintZero(),
MarketID: noMarket,
Type: accountType,
}
e.accs[accID] = ntAcc
e.addAccountToHashableSlice(ntAcc)
e.broker.Send(events.NewAccountEvent(ctx, *ntAcc))
return ntAcc
}

func (e *Engine) GetGlobalInsuranceAccount(asset string) (*types.Account, error) {
return e.GetAccountByID(e.accountID(noMarket, systemOwner, asset, types.AccountTypeGlobalInsurance))
}
Expand Down Expand Up @@ -4801,19 +4835,23 @@ func (e *Engine) GetSystemAccountBalance(asset, market string, accountType types
return account.Balance.Clone(), nil
}

// TransferToHoldingAccount locks funds from general account into holding account account of the party.
func (e *Engine) TransferToHoldingAccount(ctx context.Context, transfer *types.Transfer) (*types.LedgerMovement, error) {
generalAccountID := e.accountID(transfer.Market, transfer.Owner, transfer.Amount.Asset, types.AccountTypeGeneral)
generalAccount, err := e.GetAccountByID(generalAccountID)
// TransferToHoldingAccount locks funds from accountTypeFrom into holding account account of the party.
func (e *Engine) TransferToHoldingAccount(ctx context.Context, transfer *types.Transfer, accountTypeFrom types.AccountType) (*types.LedgerMovement, error) {
party := transfer.Owner
if party == types.NetworkParty {
party = systemOwner
}
sourceAccountID := e.accountID(transfer.Market, party, transfer.Amount.Asset, accountTypeFrom)
sourceAccount, err := e.GetAccountByID(sourceAccountID)
if err != nil {
return nil, err
}

holdingAccountID := e.accountID(noMarket, transfer.Owner, transfer.Amount.Asset, types.AccountTypeHolding)
holdingAccountID := e.accountID(noMarket, party, transfer.Amount.Asset, types.AccountTypeHolding)
holdingAccount, err := e.GetAccountByID(holdingAccountID)
if err != nil {
// if the holding account doesn't exist yet we create it here
holdingAccountID, err := e.CreatePartyHoldingAccount(ctx, transfer.Owner, transfer.Amount.Asset)
holdingAccountID, err := e.CreatePartyHoldingAccount(ctx, party, transfer.Amount.Asset)
if err != nil {
return nil, err
}
Expand All @@ -4825,7 +4863,7 @@ func (e *Engine) TransferToHoldingAccount(ctx context.Context, transfer *types.T
MinAmount: transfer.Amount.Amount.Clone(),
Asset: transfer.Amount.Asset,
Type: types.TransferTypeHoldingAccount,
FromAccount: []*types.Account{generalAccount},
FromAccount: []*types.Account{sourceAccount},
ToAccount: []*types.Account{holdingAccount},
}

Expand All @@ -4845,15 +4883,19 @@ func (e *Engine) TransferToHoldingAccount(ctx context.Context, transfer *types.T
return res, nil
}

// ReleaseFromHoldingAccount releases locked funds from holding account back to the general account of the party.
func (e *Engine) ReleaseFromHoldingAccount(ctx context.Context, transfer *types.Transfer) (*types.LedgerMovement, error) {
holdingAccountID := e.accountID(noMarket, transfer.Owner, transfer.Amount.Asset, types.AccountTypeHolding)
// ReleaseFromHoldingAccount releases locked funds from holding account back to the toAccount of the party.
func (e *Engine) ReleaseFromHoldingAccount(ctx context.Context, transfer *types.Transfer, toAccountType types.AccountType) (*types.LedgerMovement, error) {
party := transfer.Owner
if party == types.NetworkParty {
party = systemOwner
}
holdingAccountID := e.accountID(noMarket, party, transfer.Amount.Asset, types.AccountTypeHolding)
holdingAccount, err := e.GetAccountByID(holdingAccountID)
if err != nil {
return nil, err
}

generalAccount, err := e.GetAccountByID(e.accountID(noMarket, transfer.Owner, transfer.Amount.Asset, types.AccountTypeGeneral))
targetAccount, err := e.GetAccountByID(e.accountID(noMarket, party, transfer.Amount.Asset, toAccountType))
if err != nil {
return nil, err
}
Expand All @@ -4864,7 +4906,7 @@ func (e *Engine) ReleaseFromHoldingAccount(ctx context.Context, transfer *types.
Asset: transfer.Amount.Asset,
Type: types.TransferTypeReleaseHoldingAccount,
FromAccount: []*types.Account{holdingAccount},
ToAccount: []*types.Account{generalAccount},
ToAccount: []*types.Account{targetAccount},
}

res, err := e.getLedgerEntries(ctx, req)
Expand Down Expand Up @@ -5018,9 +5060,14 @@ func (e *Engine) CreateSpotMarketAccounts(ctx context.Context, marketID, quoteAs
return err
}

// PartyHasSufficientBalance checks if the party has sufficient amount in the general account.
func (e *Engine) PartyHasSufficientBalance(asset, partyID string, amount *num.Uint) error {
acc, err := e.GetPartyGeneralAccount(partyID, asset)
// PartyHasSufficientBalance checks if the party has sufficient amount in the <fromAccountType> account.
func (e *Engine) PartyHasSufficientBalance(asset, partyID string, amount *num.Uint, fromAccountType types.AccountType) error {
party := partyID
if party == types.NetworkParty {
party = systemOwner
}
accId := e.accountID(noMarket, party, asset, fromAccountType)
acc, err := e.GetAccountByID(accId)
if err != nil {
return err
}
Expand Down Expand Up @@ -5056,28 +5103,42 @@ func (e *Engine) CreatePartyHoldingAccount(ctx context.Context, partyID, asset s
}

// TransferSpot transfers the given asset/quantity from partyID to partyID.
// The source partyID general account must exist in the asset, the target partyID general account in the asset is created if it doesn't yet exist.
func (e *Engine) TransferSpot(ctx context.Context, partyID, toPartyID, asset string, quantity *num.Uint) (*types.LedgerMovement, error) {
generalAccountID := e.accountID(noMarket, partyID, asset, types.AccountTypeGeneral)
generalAccount, err := e.GetAccountByID(generalAccountID)
// The source partyID fromAccountType account must exist in the asset, the target partyID account in the asset is created if it doesn't yet exist.
func (e *Engine) TransferSpot(ctx context.Context, party, toParty, asset string, quantity *num.Uint, fromAccountType types.AccountType, toAccountType types.AccountType) (*types.LedgerMovement, error) {
partyID := party
if party == types.NetworkParty {
partyID = systemOwner
}

toPartyID := toParty
if toParty == types.NetworkParty {
toPartyID = systemOwner
}

fromAccountID := e.accountID(noMarket, partyID, asset, fromAccountType)
fromAccount, err := e.GetAccountByID(fromAccountID)
if err != nil {
return nil, err
}

targetGeneralAccountID := e.accountID(noMarket, toPartyID, asset, types.AccountTypeGeneral)
toGeneralAccount, err := e.GetAccountByID(targetGeneralAccountID)
toAccountID := e.accountID(noMarket, toPartyID, asset, toAccountType)
toAccount, err := e.GetAccountByID(toAccountID)
if err != nil {
targetGeneralAccountID, _ = e.CreatePartyGeneralAccount(ctx, toPartyID, asset)
toGeneralAccount, _ = e.GetAccountByID(targetGeneralAccountID)
if toAccountType == types.AccountTypeGeneral {
toAccountID, _ = e.CreatePartyGeneralAccount(ctx, toPartyID, asset)
toAccount, _ = e.GetAccountByID(toAccountID)
} else if toPartyID == types.NetworkParty {
toAccount = e.getOrCreateNetworkAccount(ctx, asset, toAccountType)
}
}

req := &types.TransferRequest{
Amount: quantity.Clone(),
MinAmount: quantity.Clone(),
Asset: asset,
Type: types.TransferTypeSpot,
FromAccount: []*types.Account{generalAccount},
ToAccount: []*types.Account{toGeneralAccount},
FromAccount: []*types.Account{fromAccount},
ToAccount: []*types.Account{toAccount},
}

res, err := e.getLedgerEntries(ctx, req)
Expand Down Expand Up @@ -5130,3 +5191,51 @@ func (e *Engine) GetVestingAccounts() []*types.Account {
})
return accs
}

func (e *Engine) EarmarkForAutomatedPurchase(asset string, accountType types.AccountType, min, max *num.Uint) (*num.Uint, error) {
id := e.accountID(noMarket, systemOwner, asset, accountType)
acc, err := e.GetAccountByID(id)
if err != nil {
return num.UintZero(), err
}
earmarked, ok := e.earmarkedBalance[id]
if !ok {
earmarked = num.UintZero()
}

balanceToEarmark := acc.Balance.Clone()
if earmarked.GT(balanceToEarmark) {
e.log.Panic("earmarked balance is greater than account balance, this should never happen")
}
balanceToEarmark = balanceToEarmark.Sub(balanceToEarmark, earmarked)

if balanceToEarmark.IsZero() || balanceToEarmark.LT(min) {
return num.UintZero(), fmt.Errorf("insufficient balance to earmark")
}
if balanceToEarmark.GT(max) {
balanceToEarmark = num.Min(balanceToEarmark, max)
}

earmarked.AddSum(balanceToEarmark)
e.earmarkedBalance[id] = earmarked
e.state.updateEarmarked(e.earmarkedBalance)
return balanceToEarmark, nil
}

func (e *Engine) UnearmarkForAutomatedPurchase(asset string, accountType types.AccountType, releaseRequest *num.Uint) error {
id := e.accountID(noMarket, systemOwner, asset, accountType)
_, err := e.GetAccountByID(id)
if err != nil {
return err
}
earmarked, ok := e.earmarkedBalance[id]
if !ok || releaseRequest.GT(earmarked) {
e.log.Panic("trying to unearmark an amount that is greater than the earmarked balance")
}
earmarked.Sub(earmarked, releaseRequest)
if earmarked.IsZero() {
delete(e.earmarkedBalance, id)
}
e.state.updateEarmarked(e.earmarkedBalance)
return nil
}
Loading

0 comments on commit 2fda1e3

Please sign in to comment.