Skip to content

Commit

Permalink
feat: protocol automated purchase auctions
Browse files Browse the repository at this point in the history
  • Loading branch information
ze97286 committed Sep 22, 2024
1 parent 3a48987 commit a2df62a
Show file tree
Hide file tree
Showing 61 changed files with 13,494 additions and 8,697 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@

- [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.

- [11685](https://github.com/vegaprotocol/vega/issues/11685) - Automated purchase support added.

### 🐛 Fixes

- [11672](https://github.com/vegaprotocol/vega/issues/11672) - Add missing fees in GraphQL bindings.
Expand Down
446 changes: 283 additions & 163 deletions commands/proposal_submission.go

Large diffs are not rendered by default.

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

Large diffs are not rendered by default.

156 changes: 131 additions & 25 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 @@ -4688,6 +4691,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 +4735,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 +4828,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 +4856,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 +4876,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 +4899,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 +5053,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 +5096,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 +5184,55 @@ 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)
println("earmarking account", id)
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)
acc, err := e.GetAccountByID(id)
if err != nil {
return err
}
if releaseRequest.GT(acc.Balance) {
e.log.Panic("trying to unearmark an amount that is greater than the balance of the account")
}
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 a2df62a

Please sign in to comment.