Skip to content

Commit

Permalink
Merge pull request #11743 from vegaprotocol/feature/staking-from-coll…
Browse files Browse the repository at this point in the history
…ateral

feat: add staking staking from collateral asset
  • Loading branch information
jeremyletang authored Oct 22, 2024
2 parents 0bea846 + cc1cdaf commit 1e3d406
Show file tree
Hide file tree
Showing 30 changed files with 943 additions and 289 deletions.
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@
- [11519](https://github.com/vegaprotocol/vega/issues/11519) - Add fees to position API types.
- [11714](https://github.com/vegaprotocol/vega/issues/11714) - Improve `AMM` performance by caching best prices and volumes.
- [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.
- [11685](https://github.com/vegaprotocol/vega/issues/11685) - Automated purchase support added.
- [11732](https://github.com/vegaprotocol/vega/issues/11732) - `AMMs` can now have their base price automatically updated by a market's data source.
- [11685](https://github.com/vegaprotocol/vega/issues/11685) - Automated purchase support added.
- [11726](https://github.com/vegaprotocol/vega/issues/11726) - Combined `AMM` uncrossing orders for better performance when uncrossing the book.
- [11711](https://github.com/vegaprotocol/vega/issues/11711) - Manage closed team membership by updating the allow list.
- [11722](https://github.com/vegaprotocol/vega/issues/11722) - Expose active protocol automated purchase identifier in market data API.
- [11722](https://github.com/vegaprotocol/vega/issues/11722) - Expose active protocol automated purchase identifier in market data API.
- [11744](https://github.com/vegaprotocol/vega/issues/11744) - Staking from collateral bridged assets.

### 🐛 Fixes

Expand Down
5 changes: 3 additions & 2 deletions commands/transfer_funds.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ func checkTransfer(cmd *commandspb.Transfer) (e Errors) {
}

if cmd.FromAccountType != vega.AccountType_ACCOUNT_TYPE_GENERAL &&
cmd.FromAccountType != vega.AccountType_ACCOUNT_TYPE_VESTED_REWARDS {
cmd.FromAccountType != vega.AccountType_ACCOUNT_TYPE_VESTED_REWARDS &&
cmd.FromAccountType != vega.AccountType_ACCOUNT_TYPE_LOCKED_FOR_STAKING {
errs.AddForProperty("transfer.from_account_type", ErrIsNotValid)
}

Expand Down Expand Up @@ -109,7 +110,7 @@ func checkTransfer(cmd *commandspb.Transfer) (e Errors) {
} else {
switch k := cmd.Kind.(type) {
case *commandspb.Transfer_OneOff:
if cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_GLOBAL_REWARD && cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_GENERAL && cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_UNSPECIFIED && cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY && cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_BUY_BACK_FEES {
if cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_GLOBAL_REWARD && cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_GENERAL && cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_UNSPECIFIED && cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_NETWORK_TREASURY && cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_BUY_BACK_FEES && cmd.ToAccountType != vega.AccountType_ACCOUNT_TYPE_LOCKED_FOR_STAKING {
errs.AddForProperty("transfer.to_account_type", errors.New("account type is not valid for one off transfer"))
}
if k.OneOff.GetDeliverOn() < 0 {
Expand Down
18 changes: 17 additions & 1 deletion core/banking/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import (
"golang.org/x/exp/maps"
)

//go:generate go run github.com/golang/mock/mockgen -destination mocks/mocks.go -package mocks code.vegaprotocol.io/vega/core/banking Assets,Notary,Collateral,Witness,TimeService,EpochService,Topology,MarketActivityTracker,ERC20BridgeView,EthereumEventSource,Parties
//go:generate go run github.com/golang/mock/mockgen -destination mocks/mocks.go -package mocks code.vegaprotocol.io/vega/core/banking Assets,Notary,Collateral,Witness,TimeService,EpochService,Topology,MarketActivityTracker,ERC20BridgeView,EthereumEventSource,Parties,StakeAccounting

var (
ErrWrongAssetTypeUsedInBuiltinAssetChainEvent = errors.New("non builtin asset used for builtin asset chain event")
Expand Down Expand Up @@ -72,6 +72,7 @@ type Collateral interface {
Withdraw(ctx context.Context, party, asset string, amount *num.Uint) (*types.LedgerMovement, error)
EnableAsset(ctx context.Context, asset types.Asset) error
GetPartyGeneralAccount(party, asset string) (*types.Account, error)
GetPartyLockedForStaking(party, asset string) (*types.Account, error)
GetPartyVestedRewardAccount(partyID, asset string) (*types.Account, error)
TransferFunds(ctx context.Context,
transfers []*types.Transfer,
Expand Down Expand Up @@ -106,6 +107,11 @@ type Topology interface {
IsValidator() bool
}

// StakeAccounting ...
type StakeAccounting interface {
AddEvent(ctx context.Context, evt *types.StakeLinking)
}

type MarketActivityTracker interface {
CalculateMetricForIndividuals(ctx context.Context, ds *vega.DispatchStrategy) []*types.PartyContributionScore
CalculateMetricForTeams(ctx context.Context, ds *vega.DispatchStrategy) ([]*types.PartyContributionScore, map[string][]*types.PartyContributionScore)
Expand Down Expand Up @@ -223,6 +229,9 @@ type Engine struct {

// transient cache used to market a dispatch strategy as checked for eligibility for this round so we don't check again.
dispatchRequiredCache map[string]bool

stakingAsset string
stakeAccounting StakeAccounting
}

type withdrawalRef struct {
Expand All @@ -244,6 +253,7 @@ func New(log *logging.Logger,
secondaryBridgeView ERC20BridgeView,
ethEventSource EthereumEventSource,
parties Parties,
stakeAccounting StakeAccounting,
) (e *Engine) {
log = log.Named(namedLogger)
log.SetLevel(cfg.Level.Get())
Expand Down Expand Up @@ -290,9 +300,15 @@ func New(log *logging.Logger,
primaryBridgeView: primaryBridgeView,
secondaryBridgeView: secondaryBridgeView,
dispatchRequiredCache: map[string]bool{},
stakeAccounting: stakeAccounting,
}
}

func (e *Engine) OnStakingAsset(_ context.Context, a string) error {
e.stakingAsset = a
return nil
}

func (e *Engine) OnMaxFractionChanged(ctx context.Context, f num.Decimal) error {
e.maxGovTransferFraction = f
return nil
Expand Down
5 changes: 4 additions & 1 deletion core/banking/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type testEngine struct {
ethSource *mocks.MockEthereumEventSource
secondaryBridgeView *mocks.MockERC20BridgeView
parties *mocks.MockParties
stakeAccounting *mocks.MockStakeAccounting
}

func getTestEngine(t *testing.T) *testEngine {
Expand All @@ -73,6 +74,7 @@ func getTestEngine(t *testing.T) *testEngine {
broker := bmocks.NewMockBroker(ctrl)
top := mocks.NewMockTopology(ctrl)
epoch := mocks.NewMockEpochService(ctrl)
stakeAccounting := mocks.NewMockStakeAccounting(ctrl)
primaryBridgeView := mocks.NewMockERC20BridgeView(ctrl)
secondaryBridgeView := mocks.NewMockERC20BridgeView(ctrl)
marketActivityTracker := mocks.NewMockMarketActivityTracker(ctrl)
Expand All @@ -82,7 +84,7 @@ func getTestEngine(t *testing.T) *testEngine {
notary.EXPECT().OfferSignatures(gomock.Any(), gomock.Any()).AnyTimes()
epoch.EXPECT().NotifyOnEpoch(gomock.Any(), gomock.Any()).AnyTimes()
parties := mocks.NewMockParties(ctrl)
eng := banking.New(logging.NewTestLogger(), banking.NewDefaultConfig(), col, witness, tsvc, assets, notary, broker, top, marketActivityTracker, primaryBridgeView, secondaryBridgeView, ethSource, parties)
eng := banking.New(logging.NewTestLogger(), banking.NewDefaultConfig(), col, witness, tsvc, assets, notary, broker, top, marketActivityTracker, primaryBridgeView, secondaryBridgeView, ethSource, parties, stakeAccounting)

require.NoError(t, eng.OnMaxQuantumAmountUpdate(context.Background(), num.DecimalOne()))
eng.OnPrimaryEthChainIDUpdated("1", "hello")
Expand All @@ -103,6 +105,7 @@ func getTestEngine(t *testing.T) *testEngine {
marketActivityTracker: marketActivityTracker,
ethSource: ethSource,
parties: parties,
stakeAccounting: stakeAccounting,
}
}

Expand Down
8 changes: 6 additions & 2 deletions core/banking/fee_estimate.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ func EstimateFee(
fromAccountType types.AccountType,
fromDerivedKey *string,
to string,
toAccountType types.AccountType,
) (fee *num.Uint, discount *num.Uint) {
tFee := calculateFeeForTransfer(assetQuantum, maxQuantumAmount, transferFeeFactor, amount, from,
fromAccountType, fromDerivedKey, to)
fromAccountType, fromDerivedKey, to, toAccountType)
return calculateDiscount(accumulatedDiscount, tFee)
}

Expand All @@ -46,12 +47,15 @@ func calculateFeeForTransfer(
fromAccountType types.AccountType,
fromDerivedKey *string,
to string,
toAccountType types.AccountType,
) *num.Uint {
feeAmount := num.UintZero()

// no fee for Vested account
// either from owner's vested to their general account or from derived key vested to owner's general account
if fromAccountType == types.AccountTypeVestedRewards && (from == to || fromDerivedKey != nil) {
if (fromAccountType == types.AccountTypeVestedRewards && (from == to || fromDerivedKey != nil)) ||
(fromAccountType == types.AccountTypeLockedForStaking && from == to) ||
(fromAccountType == types.AccountTypeGeneral && toAccountType == types.AccountTypeLockedForStaking && from == to) {
return feeAmount
}

Expand Down
52 changes: 51 additions & 1 deletion core/banking/mocks/mocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

62 changes: 62 additions & 0 deletions core/banking/oneoff_transfers.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import (

"code.vegaprotocol.io/vega/core/events"
"code.vegaprotocol.io/vega/core/types"
vgcontext "code.vegaprotocol.io/vega/libs/context"
"code.vegaprotocol.io/vega/libs/crypto"
"code.vegaprotocol.io/vega/logging"
checkpoint "code.vegaprotocol.io/vega/protos/vega/checkpoint/v1"
)
Expand Down Expand Up @@ -61,6 +63,65 @@ func scheduledTransferFromProto(p *checkpoint.ScheduledTransfer) (scheduledTrans
}, nil
}

func (e *Engine) updateStakingAccounts(
ctx context.Context, transfer *types.OneOffTransfer,
) {
if transfer.Asset != e.stakingAsset {
// nothing to do
return
}

var (
now = e.timeService.GetTimeNow().Unix()
height, _ = vgcontext.BlockHeightFromContext(ctx)
txhash, _ = vgcontext.TxHashFromContext(ctx)
id = crypto.HashStrToHex(fmt.Sprintf("%v%v", txhash, height))
stakeLinking *types.StakeLinking
)

// manually send funds from the general account to the locked for staking
if transfer.FromAccountType == types.AccountTypeGeneral && transfer.ToAccountType == types.AccountTypeLockedForStaking {
stakeLinking = &types.StakeLinking{
ID: id,
Type: types.StakeLinkingTypeDeposited,
TS: now,
Party: transfer.From,
Amount: transfer.Amount.Clone(),
Status: types.StakeLinkingStatusAccepted,
FinalizedAt: now,
TxHash: txhash,
BlockHeight: height,
BlockTime: now,
LogIndex: 1,
EthereumAddress: "",
}
}

// from staking account or vested rewards, we send a remove event
if (transfer.FromAccountType == types.AccountTypeLockedForStaking && transfer.ToAccountType == types.AccountTypeGeneral) ||
(transfer.FromAccountType == types.AccountTypeVestedRewards && transfer.ToAccountType == types.AccountTypeGeneral) {
stakeLinking = &types.StakeLinking{
ID: id,
Type: types.StakeLinkingTypeRemoved,
TS: now,
Party: transfer.From,
Amount: transfer.Amount.Clone(),
Status: types.StakeLinkingStatusAccepted,
FinalizedAt: now,
TxHash: txhash,
BlockHeight: height,
BlockTime: now,
LogIndex: 1,
EthereumAddress: "",
}
}

if stakeLinking != nil {
e.stakeAccounting.AddEvent(ctx, stakeLinking)
e.broker.Send(events.NewStakeLinking(ctx, *stakeLinking))
}
}

func (e *Engine) oneOffTransfer(
ctx context.Context,
transfer *types.OneOffTransfer,
Expand All @@ -70,6 +131,7 @@ func (e *Engine) oneOffTransfer(
e.broker.Send(events.NewOneOffTransferFundsEventWithReason(ctx, transfer, err.Error()))
} else {
e.broker.Send(events.NewOneOffTransferFundsEvent(ctx, transfer))
e.updateStakingAccounts(ctx, transfer)
}
}()

Expand Down
Loading

0 comments on commit 1e3d406

Please sign in to comment.